Add documentation for OutProperties support
[policy/parent.git] / docs / clamp / acm / acm-participant-guide.rst
1 .. This work is licensed under a Creative Commons Attribution 4.0 International License.
2 .. http://creativecommons.org/licenses/by/4.0
3 .. Copyright (c) Nordix Foundation.  All rights reserved.
4
5 .. _acm-participant-guide-label:
6
7 Participant developer guide
8 ###########################
9
10 .. contents::
11     :depth: 4
12
13 The ACM runtime delegates the user requests to the participants for performing the actual operations.
14 Hence the participant module in ACM is implemented adhering to a list of ACM protocols along with their own functional logic.
15 It works in a contract with the Participant Intermediary module for communicating with ACM-R.
16 This guide explains the design considerations for a new participant implementation in ACM.
17
18 Please refer the following section for a detailed understanding of Inbound and outbound messages a participant interacts with.
19
20 .. toctree::
21    :maxdepth: 2
22
23    design-impl/participants/participants
24
25 Design considerations for a participant
26 ---------------------------------------
27
28 In ONAP, the ACM-runtime and participant modules are implemented in Java spring boot. The participant Intermediary module
29 which is added as a maven dependency to the participants has the default implementations available for listening the kafka
30 events coming in from the ACM-runtime, process them and delegate them to the appropriate handler class. Similarly the
31 Intermediary module also has the publisher class implementations for publishing events back from the participants to the ACM-runtime.
32
33 Hence the new participants has to have this Participant Intermediary module as a dependency and should:
34
35 * Configure SpringBoot to scan the components located into the package "org.onap.policy.clamp.acm.participant.intermediary".
36 * Implement the following interfaces from the Participant Intermediary.
37 * Provide the following mandatory properties in order to make the participant work in synchronisation with ACM-runtime.
38
39 The participant application should be provided with the following Intermediary parameter values in the application properties
40 and the same is configured for the 'ParticipantIntermediaryParameters' object in the code.
41
42 1. participantId - A unique participant UUID that is used by the runtime to identify the participant.
43 2. ReportingTimeIntervalMs - Time inertval the participant should report the status/heartbeat to the runtime.
44 3. clampAutomationCompositionTopics - This property takes in the kafka topic names and servers for the intermediary module to use.
45    These values should be provided for both source and sink configs. The following example shows the topic parameters set for using DMaap.
46
47 .. code-block:: bash
48
49     clampAutomationCompositionTopics:
50           topicSources:
51             -
52               topic: POLICY-ACRUNTIME-PARTICIPANT
53               servers:
54                 - ${topicServer:localhost}
55               topicCommInfrastructure: dmaap
56               fetchTimeout: 15000
57           topicSinks:
58             -
59               topic: POLICY-ACRUNTIME-PARTICIPANT
60               servers:
61                 - ${topicServer:localhost}
62               topicCommInfrastructure: dmaap
63
64 4. participantSupportedElementTypes - This property takes a list of typeName and typeVersion fields to define the types of AC elements the participant deals with.
65    These are user defined name and version and the same should be defined for the AC elements that are included in the TOSCA based AC definitions.
66
67 .. code-block:: bash
68
69     participantSupportedElementTypes:
70       -
71         typeName: org.onap.policy.clamp.acm.PolicyAutomationCompositionElement
72         typeVersion: 1.0.0
73
74 Interfaces to Implement
75 -----------------------
76 AutomationCompositionElementListener:
77   Every participant should implement a handler class that implements the AutomationCompositionElementListener interface
78   from the Participant Intermediary. The intermediary listener class listens for the incoming events from the ACM-runtime
79   and invoke the handler class implementations for various operations. This class implements the methods for deploying,
80   undeploying, locking, unlocking , deleting, updating, priming, depriming requests that are coming from the ACM-runtime.
81   The methods are as follows.
82
83 .. code-block:: java
84
85   1. void undeploy(UUID automationCompositionId, UUID automationCompositionElementId) throws PfModelException;
86   2. void deploy(UUID automationCompositionId, AcElementDeploy element, Map<String, Object> inProperties) throws PfModelException;
87   3. void lock(UUID automationCompositionId, UUID automationCompositionElementId) throws PfModelException;
88   4. void unlock(UUID automationCompositionId, UUID automationCompositionElementId) throws PfModelException;
89   5. void delete(UUID automationCompositionId, UUID automationCompositionElementId) throws PfModelException;
90   6. void update(UUID automationCompositionId, AcElementDeploy element, Map<String, Object> inProperties) throws PfModelException;
91   7. void prime(UUID compositionId, List<AutomationCompositionElementDefinition> elementDefinitionList) throws PfModelException;
92   8. void deprime(UUID compositionId) throws PfModelException;
93   9. void handleRestartComposition(UUID compositionId, List<AutomationCompositionElementDefinition> elementDefinitionList, AcTypeState state) throws PfModelException;
94   10. void handleRestartInstance(UUID automationCompositionId, AcElementDeploy element, Map<String, Object> properties, DeployState deployState, LockState lockState) throws PfModelException;
95   11. void migrate(UUID instanceId, AcElementDeploy element, UUID compositionTargetId, Map<String, Object> properties) throws PfModelException;
96
97 These method from the interface are implemented independently as per the user requirement. These methods after handling the
98 appropriate requests should also invoke the intermediary's publisher apis to notify the ACM-runtime with the acknowledgement events.
99
100 ParticipantParameters:
101   Every participant should implement a properties class that contains the values of all Intermediary parameter properties.
102   This class implements the method getIntermediaryParameters that returns 'ParticipantIntermediaryParameters' object. The method is as follows.
103
104 .. code-block:: java
105
106   ParticipantIntermediaryParameters getIntermediaryParameters()
107
108
109 APIs to invoke
110 --------------
111 ParticipantIntermediaryApi:
112   The participant intermediary api has the following methods that can be invoked from the participant for the following purposes.
113
114   #. The requested operations are completed in the handler class and the ACM-runtime needs to be notified.
115   #. Collect all instances data.
116   #. Send out Properties to ACM-runtime.
117
118   The methods are as follows:
119
120 This following method is invoked to update the AC element state after each operation is completed in the participant.
121
122 .. code-block:: java
123
124   1.  void updateAutomationCompositionElementState(UUID automationCompositionId, UUID elementId, DeployState deployState, LockState lockState, StateChangeResult stateChangeResult, String message);
125   2.  Map<UUID, AutomationComposition> getAutomationCompositions();
126   3.  AutomationComposition getAutomationComposition(UUID automationCompositionId);
127   4.  AutomationCompositionElement getAutomationCompositionElement(UUID automationCompositionId, UUID elementId);
128   5.  Map<UUID, Map<ToscaConceptIdentifier, AutomationCompositionElementDefinition>> getAcElementsDefinitions();
129   6.  Map<ToscaConceptIdentifier, AutomationCompositionElementDefinition> getAcElementsDefinitions(UUID compositionId);
130   7.  AutomationCompositionElementDefinition getAcElementDefinition(UUID compositionId, ToscaConceptIdentifier elementId);
131   8.  void sendAcDefinitionInfo(UUID compositionId, ToscaConceptIdentifier elementId, Map<String, Object> outProperties);
132   9.  void updateCompositionState(UUID compositionId, AcTypeState state, StateChangeResult stateChangeResult, String message);
133   10.  void sendAcElementInfo(UUID automationCompositionId, UUID elementId, String useState, String operationalState, Map<String, Object> outProperties);
134
135 In/Out composition Properties
136 -----------------------------
137 The 'Common Properties' could be created or updated by ACM-runtime.
138 Participants will receive that Properties during priming events.
139
140 .. code-block:: java
141
142   @Override
143   public void prime(UUID compositionId, List<AutomationCompositionElementDefinition> elementDefinitionList) throws PfModelException {
144       for (var acElementDefinition : elementDefinitionList) {
145           var inProperties = acElementDefinition.getAutomationCompositionElementToscaNodeTemplate().getProperties();
146           .......
147       }
148       .......
149   }
150
151 The 'Common Properties' could be fetched during depriming events.
152
153 .. code-block:: java
154
155   @Override
156   public void deprime(UUID compositionId) throws PfModelException {
157       var elementDefinitionList = intermediaryApi.getAcElementsDefinitions(compositionId);
158       for (var acElementDefinition : elementDefinitionList.values()) {
159           var inProperties = acElementDefinition.getAutomationCompositionElementToscaNodeTemplate().getProperties();
160           .......
161       }
162       .......
163   }
164  
165 The 'Out Properties' could be created or updated by participants. ACM-runtime will receive that Properties during ParticipantStatus event.
166 The participant can trigger this event using the method sendAcDefinitionInfo.
167
168 Is allowed to the participant to read all In/Out Properties of all compositions handled by the participant using the method getAcElementsDefinitions.
169 The following code is an example how to update the property 'myProperty' and send to ACM-runtime:
170
171 .. code-block:: java
172
173   var acElement = intermediaryApi.getAcElementDefinition(compositionId, elementId);
174   var outProperties = acElement.getOutProperties();
175   outProperties.put("myProperty", myProperty);
176   intermediaryApi.sendAcDefinitionInfo(compositionId, elementId, outProperties);
177
178 In/Out instance Properties
179 --------------------------
180   The 'In/Out Properties' are stored into the instance elements, so each element has its own In/Out Properties.
181
182   The 'In Properties' could be created or updated by ACM-runtime. Participants will receive that Properties during deploy and update events.
183
184   The 'Out Properties' could be created or updated by participants. ACM-runtime will receive that Properties during ParticipantStatus event.
185   The participant can trigger this event using the method sendAcElementInfo. The 'useState' and 'operationalState' can be used as well.
186   The 'Out Properties' could be **cleaned**:
187
188   * by the participant using the method sendAcElementInfo
189   * by intermediary automatically during deleting of the instance
190   * by an update when the instance is in UNDEPLOYED state (changing the elementId)
191
192   The 'Out Properties' will be **not cleaned** by intermediary:
193
194   * during DEPLOIYNG (Out Properties will be take from last changes matching by elementId)
195   * during UNDEPLOING
196   * during LOCKING/UNLOCKING
197   * during UPDATING/MIGRATING
198
199   Is allowed to the participant to read all In/Out Properties and state of all instances handled by the participant using the method getAutomationCompositions.
200   The following code is an example how to update the property 'myProperty' and send to ACM-runtime:
201
202 .. code-block:: java
203
204   var acElement = intermediaryApi.getAutomationCompositionElement(automationCompositionId, elementId);
205   var outProperties = acElement.getOutProperties();
206   outProperties.put("myProperty", myProperty);
207   intermediaryApi.sendAcElementInfo(automationCompositionId, elementId, acElement.getUseState(), acElement.getOperationalState(), outProperties);
208
209 Restart scenario
210 ----------------
211   Restart methods handle the scenario when participant shut down and restart.
212   During RESTARTING, compositions and instances will be stored in participant memory with In/Out Properties, 'useState' and 'operationalState'.
213   The method handleRestartComposition will be called for each composition and will be present the 'state' at the time the participant shut down.
214   The method handleRestartInstance will be called for each instance element and will be present the 'deployState' and the 'lockState' at the time the participant shut down.
215
216 In ONAP, the following participants are already implemented in java spring boot for various requirements. The maven modules
217 can be referred here:
218
219   * `HTTP participant <https://github.com/onap/policy-clamp/tree/master/participant/participant-impl/participant-impl-http>`_.
220   * `Kubernetes participant <https://github.com/onap/policy-clamp/tree/master/participant/participant-impl/participant-impl-kubernetes>`_.
221   * `Policy participant <https://github.com/onap/policy-clamp/tree/master/participant/participant-impl/participant-impl-policy>`_.
222   * `A1PMS participant <https://github.com/onap/policy-clamp/tree/master/participant/participant-impl/participant-impl-a1pms>`_.
223   * `Kserve participant <https://github.com/onap/policy-clamp/tree/master/participant/participant-impl/participant-impl-kserve>`_.
224
225 Example of Implementation
226 -------------------------
227
228 This following code is an example of My First Participant:
229   * Application
230   * Parameters
231   * Handler
232
233 The Application class is configured to add the "org.onap.policy.clamp.acm.participant.intermediary" package in SpringBoot component scanning.
234
235 .. code-block:: java
236
237   @SpringBootApplication
238   @ComponentScan({
239     "org.onap.policy.clamp.acm.participant.myfirstparticipant",
240     "org.onap.policy.clamp.acm.participant.intermediary"
241   })
242   @ConfigurationPropertiesScan("org.onap.policy.clamp.acm.participant.myfirstparticipant.parameters")
243   public class MyFirstParticipantApplication {
244
245     public static void main(String[] args) {
246       SpringApplication.run(Application.class, args);
247     }
248   }
249
250 The Participant Parameters class implements the mandatory interface ParticipantParameters.
251 It could contains additional parameters.
252
253 .. code-block:: java
254
255   @Validated
256   @Getter
257   @Setter
258   @ConfigurationProperties(prefix = "participant")
259   public class ParticipantSimParameters implements ParticipantParameters {
260
261     @NotBlank
262     private String myparameter;
263
264     @NotNull
265     @Valid
266     private ParticipantIntermediaryParameters intermediaryParameters;
267   }
268
269 The following example shows the topic parameters and the additional 'myparameter'.
270
271 .. code-block:: bash
272
273   participant:
274     myparameter: my parameter
275     intermediaryParameters:
276       reportingTimeIntervalMs: 120000
277       description: Participant Description
278       participantId: 101c62b3-8918-41b9-a747-d21eb79c6c90
279       clampAutomationCompositionTopics:
280         topicSources:
281           - topic: POLICY-ACRUNTIME-PARTICIPANT
282             servers:
283               - ${topicServer:localhost}
284             topicCommInfrastructure: dmaap
285             fetchTimeout: 15000
286         topicSinks:
287           - topic: POLICY-ACRUNTIME-PARTICIPANT
288             servers:
289               - ${topicServer:localhost}
290             topicCommInfrastructure: dmaap
291       participantSupportedElementTypes:
292         -
293           typeName: org.onap.policy.clamp.acm.MyFirstAutomationCompositionElement
294           typeVersion: 1.0.0
295
296
297 The following example shows the Handler implementation and how could be the implemented the mandatory notifications.
298
299 .. code-block:: java
300
301   @Component
302   @RequiredArgsConstructor
303   public class MyFirstAcElementHandler implements AutomationCompositionElementListener {
304
305     private final ParticipantIntermediaryApi intermediaryApi;
306
307     @Override
308     public void deploy(UUID automationCompositionId, AcElementDeploy element, Map<String, Object> properties)
309             throws PfModelException {
310
311         // TODO deploy process
312
313         if (isDeploySuccess()) {
314             intermediaryApi.updateAutomationCompositionElementState(automationCompositionId, element.getId(),
315                     DeployState.DEPLOYED, null, StateChangeResult.NO_ERROR, "Deployed");
316         } else {
317             intermediaryApi.updateAutomationCompositionElementState(automationCompositionId, element.getId(),
318                     DeployState.UNDEPLOYED, null, StateChangeResult.FAILED, "Deploy failed!");
319         }
320     }
321
322     @Override
323     public void undeploy(UUID automationCompositionId, UUID automationCompositionElementId) throws PfModelException {
324         LOGGER.debug("undeploy call");
325
326         // TODO undeploy process
327
328         if (isUndeploySuccess()) {
329             intermediaryApi.updateAutomationCompositionElementState(automationCompositionId,
330                     automationCompositionElementId, DeployState.UNDEPLOYED, null, StateChangeResult.NO_ERROR,
331                     "Undeployed");
332         } else {
333             intermediaryApi.updateAutomationCompositionElementState(automationCompositionId,
334                     automationCompositionElementId, DeployState.DEPLOYED, null, StateChangeResult.FAILED,
335                     "Undeploy failed!");
336         }
337     }
338
339     @Override
340     public void lock(UUID automationCompositionId, UUID automationCompositionElementId) throws PfModelException {
341
342         // TODO lock process
343
344         if (isLockSuccess()) {
345             intermediaryApi.updateAutomationCompositionElementState(automationCompositionId,
346                     automationCompositionElementId, null, LockState.LOCKED, StateChangeResult.NO_ERROR, "Locked");
347         } else {
348             intermediaryApi.updateAutomationCompositionElementState(automationCompositionId,
349                     automationCompositionElementId, null, LockState.UNLOCKED, StateChangeResult.FAILED, "Lock failed!");
350         }
351     }
352
353     @Override
354     public void unlock(UUID automationCompositionId, UUID automationCompositionElementId) throws PfModelException {
355
356         // TODO unlock process
357
358         if (isUnlockSuccess()) {
359             intermediaryApi.updateAutomationCompositionElementState(automationCompositionId,
360                     automationCompositionElementId, null, LockState.UNLOCKED, StateChangeResult.NO_ERROR, "Unlocked");
361         } else {
362             intermediaryApi.updateAutomationCompositionElementState(automationCompositionId,
363                     automationCompositionElementId, null, LockState.LOCKED, StateChangeResult.FAILED, "Unlock failed!");
364         }
365     }
366
367     @Override
368     public void delete(UUID automationCompositionId, UUID automationCompositionElementId) throws PfModelException {
369
370         // TODO delete process
371
372         if (isDeleteSuccess()) {
373             intermediaryApi.updateAutomationCompositionElementState(automationCompositionId,
374                     automationCompositionElementId, DeployState.DELETED, null, StateChangeResult.NO_ERROR, "Deleted");
375         } else {
376             intermediaryApi.updateAutomationCompositionElementState(automationCompositionId,
377                     automationCompositionElementId, DeployState.UNDEPLOYED, null, StateChangeResult.FAILED,
378                     "Delete failed!");
379         }
380     }
381
382     @Override
383     public void update(UUID automationCompositionId, AcElementDeploy element, Map<String, Object> properties)
384             throws PfModelException {
385
386         // TODO update process
387
388         if (isUpdateSuccess()) {
389             intermediaryApi.updateAutomationCompositionElementState(automationCompositionId, element.getId(),
390                     DeployState.DEPLOYED, null, StateChangeResult.NO_ERROR, "Updated");
391         } else {
392             intermediaryApi.updateAutomationCompositionElementState(automationCompositionId, element.getId(),
393                     DeployState.DEPLOYED, null, StateChangeResult.FAILED, "Update failed!");
394         }
395     }
396
397     @Override
398     public void migrate(UUID automationCompositionId, AcElementDeploy element, UUID compositionTargetId,
399             Map<String, Object> properties) throws PfModelException {
400
401         // TODO migrate process
402
403         if (isMigrateSuccess()) {
404             intermediaryApi.updateAutomationCompositionElementState(automationCompositionId, element.getId(),
405                     DeployState.DEPLOYED, null, StateChangeResult.NO_ERROR, "Migrated");
406         } else {
407             intermediaryApi.updateAutomationCompositionElementState(automationCompositionId, element.getId(),
408                     DeployState.DEPLOYED, null, StateChangeResult.FAILED, "Migrate failed!");
409         }
410     }
411
412     @Override
413     public void prime(UUID compositionId, List<AutomationCompositionElementDefinition> elementDefinitionList)
414             throws PfModelException {
415
416         // TODO prime process
417
418         if (isPrimeSuccess()) {
419             intermediaryApi.updateCompositionState(compositionId, AcTypeState.PRIMED, StateChangeResult.NO_ERROR,
420                     "Primed");
421         } else {
422             intermediaryApi.updateCompositionState(compositionId, AcTypeState.COMMISSIONED, StateChangeResult.FAILED,
423                     "Prime failed!");
424         }
425     }
426
427     @Override
428     public void deprime(UUID compositionId) throws PfModelException {
429
430         // TODO deprime process
431
432         if (isDeprimeSuccess()) {
433             intermediaryApi.updateCompositionState(compositionId, AcTypeState.COMMISSIONED, StateChangeResult.NO_ERROR,
434                     "Deprimed");
435         } else {
436             intermediaryApi.updateCompositionState(compositionId, AcTypeState.PRIMED, StateChangeResult.FAILED,
437                     "Deprime failed!");
438         }
439     }
440
441
442     @Override
443     public void handleRestartComposition(UUID compositionId,
444             List<AutomationCompositionElementDefinition> elementDefinitionList, AcTypeState state)
445             throws PfModelException {
446
447         switch (state) {
448             case PRIMING:
449                 prime(compositionId, elementDefinitionList);
450                 break;
451
452             case DEPRIMING:
453                 // TODO restart process
454
455                 deprime(compositionId);
456                 break;
457
458             default:
459                 // TODO restart process
460
461                 intermediaryApi.updateCompositionState(compositionId, state, StateChangeResult.NO_ERROR, "Restarted");
462         }
463     }
464
465     @Override
466     public void handleRestartInstance(UUID automationCompositionId, AcElementDeploy element,
467             Map<String, Object> properties, DeployState deployState, LockState lockState) throws PfModelException {
468
469          // TODO restart process
470
471         if (DeployState.DEPLOYING.equals(deployState)) {
472             deploy(automationCompositionId, element, properties);
473             return;
474         }
475         if (DeployState.UNDEPLOYING.equals(deployState)) {
476             undeploy(automationCompositionId, element.getId());
477             return;
478         }
479         if (DeployState.UPDATING.equals(deployState)) {
480             update(automationCompositionId, element, properties);
481             return;
482         }
483         if (DeployState.DELETING.equals(deployState)) {
484             delete(automationCompositionId, element.getId());
485             return;
486         }
487         if (LockState.LOCKING.equals(lockState)) {
488             lock(automationCompositionId, element.getId());
489             return;
490         }
491         if (LockState.UNLOCKING.equals(lockState)) {
492             unlock(automationCompositionId, element.getId());
493             return;
494         }
495         intermediaryApi.updateAutomationCompositionElementState(automationCompositionId, element.getId(),
496                 deployState, lockState, StateChangeResult.NO_ERROR, "Restarted");
497     }
498
499
500 AC Element states in failure scenarios
501 --------------------------------------
502
503 During the execution of any state change order, there is always a possibility of failures or exceptions that can occur in the participant.
504 This can be tackled by the followed approaches.
505
506 The participant implementation can handle the exception and revert back the appropriate AC element state, by invoking the
507 'updateAutomationCompositionElementState' api from the participant intermediary.
508
509 Alternatively, the participant can simply throw a PfModelException from its implementation which will be handled by the participant intermediary.
510 The intermediary handles this exception and rolls back the AC element to its previous state with the appropriate stateChange Result.
511 Please refer the following table for the state change reversion that happens in the participant intermediary for the AC elements.
512
513 ================== ==================
514 **Error Scenario** **State Reverted**
515 ================== ==================
516 Prime fails        Commissoned
517
518 Deprime fails      Primed
519
520 Deploy fails       Undeployed
521
522 Undeploy fails     Deployed
523
524 Update fails       Deployed
525
526 Delete fails       Undeployed
527
528 Lock fails         Unlocked
529
530 Unlock fails       Locked
531 ================== ==================
532
533 Considering the above mentioned behavior of the participant Intermediary, it is the responsibility of the developer to tackle the
534 error scenarios in the participant with the suitable approach.
535
536 Tips:
537 If the participant tries to undeploy an element which doesn’t exist in the system any more (due to various other external factors),
538 it could update the element state to ‘undeployed’ using the Intermediary api.