2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2021-2023 Nordix Foundation.
4 * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
5 * ================================================================================
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 * SPDX-License-Identifier: Apache-2.0
19 * ============LICENSE_END=========================================================
22 package org.onap.policy.clamp.acm.participant.intermediary.handler;
24 import com.att.aft.dme2.internal.apache.commons.lang.StringUtils;
25 import java.util.ArrayList;
26 import java.util.HashMap;
27 import java.util.LinkedHashMap;
28 import java.util.List;
30 import java.util.UUID;
32 import org.onap.policy.clamp.acm.participant.intermediary.api.AutomationCompositionElementListener;
33 import org.onap.policy.clamp.acm.participant.intermediary.comm.ParticipantMessagePublisher;
34 import org.onap.policy.clamp.acm.participant.intermediary.parameters.ParticipantParameters;
35 import org.onap.policy.clamp.models.acm.concepts.AcElementDeploy;
36 import org.onap.policy.clamp.models.acm.concepts.AcElementDeployAck;
37 import org.onap.policy.clamp.models.acm.concepts.AutomationComposition;
38 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElement;
39 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElementDefinition;
40 import org.onap.policy.clamp.models.acm.concepts.DeployState;
41 import org.onap.policy.clamp.models.acm.concepts.LockState;
42 import org.onap.policy.clamp.models.acm.concepts.ParticipantDeploy;
43 import org.onap.policy.clamp.models.acm.concepts.ParticipantUtils;
44 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.AutomationCompositionDeploy;
45 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.AutomationCompositionDeployAck;
46 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.AutomationCompositionStateChange;
47 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.ParticipantMessageType;
48 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.DeployOrder;
49 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.LockOrder;
50 import org.onap.policy.clamp.models.acm.persistence.provider.AcInstanceStateResolver;
51 import org.onap.policy.models.base.PfModelException;
52 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
53 import org.onap.policy.models.tosca.authorative.concepts.ToscaNodeTemplate;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56 import org.springframework.stereotype.Component;
59 * This class is responsible for managing the state of all automation compositions in the participant.
62 public class AutomationCompositionHandler {
63 private static final Logger LOGGER = LoggerFactory.getLogger(AutomationCompositionHandler.class);
65 private final UUID participantId;
66 private final ParticipantMessagePublisher publisher;
67 private final AcInstanceStateResolver acInstanceStateResolver;
70 private final Map<UUID, AutomationComposition> automationCompositionMap = new LinkedHashMap<>();
73 private final Map<UUID, AutomationCompositionElement> elementsOnThisParticipant = new LinkedHashMap<>();
76 private final List<AutomationCompositionElementListener> listeners = new ArrayList<>();
79 * Constructor, set the participant ID and messageSender.
81 * @param parameters the parameters of the participant
82 * @param publisher the ParticipantMessage Publisher
84 public AutomationCompositionHandler(ParticipantParameters parameters, ParticipantMessagePublisher publisher) {
85 this.participantId = parameters.getIntermediaryParameters().getParticipantId();
86 this.publisher = publisher;
87 this.acInstanceStateResolver = new AcInstanceStateResolver();
90 public void registerAutomationCompositionElementListener(AutomationCompositionElementListener listener) {
91 listeners.add(listener);
95 * Handle a automation composition element state change message.
97 * @param automationCompositionId the automationComposition Id
98 * @param id the automationComposition UUID
99 * @param deployState the DeployState state
100 * @param lockState the LockState state
102 public void updateAutomationCompositionElementState(UUID automationCompositionId, UUID id, DeployState deployState,
103 LockState lockState) {
106 LOGGER.warn("Cannot update Automation composition element state, id is null");
110 // Update states of AutomationCompositionElement in automationCompositionMap
111 for (var automationComposition : automationCompositionMap.values()) {
112 var element = automationComposition.getElements().get(id);
113 if (element != null) {
114 element.setDeployState(deployState);
115 element.setLockState(lockState);
116 element.setUseState(getUseState(automationCompositionId, id));
117 element.setOperationalState(getOperationalState(automationCompositionId, id));
118 element.setStatusProperties(getStatusProperties(automationCompositionId, id));
120 var checkOpt = automationComposition.getElements().values().stream()
121 .filter(acElement -> !deployState.equals(acElement.getDeployState())).findAny();
122 if (checkOpt.isEmpty()) {
123 automationComposition.setDeployState(deployState);
125 checkOpt = automationComposition.getElements().values().stream()
126 .filter(acElement -> !lockState.equals(acElement.getLockState())).findAny();
127 if (checkOpt.isEmpty()) {
128 automationComposition.setLockState(lockState);
132 // Update states of AutomationCompositionElement in elementsOnThisParticipant
133 var acElement = elementsOnThisParticipant.get(id);
134 if (acElement != null) {
135 var automationCompositionStateChangeAck =
136 new AutomationCompositionDeployAck(ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
137 automationCompositionStateChangeAck.setParticipantId(participantId);
138 automationCompositionStateChangeAck.setAutomationCompositionId(automationCompositionId);
139 acElement.setDeployState(deployState);
140 acElement.setLockState(lockState);
141 acElement.setUseState(getUseState(automationCompositionId, id));
142 acElement.setOperationalState(getOperationalState(automationCompositionId, id));
143 acElement.setStatusProperties(getStatusProperties(automationCompositionId, id));
144 automationCompositionStateChangeAck.getAutomationCompositionResultMap().put(acElement.getId(),
145 new AcElementDeployAck(deployState, lockState, acElement.getOperationalState(),
146 acElement.getUseState(), acElement.getStatusProperties(), true,
147 "Automation composition element {} state changed to {}\", id, newState)"));
148 LOGGER.debug("Automation composition element {} state changed to {}", id, deployState);
149 automationCompositionStateChangeAck
150 .setMessage("AutomationCompositionElement state changed to {} " + deployState);
151 automationCompositionStateChangeAck.setResult(true);
152 publisher.sendAutomationCompositionAck(automationCompositionStateChangeAck);
157 * Handle a automation composition state change message.
159 * @param stateChangeMsg the state change message
160 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
162 public void handleAutomationCompositionStateChange(AutomationCompositionStateChange stateChangeMsg,
163 List<AutomationCompositionElementDefinition> acElementDefinitions) {
164 if (stateChangeMsg.getAutomationCompositionId() == null) {
168 var automationComposition = automationCompositionMap.get(stateChangeMsg.getAutomationCompositionId());
170 if (automationComposition == null) {
171 var automationCompositionAck =
172 new AutomationCompositionDeployAck(ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
173 automationCompositionAck.setParticipantId(participantId);
174 automationCompositionAck.setMessage("Automation composition " + stateChangeMsg.getAutomationCompositionId()
175 + " does not use this participant " + participantId);
176 automationCompositionAck.setResult(false);
177 automationCompositionAck.setResponseTo(stateChangeMsg.getMessageId());
178 automationCompositionAck.setAutomationCompositionId(stateChangeMsg.getAutomationCompositionId());
179 publisher.sendAutomationCompositionAck(automationCompositionAck);
180 LOGGER.debug("Automation composition {} does not use this participant",
181 stateChangeMsg.getAutomationCompositionId());
185 if (!checkConsistantOrderState(automationComposition, stateChangeMsg.getDeployOrderedState(),
186 stateChangeMsg.getLockOrderedState())) {
187 var automationCompositionAck =
188 new AutomationCompositionDeployAck(ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
189 automationCompositionAck.setParticipantId(participantId);
190 automationCompositionAck.setMessage("Automation composition is already in state "
191 + stateChangeMsg.getDeployOrderedState() + " and " + stateChangeMsg.getLockOrderedState());
192 automationCompositionAck.setResult(false);
193 automationCompositionAck.setAutomationCompositionId(automationComposition.getInstanceId());
194 publisher.sendAutomationCompositionAck(automationCompositionAck);
198 if (DeployOrder.NONE.equals(stateChangeMsg.getDeployOrderedState())) {
199 handleLockOrderState(automationComposition, stateChangeMsg.getLockOrderedState(),
200 stateChangeMsg.getStartPhase(), acElementDefinitions);
202 handleDeployOrderState(automationComposition, stateChangeMsg.getDeployOrderedState(),
203 stateChangeMsg.getStartPhase(), acElementDefinitions);
207 private boolean checkConsistantOrderState(AutomationComposition automationComposition, DeployOrder deployOrder,
208 LockOrder lockOrder) {
209 return acInstanceStateResolver.resolve(deployOrder, lockOrder, automationComposition.getDeployState(),
210 automationComposition.getLockState()) != null;
214 * Method to handle state changes.
216 * @param automationComposition participant response
217 * @param orderedState automation composition ordered state
218 * @param startPhaseMsg startPhase from message
219 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
221 private void handleDeployOrderState(final AutomationComposition automationComposition, DeployOrder orderedState,
222 Integer startPhaseMsg, List<AutomationCompositionElementDefinition> acElementDefinitions) {
224 if (DeployOrder.UNDEPLOY.equals(orderedState)) {
225 handleUndeployState(automationComposition, startPhaseMsg, acElementDefinitions);
227 LOGGER.debug("StateChange message has no state, state is null {}", automationComposition.getKey());
232 * Method to handle state changes.
234 * @param automationComposition participant response
235 * @param orderedState automation composition ordered state
236 * @param startPhaseMsg startPhase from message
237 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
239 private void handleLockOrderState(final AutomationComposition automationComposition, LockOrder orderedState,
240 Integer startPhaseMsg, List<AutomationCompositionElementDefinition> acElementDefinitions) {
242 switch (orderedState) {
244 handleLockState(automationComposition, startPhaseMsg, acElementDefinitions);
247 handleUnlockState(automationComposition, startPhaseMsg, acElementDefinitions);
250 LOGGER.debug("StateChange message has no state, state is null {}", automationComposition.getKey());
256 * Handle a automation composition Deploy message.
258 * @param updateMsg the Deploy message
259 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
261 public void handleAutomationCompositionDeploy(AutomationCompositionDeploy updateMsg,
262 List<AutomationCompositionElementDefinition> acElementDefinitions) {
264 if (updateMsg.getParticipantUpdatesList().isEmpty()) {
265 LOGGER.warn("No AutomationCompositionElement updates in message {}",
266 updateMsg.getAutomationCompositionId());
270 for (var participantDeploy : updateMsg.getParticipantUpdatesList()) {
271 if (participantId.equals(participantDeploy.getParticipantId())) {
272 if (updateMsg.isFirstStartPhase()) {
273 initializeDeploy(updateMsg.getMessageId(), updateMsg.getAutomationCompositionId(),
276 callParticipanDeploy(participantDeploy.getAcElementList(), acElementDefinitions,
277 updateMsg.getStartPhase(), updateMsg.getAutomationCompositionId());
282 private void initializeDeploy(UUID messageId, UUID instanceId, ParticipantDeploy participantDeploy) {
283 var automationComposition = automationCompositionMap.get(instanceId);
285 if (automationComposition != null) {
286 var automationCompositionUpdateAck =
287 new AutomationCompositionDeployAck(ParticipantMessageType.AUTOMATION_COMPOSITION_DEPLOY_ACK);
288 automationCompositionUpdateAck.setParticipantId(participantId);
290 automationCompositionUpdateAck.setMessage(
291 "Automation composition " + instanceId + " already defined on participant " + participantId);
292 automationCompositionUpdateAck.setResult(false);
293 automationCompositionUpdateAck.setResponseTo(messageId);
294 automationCompositionUpdateAck.setAutomationCompositionId(instanceId);
295 publisher.sendAutomationCompositionAck(automationCompositionUpdateAck);
299 automationComposition = new AutomationComposition();
300 automationComposition.setInstanceId(instanceId);
301 var acElements = storeElementsOnThisParticipant(participantDeploy);
302 automationComposition.setElements(prepareAcElementMap(acElements));
303 automationCompositionMap.put(instanceId, automationComposition);
306 private void callParticipanDeploy(List<AcElementDeploy> acElements,
307 List<AutomationCompositionElementDefinition> acElementDefinitions, Integer startPhaseMsg,
308 UUID automationCompositionId) {
310 for (var element : acElements) {
311 var acElementNodeTemplate = getAcElementNodeTemplate(acElementDefinitions, element.getDefinition());
312 if (acElementNodeTemplate != null) {
313 int startPhase = ParticipantUtils.findStartPhase(acElementNodeTemplate.getProperties());
314 if (startPhaseMsg.equals(startPhase)) {
315 for (var acElementListener : listeners) {
316 var map = new HashMap<>(acElementNodeTemplate.getProperties());
317 map.putAll(element.getProperties());
318 acElementListener.deploy(automationCompositionId, element, map);
323 } catch (PfModelException e) {
324 LOGGER.debug("Automation composition element update failed {}", automationCompositionId);
329 private ToscaNodeTemplate getAcElementNodeTemplate(
330 List<AutomationCompositionElementDefinition> acElementDefinitions, ToscaConceptIdentifier acElementDefId) {
332 for (var acElementDefinition : acElementDefinitions) {
333 if (acElementDefId.getName().contains(acElementDefinition.getAcElementDefinitionId().getName())) {
334 return acElementDefinition.getAutomationCompositionElementToscaNodeTemplate();
340 private List<AutomationCompositionElement> storeElementsOnThisParticipant(ParticipantDeploy participantDeploy) {
341 List<AutomationCompositionElement> acElementList = new ArrayList<>();
342 for (var element : participantDeploy.getAcElementList()) {
343 var acElement = new AutomationCompositionElement();
344 acElement.setId(element.getId());
345 acElement.setParticipantId(participantDeploy.getParticipantId());
346 acElement.setDefinition(element.getDefinition());
347 acElement.setDeployState(DeployState.DEPLOYING);
348 acElement.setLockState(LockState.NONE);
349 elementsOnThisParticipant.put(element.getId(), acElement);
350 acElementList.add(acElement);
352 return acElementList;
355 private Map<UUID, AutomationCompositionElement> prepareAcElementMap(List<AutomationCompositionElement> acElements) {
356 Map<UUID, AutomationCompositionElement> acElementMap = new LinkedHashMap<>();
357 for (var element : acElements) {
358 acElementMap.put(element.getId(), element);
364 * Method to handle when the new state from participant is UNINITIALISED state.
366 * @param automationComposition participant response
367 * @param startPhaseMsg startPhase from message
368 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
370 private void handleUndeployState(final AutomationComposition automationComposition, Integer startPhaseMsg,
371 List<AutomationCompositionElementDefinition> acElementDefinitions) {
373 automationComposition.getElements().values().stream()
374 .forEach(acElement -> automationCompositionElementUndeploy(automationComposition.getInstanceId(),
375 acElement, startPhaseMsg, acElementDefinitions));
377 boolean isAllUninitialised = automationComposition.getElements().values().stream()
378 .filter(element -> !DeployState.UNDEPLOYED.equals(element.getDeployState())).findAny().isEmpty();
379 if (isAllUninitialised) {
380 automationCompositionMap.remove(automationComposition.getInstanceId());
381 automationComposition.getElements().values()
382 .forEach(element -> elementsOnThisParticipant.remove(element.getId()));
387 * Method to handle when the new state from participant is PASSIVE state.
389 * @param automationComposition participant response
390 * @param startPhaseMsg startPhase from message
391 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
393 private void handleLockState(final AutomationComposition automationComposition, Integer startPhaseMsg,
394 List<AutomationCompositionElementDefinition> acElementDefinitions) {
395 automationComposition.getElements().values().stream()
396 .forEach(acElement -> automationCompositionElementLock(automationComposition.getInstanceId(), acElement,
397 startPhaseMsg, acElementDefinitions));
401 * Method to handle when the new state from participant is RUNNING state.
403 * @param automationComposition participant response
404 * @param startPhaseMsg startPhase from message
405 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
407 private void handleUnlockState(final AutomationComposition automationComposition, Integer startPhaseMsg,
408 List<AutomationCompositionElementDefinition> acElementDefinitions) {
409 automationComposition.getElements().values().stream()
410 .forEach(acElement -> automationCompositionElementUnlock(automationComposition.getInstanceId(),
411 acElement, startPhaseMsg, acElementDefinitions));
414 private void automationCompositionElementLock(UUID instanceId, AutomationCompositionElement acElement,
415 Integer startPhaseMsg, List<AutomationCompositionElementDefinition> acElementDefinitions) {
416 var acElementNodeTemplate = getAcElementNodeTemplate(acElementDefinitions, acElement.getDefinition());
417 if (acElementNodeTemplate != null) {
418 int startPhase = ParticipantUtils.findStartPhase(acElementNodeTemplate.getProperties());
419 if (startPhaseMsg.equals(startPhase)) {
420 for (var acElementListener : listeners) {
422 acElementListener.lock(instanceId, acElement.getId());
423 updateAutomationCompositionElementState(instanceId, acElement.getId(), DeployState.DEPLOYED,
425 } catch (PfModelException e) {
426 LOGGER.error("Automation composition element lock failed {}", instanceId);
433 private void automationCompositionElementUnlock(UUID instanceId, AutomationCompositionElement acElement,
434 Integer startPhaseMsg, List<AutomationCompositionElementDefinition> acElementDefinitions) {
435 var acElementNodeTemplate = getAcElementNodeTemplate(acElementDefinitions, acElement.getDefinition());
436 if (acElementNodeTemplate != null) {
437 int startPhase = ParticipantUtils.findStartPhase(acElementNodeTemplate.getProperties());
438 if (startPhaseMsg.equals(startPhase)) {
439 for (var acElementListener : listeners) {
441 acElementListener.unlock(instanceId, acElement.getId());
442 updateAutomationCompositionElementState(instanceId, acElement.getId(), DeployState.DEPLOYED,
444 } catch (PfModelException e) {
445 LOGGER.error("Automation composition element unlock failed {}", instanceId);
452 private void automationCompositionElementUndeploy(UUID instanceId, AutomationCompositionElement acElement,
453 Integer startPhaseMsg, List<AutomationCompositionElementDefinition> acElementDefinitions) {
454 var acElementNodeTemplate = getAcElementNodeTemplate(acElementDefinitions, acElement.getDefinition());
455 if (acElementNodeTemplate != null) {
456 int startPhase = ParticipantUtils.findStartPhase(acElementNodeTemplate.getProperties());
457 if (startPhaseMsg.equals(startPhase)) {
458 for (var acElementListener : listeners) {
460 acElementListener.undeploy(instanceId, acElement.getId());
461 } catch (PfModelException e) {
462 LOGGER.error("Automation composition element update failed {}", instanceId);
472 * @param instanceId the instance Id
473 * @param acElementId the Automation Composition Element Id
474 * @return the UseState of the Automation Composition Element
476 public String getUseState(UUID instanceId, UUID acElementId) {
477 var result = new StringBuilder();
478 for (var acElementListener : listeners) {
480 var state = acElementListener.getUseState(instanceId, acElementId);
481 if (!StringUtils.isBlank(state)) {
482 result.append(state);
484 } catch (PfModelException e) {
485 LOGGER.error("Automation composition element get Use State failed {}", acElementId);
488 return result.toString();
492 * Get OperationalState.
494 * @param instanceId the instance Id
495 * @param acElementId the Automation Composition Element Id
496 * @return the OperationalState of the Automation Composition Element
498 public String getOperationalState(UUID instanceId, UUID acElementId) {
499 var result = new StringBuilder();
500 for (var acElementListener : listeners) {
502 var state = acElementListener.getOperationalState(instanceId, acElementId);
503 if (!StringUtils.isBlank(state)) {
504 result.append(state);
506 } catch (PfModelException e) {
507 LOGGER.error("Automation composition element get Use State failed {}", acElementId);
510 return result.toString();
514 * Get StatusProperties.
516 * @param instanceId the instance Id
517 * @param acElementId the Automation Composition Element Id
518 * @return the Status Properties Map
520 public Map<String, Object> getStatusProperties(UUID instanceId, UUID acElementId) {
521 Map<String, Object> result = new HashMap<>();
522 for (var acElementListener : listeners) {
524 result.putAll(acElementListener.getStatusProperties(instanceId, acElementId));
525 } catch (PfModelException e) {
526 LOGGER.error("Automation composition element get Status Properties failed {}", acElementId);