diff options
Diffstat (limited to 'docs/development/actors/overview.rst')
-rw-r--r-- | docs/development/actors/overview.rst | 187 |
1 files changed, 187 insertions, 0 deletions
diff --git a/docs/development/actors/overview.rst b/docs/development/actors/overview.rst new file mode 100644 index 00000000..716f4f28 --- /dev/null +++ b/docs/development/actors/overview.rst @@ -0,0 +1,187 @@ +.. This work is licensed under a Creative Commons Attribution 4.0 International License. + +.. _actors-overview-label: + +##################### +Actor Design Overview +##################### + +.. contents:: + +Intro +##### + +An actor/operation is any ONAP component that an Operational Policy can use to control +a VNF/VM/etc. during execution of a control loop operational policy when a Control Loop +Event is triggered. + +.. image:: topview.png + +An Actor Service object contains one or more Actor objects, which are found and created +using *ServiceLoader*. Each Actor object, in turn, creates one or more Operator objects. +All of these components, the Actor Service, the Actor, and the Operator are typically +singletons that are created once, at start-up (or on the first request). +The Actor Service includes several methods, *configure()*, *start()*, and *stop()*, +which are cascaded to the Actors and then to the Operators. + +Operation objects, on the other hand, are not singletons; a new Operation object is +created for each operation that an application wishes to perform. For instance, if an +application wishes to use the "SO" Actor to add two new modules, then two separate +Operation objects would be created, one for each module. + +Actors are configured by invoking the Actor Service *configure()* method, passing it +a set of properties. The *configure()* method extracts the properties that are relevant +to each Actor and passes them to the Actor's *configure()* method. Similarly, the +Actor's *configure()* method extracts the properties that are relevant +to each Operator and passes them to the Operator's *configure()* method. Note: +Actors typically extract "default" properties from their respective property sets +and include those when invoking each Operator's *configure()* method. + +Once the Actor Service has been configured, it can be started via *start()*. It will +then continue to run until no longer needed, at which point *stop()* can be invoked. + +Note: it is possible to create separate instances of an Actor Service, each with its +own set of properties. In that case, each Actor Service will get its own instances of +Actors and Operators. + +Components +########## + +This section describes things to consider when creating a new Actor/Operator. + +Actor +***** + +- The constructor should use addOperator() to add operators +- By convention, the name of the actor is specified by a static field, "NAME" +- An actor is registered via the Java ServiceLoader by including its jar on the + classpath and adding its class name to this file, typically contained within the jar: + + onap.policy.controlloop.actorServiceProvider.spi + +- Actor loading is ordered, so that those having a lower (i.e., earlier) sequence number + are loaded first. If a later actor has the same name as one that has already been + loaded, a warning will be generated and the later actor discarded. This makes it + possible for an organization to override an actor implementation + +Operator +******** + +- Typically, developers don’t have to implement any Operator classes; they just use + *HttpOperator* or *BidirectionalTopicOperator* + +Operation +********* + +- Most operations require guard checks to be performed first. Thus, at a minimum, they + should override *startPreprocessorAsync()* and have it invoke *startGuardAsync()* +- In addition, if the operation depends on data being previously gathered and placed + into the context, then it should override *startPreprocessorAsync()* and have it + invoke *obtain()*. Note: *obtain()* + and the guard can be performed in parallel by using the *allOf()* method. If the + guard + happens to depend on the same data, then it will block until the data is available, + and then continue; the invoker need not deal with the dependency +- Subclasses will typically derive from *HttpOperation* or *BidirectionalTopicOperation*, + though if neither of those suffice, then they can extend *OperationPartial*, or + even just implement a raw *Operation* +- Operation subclasses should be written in a way so-as to avoid any blocking I/O +- Operations return a "future" when *start()* is invoked. Typically, if the "future" is + canceled, then any outstanding operation should be canceled. For instance, HTTP + connections should be closed without waiting for a response +- If an operation sets the outcome to "FAILURE", it will be automatically retried; other + failure types are not retried + +ControlLoopParams +***************** + +- Identifies the operation to be performed +- Includes timeout and retry information, though the actors typically provide default + values if they are not specified in the parameters +- Includes the event "context" +- Includes “Policy” fields (e.g., "actor" and "operation") + +Context (aka, Event Context) +**************************** + +- Includes: + + - the original onset event + - enrichment data associated with the event + - results of A&AI queries + +XxxParams and XxxConfig +*********************** + +- XxxParams objects are POJOs into which the property Maps are decoded when configuring + actors or operators +- XxxConfig objects contain a single Operator's (or Actor's) configuration information, + based on what was in the XxxParams. For instance, the HttpConfig contains a reference + to the HttpClient that is used to perform HTTP + operations, while the associated HttpParams just contains the name of the HttpClient. + XxxConfig objects are + shared by all operations created by a single Operator. As a result, it should not + contain any data associated with an individual operation; such data should be stored + within the operation object, itself + +Junit tests +*********** + +- Operation Tests may choose to subclass from *BasicHttpOperation*, which provides some + supporting utilities and mock objects +- Should include a test to verify that the Actor, and possibly each Operator, can be + retrieved via an Actor Service +- Tests with an actual REST server are performed within *HttpOperationTest*, so need not + be repeated in subclasses. Instead, they can catch the callback to the *get()*, + *post()*, etc., methods and pass the rawResponse to it there. That being said, a + number of actors spin up a simulator to verify end-to-end request/response processing + +Clients (e.g., drools-applications) +*********************************** + +- When using callbacks, a client may want to use the *isFor()* method to verify that the + outcome is for the desired operation, as callbacks are invoked with the outcome of all + operations performed, including any preprocessor steps + +Flow of operation +################# + +- PDP: + + - Populates a *ControlLoopParams* using *ControlLoopParams.builder()* + - Invokes *start()* on the *ControlLoopParams* + +- ControlLoopParams: + + - Finds the actor/operator + - Uses it to invoke *buildOperation()* + - Invokes *start()* on the Operation + +- Operation: + + - *start()* invokes *startPreprocessorAsync()* and then *startOperationAsync()* + - Exceptions that occur while **constructing** the operation pipeline propagate back + to the client that invoked *start()* + - Exceptions that occur while **executing** the operation pipeline are caught and + turned into an *OperationOutcome* whose result is FAILURE_EXCEPTION. In addition, + the "start" callback (i.e., specified via the *ControlLoopParams*) will be invoked, + if it hasn't been invoked yet, and then the "complete" callback will be invoked + - By default, *startPreprocessorAsync()* does nothing, thus most subclasses will override it to: + + - Do any A&AI query that is needed (beyond enrichment, which is already available in + the *Context*) + - Use *Context obtain()* to request the data asynchronously + - Invoke *startGuardAsync()* + + - By default, *startGuardAsync()* will simply perform a guard check, passing it the + "standard" payload + - Subclasses may override *makeGuardPayload()* to add extra fields to the payload + (e.g., some SO operations add the VF count) + - If any preprocessing step fails, then the "start" & "complete" callbacks will be + invoked to indicate a failure of the operation as a whole. Otherwise, the flow will + continue on to *startOperationAsync()*, after the "start" callback is invoked + - *StartOperationAsync()* will perform whatever needs to be done to start the operation + - Once it completes, the "complete" callback will be invoked with the outcome of the + operation. *StartOperationAsync()* should not invoke the callback, as that is + handled automatically by *OperationPartial*, which is the superclass of most + Operations |