summaryrefslogtreecommitdiffstats
path: root/docs/development/actors/overview.rst
diff options
context:
space:
mode:
Diffstat (limited to 'docs/development/actors/overview.rst')
-rw-r--r--docs/development/actors/overview.rst197
1 files changed, 197 insertions, 0 deletions
diff --git a/docs/development/actors/overview.rst b/docs/development/actors/overview.rst
new file mode 100644
index 00000000..f1ca0d68
--- /dev/null
+++ b/docs/development/actors/overview.rst
@@ -0,0 +1,197 @@
+.. 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
+- An implementation for a specific Actor will typically be derived from
+ *HttpActor* or *BidirectionalTopicActor*, depending whether it is HTTP/REST-based
+ or DMaaP-topic-based. These super classes provide most of the functionality needed
+ to configure the operators, extracting operator-specific properties and adding
+ default, actor-level properties
+
+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*. *OperationPartial* is the super class of
+ *HttpOperation* and *BidirectionalTopicOperation* and provides most of the methods
+ used by the Operation subclasses, including a number of utility methods (e.g.,
+ cancellable *allOf*)
+- Operation subclasses should be written in a way so-as to avoid any blocking I/O. If
+ this proves too difficult, then the implementation should override *doOperation()*
+ instead of *startOperationAsync()*
+- 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" and "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