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.
5 .. _acm-participant-guide-label:
7 Participant developer guide
8 ###########################
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.
18 Please refer the following section for a detailed understanding of Inbound and outbound messages a participant interacts with.
23 design-impl/participants/participants
25 Design considerations for a participant
26 ---------------------------------------
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.
33 Hence the new participants has to have this Participant Intermediary module as a dependency and should:
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.
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.
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.
49 clampAutomationCompositionTopics:
52 topic: POLICY-ACRUNTIME-PARTICIPANT
54 - ${topicServer:localhost}
55 topicCommInfrastructure: dmaap
59 topic: POLICY-ACRUNTIME-PARTICIPANT
61 - ${topicServer:localhost}
62 topicCommInfrastructure: dmaap
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.
69 participantSupportedElementTypes:
71 typeName: org.onap.policy.clamp.acm.PolicyAutomationCompositionElement
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, migrating, priming, depriming requests that are coming from the ACM-runtime.
81 The methods are as follows.
85 1. void deploy(CompositionElementDto compositionElement, InstanceElementDto instanceElement) throws PfModelException;
86 2. void undeploy(CompositionElementDto compositionElement, InstanceElementDto instanceElement) throws PfModelException;
87 3. void lock(CompositionElementDto compositionElement, InstanceElementDto instanceElement) throws PfModelException;
88 4. void unlock(CompositionElementDto compositionElement, InstanceElementDto instanceElement) throws PfModelException;
89 5. void delete(CompositionElementDto compositionElement, InstanceElementDto instanceElement) throws PfModelException;
90 6. void update(CompositionElementDto compositionElement, InstanceElementDto instanceElement, InstanceElementDto instanceElementUpdated) throws PfModelException;
91 7. void prime(CompositionDto composition) throws PfModelException;
92 8. void deprime(CompositionDto composition) throws PfModelException;
93 9. void handleRestartComposition(CompositionDto composition, AcTypeState state) throws PfModelException;
94 10. void handleRestartInstance(CompositionElementDto compositionElement, InstanceElementDto instanceElement, DeployState deployState, LockState lockState) throws PfModelException;
95 11. void migrate(CompositionElementDto compositionElement, CompositionElementDto compositionElementTarget, InstanceElementDto instanceElement, InstanceElementDto instanceElementMigrate) throws PfModelException;
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.
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.
106 ParticipantIntermediaryParameters getIntermediaryParameters()
108 Abstract class AcElementListenerV1
109 ----------------------------------
110 This abstract class is introduced to help to maintain the java backward compatibility with AutomationCompositionElementListener implemented in 7.1.0 version.
111 So developers can decide to align to new functionality later. Any new functionality in the future will be wrapped by this class.
113 The Abstract class AcElementListenerV1 supports the follow methods.
117 1. void undeploy(UUID instanceId, UUID elementId) throws PfModelException;
118 2. void deploy(UUID instanceId, AcElementDeploy element, Map<String, Object> inProperties) throws PfModelException;
119 3. void lock(UUID instanceId, UUID elementId) throws PfModelException;
120 4. void unlock(UUID instanceId, UUID elementId) throws PfModelException;
121 5. void delete(UUID instanceId, UUID elementId) throws PfModelException;
122 6. void update(UUID instanceId, AcElementDeploy element, Map<String, Object> inProperties) throws PfModelException;
123 7. void prime(UUID compositionId, List<AutomationCompositionElementDefinition> elementDefinitionList) throws PfModelException;
124 8. void deprime(UUID compositionId) throws PfModelException;
125 9. void handleRestartComposition(UUID compositionId, List<AutomationCompositionElementDefinition> elementDefinitionList, AcTypeState state) throws PfModelException;
126 10. void handleRestartInstance(UUID instanceId, AcElementDeploy element, Map<String, Object> properties, DeployState deployState, LockState lockState) throws PfModelException;
127 11. void migrate(UUID instanceId, AcElementDeploy element, UUID compositionTargetId, Map<String, Object> properties) throws PfModelException;
129 **Note**: this class needs intermediaryApi and it should be passed by constructor. It is declared as protected and can be used.
130 Default implementation are supported for the methods: lock, unlock, update, migrate, delete, prime, deprime, handleRestartComposition and handleRestartInstance.
132 Un example of AutomationCompositionElementHandler implemented in 7.1.0 version and how to use AcElementListenerV1 abstract class:
137 @RequiredArgsConstructor
138 public class AutomationCompositionElementHandler implements AutomationCompositionElementListener {
140 private final ParticipantIntermediaryApi intermediaryApi;
141 private final otherService otherService;
142 ..............................
146 public class AutomationCompositionElementHandler extends AcElementListenerV1 {
148 private final OtherService otherService;
150 public AutomationCompositionElementHandler(ParticipantIntermediaryApi intermediaryApi, OtherService otherService) {
151 super(intermediaryApi);
152 this.otherService = otherService;
154 ..............................
164 public class AutomationCompositionElementHandler implements AutomationCompositionElementListener {
167 private ParticipantIntermediaryApi intermediaryApi;
170 private otherService otherService;
171 ..............................
175 public class AutomationCompositionElementHandler extends AcElementListenerV1 {
178 private otherService otherService;
180 public AutomationCompositionElementHandler(ParticipantIntermediaryApi intermediaryApi) {
181 super(intermediaryApi);
183 ..............................
186 Abstract class AcElementListenerV2
187 ----------------------------------
188 This abstract class is introduced to help to maintain the java backward compatibility with AutomationCompositionElementListener from new releases.
189 Any new functionality in the future will be wrapped by this class.
191 **Note**: this class needs intermediaryApi and it should be passed by constructor. It is declared as protected and can be used.
192 Default implementation are supported for the methods: lock, unlock, update, migrate, delete, prime, deprime, handleRestartComposition and handleRestartInstance.
197 ParticipantIntermediaryApi:
198 The participant intermediary api has the following methods that can be invoked from the participant for the following purposes.
200 #. The requested operations are completed in the handler class and the ACM-runtime needs to be notified.
201 #. Collect all instances data.
202 #. Send out Properties to ACM-runtime.
204 The methods are as follows:
206 This following methods could be invoked to fetch data during each operation in the participant.
210 1. Map<UUID, AutomationComposition> getAutomationCompositions();
211 2. AutomationComposition getAutomationComposition(UUID instanceId);
212 3. AutomationCompositionElement getAutomationCompositionElement(UUID instanceId, UUID elementId);
213 4. Map<UUID, Map<ToscaConceptIdentifier, AutomationCompositionElementDefinition>> getAcElementsDefinitions();
214 5. Map<ToscaConceptIdentifier, AutomationCompositionElementDefinition> getAcElementsDefinitions(UUID compositionId);
215 6. AutomationCompositionElementDefinition getAcElementDefinition(UUID compositionId, ToscaConceptIdentifier elementId);
217 This following methods are invoked to update the outProperties during each operation in the participant.
221 1. void sendAcDefinitionInfo(UUID compositionId, ToscaConceptIdentifier elementId, Map<String, Object> outProperties);
222 2. void sendAcElementInfo(UUID instanceId, UUID elementId, String useState, String operationalState, Map<String, Object> outProperties);
224 This following methods are invoked to update the AC element state or AC element definition state after each operation is completed in the participant.
228 1. void updateAutomationCompositionElementState(UUID instanceId, UUID elementId, DeployState deployState, LockState lockState, StateChangeResult stateChangeResult, String message);
229 2. void updateCompositionState(UUID compositionId, AcTypeState state, StateChangeResult stateChangeResult, String message);
231 In/Out composition Properties
232 -----------------------------
233 The 'Common Properties' could be created or updated by ACM-runtime.
234 Participants will receive that Properties during priming and deprime events by CompositionDto class.
239 public void prime(CompositionDto composition) throws PfModelException {
240 for (var entry : composition.inPropertiesMap().entrySet()) {
241 var elementDefinitionId = entry.getKey();
242 var inProperties = entry.getValue();
248 Participants will receive the Properties related to the element definition by CompositionElementDto class.
253 public void deploy(CompositionElementDto compositionElement, InstanceElementDto instanceElement) throws PfModelException {
254 var inCompositionProperties = compositionElement.inProperties();
258 The 'Out Properties' could be created or updated by participants. ACM-runtime will receive that Properties during ParticipantStatus event.
259 The participant can trigger this event using the method sendAcDefinitionInfo.
261 Participants will receive that outProperties during priming and deprime events by CompositionDto class.
266 public void deprime(CompositionDto composition) throws PfModelException {
267 for (var entry : composition.outPropertiesMap().entrySet()) {
268 var elementDefinitionId = entry.getKey();
269 var outProperties = entry.getValue();
275 Participants will receive the outProperties related to the element definition by CompositionElementDto class.
280 public void deploy(CompositionElementDto compositionElement, InstanceElementDto instanceElement) throws PfModelException {
281 var outCompositionProperties = compositionElement.outProperties();
285 Is allowed to the participant to read all In/Out Properties of all compositions handled by the participant using the method getAcElementsDefinitions.
286 The following code is an example how to update the property 'myProperty' and send to ACM-runtime:
290 var acElement = intermediaryApi.getAcElementDefinition(compositionId, elementDefinitionId);
291 var outProperties = acElement.getOutProperties();
292 outProperties.put("myProperty", myProperty);
293 intermediaryApi.sendAcDefinitionInfo(compositionId, elementDefinitionId, outProperties);
295 In/Out instance Properties
296 --------------------------
297 The 'In/Out Properties' are stored into the instance elements, so each element has its own In/Out Properties.
299 The 'In Properties' could be created or updated by ACM-runtime. Participants will receive that Properties during deploy and update events.
301 The 'Out Properties' could be created or updated by participants. ACM-runtime will receive that Properties during ParticipantStatus event.
302 The participant can trigger this event using the method sendAcElementInfo. The 'useState' and 'operationalState' can be used as well.
303 The 'Out Properties' could be **cleaned**:
305 * by the participant using the method sendAcElementInfo
306 * by intermediary automatically during deleting of the instance
307 * by an update when the instance is in UNDEPLOYED state (changing the elementId)
309 The 'Out Properties' will be **not cleaned** by intermediary:
311 * during DEPLOIYNG (Out Properties will be take from last changes matching by elementId)
313 * during LOCKING/UNLOCKING
314 * during UPDATING/MIGRATING
316 Participants will receive the in/out instance Properties related to the element by InstanceElementDto class.
321 public void deploy(CompositionElementDto compositionElement, InstanceElementDto instanceElement) throws PfModelException {
322 var inProperties = instanceElement.inProperties();
323 var outProperties = instanceElement.outProperties();
327 Is allowed to the participant to read all In/Out Properties and state of all instances handled by the participant using the method getAutomationCompositions.
328 The following code is an example how to update the property 'myProperty' and send to ACM-runtime:
332 var acElement = intermediaryApi.getAutomationCompositionElement(instanceId, elementId);
333 var outProperties = acElement.getOutProperties();
334 outProperties.put("myProperty", myProperty);
335 intermediaryApi.sendAcElementInfo(instanceId, elementId, acElement.getUseState(), acElement.getOperationalState(), outProperties);
337 **Note**: In update and migrate Participants will receive the instance Properties before the merge (instanceElement) and the instance Properties merged (instanceElementUpdated / instanceElementMigrate).
341 Restart methods handle the scenario when participant shut down and restart.
342 During RESTARTING, compositions and instances will be stored in participant memory with In/Out Properties, 'useState' and 'operationalState'.
343 The method handleRestartComposition will be called for each composition and will be present the 'state' at the time the participant shut down.
344 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.
346 In ONAP, the following participants are already implemented in java spring boot for various requirements. The maven modules
347 can be referred here:
349 * `HTTP participant <https://github.com/onap/policy-clamp/tree/master/participant/participant-impl/participant-impl-http>`_.
350 * `Kubernetes participant <https://github.com/onap/policy-clamp/tree/master/participant/participant-impl/participant-impl-kubernetes>`_.
351 * `Policy participant <https://github.com/onap/policy-clamp/tree/master/participant/participant-impl/participant-impl-policy>`_.
352 * `A1PMS participant <https://github.com/onap/policy-clamp/tree/master/participant/participant-impl/participant-impl-a1pms>`_.
353 * `Kserve participant <https://github.com/onap/policy-clamp/tree/master/participant/participant-impl/participant-impl-kserve>`_.
355 Example of Implementation
356 -------------------------
358 This following code is an example of My First Participant:
363 The Application class is configured to add the "org.onap.policy.clamp.acm.participant.intermediary" package in SpringBoot component scanning.
367 @SpringBootApplication
369 "org.onap.policy.clamp.acm.participant.myfirstparticipant",
370 "org.onap.policy.clamp.acm.participant.intermediary"
372 @ConfigurationPropertiesScan("org.onap.policy.clamp.acm.participant.myfirstparticipant.parameters")
373 public class MyFirstParticipantApplication {
375 public static void main(String[] args) {
376 SpringApplication.run(Application.class, args);
380 The Participant Parameters class implements the mandatory interface ParticipantParameters.
381 It could contains additional parameters.
388 @ConfigurationProperties(prefix = "participant")
389 public class ParticipantSimParameters implements ParticipantParameters {
392 private String myparameter;
396 private ParticipantIntermediaryParameters intermediaryParameters;
399 The following example shows the topic parameters and the additional 'myparameter'.
404 myparameter: my parameter
405 intermediaryParameters:
406 reportingTimeIntervalMs: 120000
407 description: Participant Description
408 participantId: 101c62b3-8918-41b9-a747-d21eb79c6c90
409 clampAutomationCompositionTopics:
411 - topic: POLICY-ACRUNTIME-PARTICIPANT
413 - ${topicServer:localhost}
414 topicCommInfrastructure: dmaap
417 - topic: POLICY-ACRUNTIME-PARTICIPANT
419 - ${topicServer:localhost}
420 topicCommInfrastructure: dmaap
421 participantSupportedElementTypes:
423 typeName: org.onap.policy.clamp.acm.MyFirstAutomationCompositionElement
427 The following example shows the Handler implementation and how could be the implemented the mandatory notifications.
432 public class AutomationCompositionElementHandler extends AcElementListenerV2 {
435 public void deploy(CompositionElementDto compositionElement, InstanceElementDto instanceElement)
436 throws PfModelException {
438 // TODO deploy process
440 if (isDeploySuccess()) {
441 intermediaryApi.updateAutomationCompositionElementState(instanceElement.instanceId(),
442 instanceElement.elementId(), DeployState.DEPLOYED, null, StateChangeResult.NO_ERROR,
445 intermediaryApi.updateAutomationCompositionElementState(instanceElement.instanceId(),
446 instanceElement.elementId(), DeployState.UNDEPLOYED, null, StateChangeResult.FAILED,
452 public void undeploy(CompositionElementDto compositionElement, InstanceElementDto instanceElement)
453 throws PfModelException {
455 // TODO undeploy process
457 if (isUndeploySuccess()) {
458 intermediaryApi.updateAutomationCompositionElementState(instanceElement.instanceId(),
459 instanceElement.elementId(), DeployState.UNDEPLOYED, null, StateChangeResult.NO_ERROR,
462 intermediaryApi.updateAutomationCompositionElementState(instanceElement.instanceId(),
463 instanceElement.elementId(), DeployState.DEPLOYED, null, StateChangeResult.FAILED,
469 public void lock(CompositionElementDto compositionElement, InstanceElementDto instanceElement)
470 throws PfModelException {
474 if (isLockSuccess()) {
475 intermediaryApi.updateAutomationCompositionElementState(instanceElement.instanceId(),
476 instanceElement.elementId(), null, LockState.LOCKED, StateChangeResult.NO_ERROR, "Locked");
478 intermediaryApi.updateAutomationCompositionElementState(instanceElement.instanceId(),
479 instanceElement.elementId(), null, LockState.UNLOCKED, StateChangeResult.FAILED, "Lock failed!");
484 public void unlock(CompositionElementDto compositionElement, InstanceElementDto instanceElement)
485 throws PfModelException {
487 // TODO unlock process
489 if (isUnlockSuccess()) {
490 intermediaryApi.updateAutomationCompositionElementState(instanceElement.instanceId(),
491 instanceElement.elementId(), null, LockState.UNLOCKED, StateChangeResult.NO_ERROR, "Unlocked");
493 intermediaryApi.updateAutomationCompositionElementState(instanceElement.instanceId(),
494 instanceElement.elementId(), null, LockState.LOCKED, StateChangeResult.FAILED, "Unlock failed!");
499 public void delete(CompositionElementDto compositionElement, InstanceElementDto instanceElement)
500 throws PfModelException {
502 // TODO delete process
504 if (isDeleteSuccess()) {
505 intermediaryApi.updateAutomationCompositionElementState(instanceElement.instanceId(),
506 instanceElement.elementId(), DeployState.DELETED, null, StateChangeResult.NO_ERROR, "Deleted");
508 intermediaryApi.updateAutomationCompositionElementState(instanceElement.instanceId(),
509 instanceElement.elementId(), DeployState.UNDEPLOYED, null, StateChangeResult.FAILED,
515 public void update(CompositionElementDto compositionElement, InstanceElementDto instanceElement,
516 InstanceElementDto instanceElementUpdated) throws PfModelException {
518 // TODO update process
520 if (isUpdateSuccess()) {
521 intermediaryApi.updateAutomationCompositionElementState(
522 instanceElement.instanceId(), instanceElement.elementId(),
523 DeployState.DEPLOYED, null, StateChangeResult.NO_ERROR, "Updated");
525 intermediaryApi.updateAutomationCompositionElementState(
526 instanceElement.instanceId(), instanceElement.elementId(),
527 DeployState.DEPLOYED, null, StateChangeResult.FAILED, "Update failed!");
532 public void migrate(CompositionElementDto compositionElement, CompositionElementDto compositionElementTarget,
533 InstanceElementDto instanceElement, InstanceElementDto instanceElementMigrate)
534 throws PfModelException
536 // TODO migrate process
538 if (isMigrateSuccess()) {
539 intermediaryApi.updateAutomationCompositionElementState(
540 instanceElement.instanceId(), instanceElement.elementId(),
541 DeployState.DEPLOYED, null, StateChangeResult.NO_ERROR, "Migrated");
543 intermediaryApi.updateAutomationCompositionElementState(
544 instanceElement.instanceId(), instanceElement.elementId(),
545 DeployState.DEPLOYED, null, StateChangeResult.FAILED, "Migrate failed!");
550 public void prime(CompositionDto composition) throws PfModelException {
552 // TODO prime process
554 if (isPrimeSuccess()) {
555 intermediaryApi.updateCompositionState(composition.compositionId(),
556 AcTypeState.PRIMED, StateChangeResult.NO_ERROR, "Primed");
558 intermediaryApi.updateCompositionState(composition.compositionId(),
559 AcTypeState.COMMISSIONED, StateChangeResult.FAILED, "Prime failed!");
564 public void deprime(CompositionDto composition) throws PfModelException {
566 // TODO deprime process
568 if (isDeprimeSuccess()) {
569 intermediaryApi.updateCompositionState(composition.compositionId(), AcTypeState.COMMISSIONED,
570 StateChangeResult.NO_ERROR, "Deprimed");
572 intermediaryApi.updateCompositionState(composition.compositionId(), AcTypeState.PRIMED,
573 StateChangeResult.FAILED, "Deprime failed!");
579 public void handleRestartComposition(CompositionDto composition, AcTypeState state) throws PfModelException {
581 // TODO restart process
584 case PRIMING -> prime(composition);
585 case DEPRIMING -> deprime(composition);
586 default -> intermediaryApi
587 .updateCompositionState(composition.compositionId(), state, StateChangeResult.NO_ERROR, "Restarted");
592 public void handleRestartInstance(CompositionElementDto compositionElement, InstanceElementDto instanceElement,
593 DeployState deployState, LockState lockState) throws PfModelException {
595 // TODO restart process
597 if (DeployState.DEPLOYING.equals(deployState)) {
598 deploy(compositionElement, instanceElement);
601 if (DeployState.UNDEPLOYING.equals(deployState)) {
602 undeploy(compositionElement, instanceElement);
605 if (DeployState.UPDATING.equals(deployState)) {
606 update(compositionElement, instanceElement, instanceElement);
609 if (DeployState.DELETING.equals(deployState)) {
610 delete(compositionElement, instanceElement);
613 if (LockState.LOCKING.equals(lockState)) {
614 lock(compositionElement, instanceElement);
617 if (LockState.UNLOCKING.equals(lockState)) {
618 unlock(compositionElement, instanceElement);
621 intermediaryApi.updateAutomationCompositionElementState(instanceElement.instanceId(),
622 instanceElement.elementId(), deployState, lockState, StateChangeResult.NO_ERROR, "Restarted");
626 AC Element states in failure scenarios
627 --------------------------------------
629 During the execution of any state change order, there is always a possibility of failures or exceptions that can occur in the participant.
630 This can be tackled by the followed approaches.
632 The participant implementation can handle the exception and revert back the appropriate AC element state, by invoking the
633 'updateAutomationCompositionElementState' api from the participant intermediary.
635 Alternatively, the participant can simply throw a PfModelException from its implementation which will be handled by the participant intermediary.
636 The intermediary handles this exception and rolls back the AC element to its previous state with the appropriate stateChange Result.
637 Please refer the following table for the state change reversion that happens in the participant intermediary for the AC elements.
639 ================== ==================
640 **Error Scenario** **State Reverted**
641 ================== ==================
642 Prime fails Commissoned
646 Deploy fails Undeployed
648 Undeploy fails Deployed
650 Update fails Deployed
652 Delete fails Undeployed
658 Migrate fails Deployed
659 ================== ==================
661 Considering the above mentioned behavior of the participant Intermediary, it is the responsibility of the developer to tackle the
662 error scenarios in the participant with the suitable approach.
665 If the participant tries to undeploy an element which doesn’t exist in the system any more (due to various other external factors),
666 it could update the element state to ‘undeployed’ using the Intermediary api.