6102fc723f36114950dc792b0dddf3e7ddc14f18
[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 Properties' could be created or updated by ACM-runtime. Participants will receive that Properties during deploy and update events.
181
182   The 'Out Properties' could be created or updated by participants. ACM-runtime will receive that Properties during ParticipantStatus event.
183   The participant can trigger this event using the method sendAcElementInfo.
184   The 'useState' and 'operationalState' can be used as well.
185
186   Is allowed to the participant to read all In/Out Properties and state of all instances handled by the participant using the method getAutomationCompositions.
187   The following code is an example how to update the property 'myProperty' and send to ACM-runtime:
188
189 .. code-block:: java
190
191   var acElement = intermediaryApi.getAutomationCompositionElement(automationCompositionId, elementId);
192   var outProperties = acElement.getOutProperties();
193   outProperties.put("myProperty", myProperty);
194   intermediaryApi.sendAcElementInfo(automationCompositionId, elementId, acElement.getUseState(), acElement.getOperationalState(), outProperties);
195
196 Restart scenario
197 ----------------
198   Restart methods handle the scenario when participant shut down and restart.
199   The method handleRestartComposition will be called for each composition and will be present the 'state' at the time the participant shut down.
200   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.
201
202 In ONAP, the following participants are already implemented in java spring boot for various requirements. The maven modules
203 can be referred here:
204
205   * `HTTP participant <https://github.com/onap/policy-clamp/tree/master/participant/participant-impl/participant-impl-http>`_.
206   * `Kubernetes participant <https://github.com/onap/policy-clamp/tree/master/participant/participant-impl/participant-impl-kubernetes>`_.
207   * `Policy participant <https://github.com/onap/policy-clamp/tree/master/participant/participant-impl/participant-impl-policy>`_.
208   * `A1PMS participant <https://github.com/onap/policy-clamp/tree/master/participant/participant-impl/participant-impl-a1pms>`_.
209   * `Kserve participant <https://github.com/onap/policy-clamp/tree/master/participant/participant-impl/participant-impl-kserve>`_.
210
211 Example of Implementation
212 -------------------------
213
214 This following code is an example of My First Participant:
215   * Application
216   * Parameters
217   * Handler
218
219 The Application class is configured to add the "org.onap.policy.clamp.acm.participant.intermediary" package in SpringBoot component scanning.
220
221 .. code-block:: java
222
223   @SpringBootApplication
224   @ComponentScan({
225     "org.onap.policy.clamp.acm.participant.myfirstparticipant",
226     "org.onap.policy.clamp.acm.participant.intermediary"
227   })
228   @ConfigurationPropertiesScan("org.onap.policy.clamp.acm.participant.myfirstparticipant.parameters")
229   public class MyFirstParticipantApplication {
230
231     public static void main(String[] args) {
232       SpringApplication.run(Application.class, args);
233     }
234   }
235
236 The Participant Parameters class implements the mandatory interface ParticipantParameters.
237 It could contains additional parameters.
238
239 .. code-block:: java
240
241   @Validated
242   @Getter
243   @Setter
244   @ConfigurationProperties(prefix = "participant")
245   public class ParticipantSimParameters implements ParticipantParameters {
246
247     @NotBlank
248     private String myparameter;
249
250     @NotNull
251     @Valid
252     private ParticipantIntermediaryParameters intermediaryParameters;
253   }
254
255 The following example shows the topic parameters and the additional 'myparameter'.
256
257 .. code-block:: bash
258
259   participant:
260     myparameter: my parameter
261     intermediaryParameters:
262       reportingTimeIntervalMs: 120000
263       description: Participant Description
264       participantId: 101c62b3-8918-41b9-a747-d21eb79c6c90
265       clampAutomationCompositionTopics:
266         topicSources:
267           - topic: POLICY-ACRUNTIME-PARTICIPANT
268             servers:
269               - ${topicServer:localhost}
270             topicCommInfrastructure: dmaap
271             fetchTimeout: 15000
272         topicSinks:
273           - topic: POLICY-ACRUNTIME-PARTICIPANT
274             servers:
275               - ${topicServer:localhost}
276             topicCommInfrastructure: dmaap
277       participantSupportedElementTypes:
278         -
279           typeName: org.onap.policy.clamp.acm.MyFirstAutomationCompositionElement
280           typeVersion: 1.0.0
281
282
283 The following example shows the Handler implementation and how could be the implemented the mandatory notifications.
284
285 .. code-block:: java
286
287   @Component
288   @RequiredArgsConstructor
289   public class MyFirstAcElementHandler implements AutomationCompositionElementListener {
290
291     private final ParticipantIntermediaryApi intermediaryApi;
292
293     @Override
294     public void deploy(UUID automationCompositionId, AcElementDeploy element, Map<String, Object> properties)
295             throws PfModelException {
296
297         // TODO deploy process
298
299         if (isDeploySuccess()) {
300             intermediaryApi.updateAutomationCompositionElementState(automationCompositionId, element.getId(),
301                     DeployState.DEPLOYED, null, StateChangeResult.NO_ERROR, "Deployed");
302         } else {
303             intermediaryApi.updateAutomationCompositionElementState(automationCompositionId, element.getId(),
304                     DeployState.UNDEPLOYED, null, StateChangeResult.FAILED, "Deploy failed!");
305         }
306     }
307
308     @Override
309     public void undeploy(UUID automationCompositionId, UUID automationCompositionElementId) throws PfModelException {
310         LOGGER.debug("undeploy call");
311
312         // TODO undeploy process
313
314         if (isUndeploySuccess()) {
315             intermediaryApi.updateAutomationCompositionElementState(automationCompositionId,
316                     automationCompositionElementId, DeployState.UNDEPLOYED, null, StateChangeResult.NO_ERROR,
317                     "Undeployed");
318         } else {
319             intermediaryApi.updateAutomationCompositionElementState(automationCompositionId,
320                     automationCompositionElementId, DeployState.DEPLOYED, null, StateChangeResult.FAILED,
321                     "Undeploy failed!");
322         }
323     }
324
325     @Override
326     public void lock(UUID automationCompositionId, UUID automationCompositionElementId) throws PfModelException {
327
328         // TODO lock process
329
330         if (isLockSuccess()) {
331             intermediaryApi.updateAutomationCompositionElementState(automationCompositionId,
332                     automationCompositionElementId, null, LockState.LOCKED, StateChangeResult.NO_ERROR, "Locked");
333         } else {
334             intermediaryApi.updateAutomationCompositionElementState(automationCompositionId,
335                     automationCompositionElementId, null, LockState.UNLOCKED, StateChangeResult.FAILED, "Lock failed!");
336         }
337     }
338
339     @Override
340     public void unlock(UUID automationCompositionId, UUID automationCompositionElementId) throws PfModelException {
341
342         // TODO unlock process
343
344         if (isUnlockSuccess()) {
345             intermediaryApi.updateAutomationCompositionElementState(automationCompositionId,
346                     automationCompositionElementId, null, LockState.UNLOCKED, StateChangeResult.NO_ERROR, "Unlocked");
347         } else {
348             intermediaryApi.updateAutomationCompositionElementState(automationCompositionId,
349                     automationCompositionElementId, null, LockState.LOCKED, StateChangeResult.FAILED, "Unlock failed!");
350         }
351     }
352
353     @Override
354     public void delete(UUID automationCompositionId, UUID automationCompositionElementId) throws PfModelException {
355
356         // TODO delete process
357
358         if (isDeleteSuccess()) {
359             intermediaryApi.updateAutomationCompositionElementState(automationCompositionId,
360                     automationCompositionElementId, DeployState.DELETED, null, StateChangeResult.NO_ERROR, "Deleted");
361         } else {
362             intermediaryApi.updateAutomationCompositionElementState(automationCompositionId,
363                     automationCompositionElementId, DeployState.UNDEPLOYED, null, StateChangeResult.FAILED,
364                     "Delete failed!");
365         }
366     }
367
368     @Override
369     public void update(UUID automationCompositionId, AcElementDeploy element, Map<String, Object> properties)
370             throws PfModelException {
371
372         // TODO update process
373
374         if (isUpdateSuccess()) {
375             intermediaryApi.updateAutomationCompositionElementState(automationCompositionId, element.getId(),
376                     DeployState.DEPLOYED, null, StateChangeResult.NO_ERROR, "Updated");
377         } else {
378             intermediaryApi.updateAutomationCompositionElementState(automationCompositionId, element.getId(),
379                     DeployState.DEPLOYED, null, StateChangeResult.FAILED, "Update failed!");
380         }
381     }
382
383     @Override
384     public void migrate(UUID automationCompositionId, AcElementDeploy element, UUID compositionTargetId,
385             Map<String, Object> properties) throws PfModelException {
386
387         // TODO migrate process
388
389         if (isMigrateSuccess()) {
390             intermediaryApi.updateAutomationCompositionElementState(automationCompositionId, element.getId(),
391                     DeployState.DEPLOYED, null, StateChangeResult.NO_ERROR, "Migrated");
392         } else {
393             intermediaryApi.updateAutomationCompositionElementState(automationCompositionId, element.getId(),
394                     DeployState.DEPLOYED, null, StateChangeResult.FAILED, "Migrate failed!");
395         }
396     }
397
398     @Override
399     public void prime(UUID compositionId, List<AutomationCompositionElementDefinition> elementDefinitionList)
400             throws PfModelException {
401
402         // TODO prime process
403
404         if (isPrimeSuccess()) {
405             intermediaryApi.updateCompositionState(compositionId, AcTypeState.PRIMED, StateChangeResult.NO_ERROR,
406                     "Primed");
407         } else {
408             intermediaryApi.updateCompositionState(compositionId, AcTypeState.COMMISSIONED, StateChangeResult.FAILED,
409                     "Prime failed!");
410         }
411     }
412
413     @Override
414     public void deprime(UUID compositionId) throws PfModelException {
415
416         // TODO deprime process
417
418         if (isDeprimeSuccess()) {
419             intermediaryApi.updateCompositionState(compositionId, AcTypeState.COMMISSIONED, StateChangeResult.NO_ERROR,
420                     "Deprimed");
421         } else {
422             intermediaryApi.updateCompositionState(compositionId, AcTypeState.PRIMED, StateChangeResult.FAILED,
423                     "Deprime failed!");
424         }
425     }
426
427
428     @Override
429     public void handleRestartComposition(UUID compositionId,
430             List<AutomationCompositionElementDefinition> elementDefinitionList, AcTypeState state)
431             throws PfModelException {
432
433         switch (state) {
434             case PRIMING:
435                 prime(compositionId, elementDefinitionList);
436                 break;
437
438             case DEPRIMING:
439                 // TODO restart process
440
441                 deprime(compositionId);
442                 break;
443
444             default:
445                 // TODO restart process
446
447                 intermediaryApi.updateCompositionState(compositionId, state, StateChangeResult.NO_ERROR, "Restarted");
448         }
449     }
450
451     @Override
452     public void handleRestartInstance(UUID automationCompositionId, AcElementDeploy element,
453             Map<String, Object> properties, DeployState deployState, LockState lockState) throws PfModelException {
454
455          // TODO restart process
456
457         if (DeployState.DEPLOYING.equals(deployState)) {
458             deploy(automationCompositionId, element, properties);
459             return;
460         }
461         if (DeployState.UNDEPLOYING.equals(deployState)) {
462             undeploy(automationCompositionId, element.getId());
463             return;
464         }
465         if (DeployState.UPDATING.equals(deployState)) {
466             update(automationCompositionId, element, properties);
467             return;
468         }
469         if (DeployState.DELETING.equals(deployState)) {
470             delete(automationCompositionId, element.getId());
471             return;
472         }
473         if (LockState.LOCKING.equals(lockState)) {
474             lock(automationCompositionId, element.getId());
475             return;
476         }
477         if (LockState.UNLOCKING.equals(lockState)) {
478             unlock(automationCompositionId, element.getId());
479             return;
480         }
481         intermediaryApi.updateAutomationCompositionElementState(automationCompositionId, element.getId(),
482                 deployState, lockState, StateChangeResult.NO_ERROR, "Restarted");
483     }
484
485