This appendix describes generic logic flows that combine to act as an Autonomic Service Agent (ASA) for resource management. Note that these are illustrative examples and are in no sense requirements. As long as the rules of GRASP are followed, a real implementation could be different. The reader is assumed to be familiar with GRASP [
RFC 8990] and its conceptual API [
RFC 8991].
A complete autonomic function for a distributed resource will consist of a number of instances of the ASA placed at relevant points in a network. Specific details will, of course, depend on the resource concerned. One example is IP address prefix management, as specified in [
RFC 8992]. In this case, an instance of the ASA will exist in each delegating router.
An underlying assumption is that there is an initial source of the resource in question, referred to here as an origin ASA. The other ASAs, known as delegators, obtain supplies of the resource from the origin, delegate quantities of the resource to consumers that request it, and recover it when no longer needed.
Another assumption is there is a set of network-wide policy parameters, which the origin will provide to the delegators. These parameters will control how the delegators decide how much resource to provide to consumers. Thus, the ASA logic has two operating modes: origin and delegator. When running as an origin, it starts by obtaining a quantity of the resource from the NOC, and it acts as a source of policy parameters, via both GRASP flooding and GRASP synchronization. (In some scenarios, flooding or synchronization alone might be sufficient, but this example includes both.)
When running as a delegator, it starts with an empty resource pool, acquires the policy parameters by GRASP synchronization, and delegates quantities of the resource to consumers that request it. Both as an origin and as a delegator, when its pool is low, it seeks quantities of the resource by requesting GRASP negotiation with peer ASAs. When its pool is sufficient, it hands out resource to peer ASAs in response to negotiation requests. Thus, over time, the initial resource pool held by the origin will be shared among all the delegators according to demand.
In theory, a network could include any number of origins and any number of delegators, with the only condition being that each origin's initial resource pool is unique. A realistic scenario is to have exactly one origin and as many delegators as you like. A scenario with no origin is useless.
An implementation requirement is that resource pools are kept in stable storage. Otherwise, if a delegator exits for any reason, all the resources it has obtained or delegated are lost. If an origin exits, its entire spare pool is lost. The logic for using stable storage and for crash recovery is not included in the pseudocode below, which focuses on communication between ASAs. Since GRASP operations are not intrinsically idempotent, data integrity during failure scenarios is the responsibility of the ASA designer. This is a complex topic in its own right that is not discussed in the present document.
The description below does not implement GRASP's dry run function. That would require temporarily marking any resource handed out in a dry run negotiation as reserved, until either the peer obtains it in a live run, or a suitable timeout occurs.
The main data structures used in each instance of the ASA are:
-
resource_pool: an ordered list of available resources, for example. Depending on the nature of the resource, units of resource are split when appropriate, and a background garbage collector recombines split resources if they are returned to the pool.
-
delegated_list: where a delegator stores the resources it has given to subsidiary devices.
Possible main logic flows are below, using a threaded implementation model. As noted above, alternative approaches to asynchronous operations are possible. The transformation to an event loop model should be apparent; each thread would correspond to one event in the event loop.
The GRASP objectives are as follows:
-
["EX1.Resource", flags, loop_count, value], where the value depends on the resource concerned but will typically include its size and identification.
-
["EX1.Params", flags, loop_count, value], where the value will be, for example, a JSON object defining the applicable parameters.
In the outline logic flows below, these objectives are represented simply by their names.
MAIN PROGRAM:
Create empty resource_pool (and an associated lock)
Create empty delegated_list
Determine whether to act as origin
if origin:
Obtain initial resource_pool contents from NOC
Obtain value of EX1.Params from NOC
Register ASA with GRASP
Register GRASP objectives EX1.Resource and EX1.Params
if origin:
Start FLOODER thread to flood EX1.Params
Start SYNCHRONIZER listener for EX1.Params
Start MAIN_NEGOTIATOR thread for EX1.Resource
if not origin:
Obtain value of EX1.Params from GRASP flood or synchronization
Start DELEGATOR thread
Start GARBAGE_COLLECTOR thread
good_peer = none
do forever:
if resource_pool is low:
Calculate amount A of resource needed
Discover peers using GRASP M_DISCOVER / M_RESPONSE
if good_peer in peers:
peer = good_peer
else:
peer = #any choice among peers
grasp.request_negotiate("EX1.Resource", peer)
#i.e., send negotiation request
Wait for response (M_NEGOTIATE, M_END or M_WAIT)
if OK:
if offered amount of resource sufficient:
Send M_END + O_ACCEPT #negotiation succeeded
Add resource to pool
good_peer = peer #remember this choice
else:
Send M_END + O_DECLINE #negotiation failed
good_peer = none #forget this choice
sleep() #periodic timer suitable for application scenario
MAIN_NEGOTIATOR thread:
do forever:
grasp.listen_negotiate("EX1.Resource")
#i.e., wait for negotiation request
Start a separate new NEGOTIATOR thread for requested amount A
NEGOTIATOR thread:
Request resource amount A from resource_pool
if not OK:
while not OK and A > Amin:
A = A-1
Request resource amount A from resource_pool
if OK:
Offer resource amount A to peer by GRASP M_NEGOTIATE
if received M_END + O_ACCEPT:
#negotiation succeeded
elif received M_END + O_DECLINE or other error:
#negotiation failed
Return resource to resource_pool
else:
Send M_END + O_DECLINE #negotiation failed
#thread exits
DELEGATOR thread:
do forever:
Wait for request or release for resource amount A
if request:
Get resource amount A from resource_pool
if OK:
Delegate resource to consumer #atomic
Record in delegated_list #operation
else:
Signal failure to consumer
Signal main thread that resource_pool is low
else:
Delete resource from delegated_list
Return resource amount A to resource_pool
SYNCHRONIZER thread:
do forever:
Wait for M_REQ_SYN message for EX1.Params
Reply with M_SYNCH message for EX1.Params
FLOODER thread:
do forever:
Send M_FLOOD message for EX1.Params
sleep() #periodic timer suitable for application scenario
GARBAGE_COLLECTOR thread:
do forever:
Search resource_pool for adjacent resources
Merge adjacent resources
sleep() #periodic timer suitable for application scenario