2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2021-2024 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 java.util.ArrayList;
25 import java.util.HashMap;
26 import java.util.List;
28 import java.util.UUID;
29 import java.util.stream.Collectors;
30 import org.onap.policy.clamp.acm.participant.intermediary.api.CompositionDto;
31 import org.onap.policy.clamp.acm.participant.intermediary.api.CompositionElementDto;
32 import org.onap.policy.clamp.acm.participant.intermediary.api.InstanceElementDto;
33 import org.onap.policy.clamp.acm.participant.intermediary.comm.ParticipantMessagePublisher;
34 import org.onap.policy.clamp.models.acm.concepts.AcElementDeploy;
35 import org.onap.policy.clamp.models.acm.concepts.AcTypeState;
36 import org.onap.policy.clamp.models.acm.concepts.AutomationComposition;
37 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElement;
38 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElementDefinition;
39 import org.onap.policy.clamp.models.acm.concepts.DeployState;
40 import org.onap.policy.clamp.models.acm.concepts.LockState;
41 import org.onap.policy.clamp.models.acm.concepts.ParticipantDeploy;
42 import org.onap.policy.clamp.models.acm.concepts.ParticipantRestartAc;
43 import org.onap.policy.clamp.models.acm.concepts.ParticipantState;
44 import org.onap.policy.clamp.models.acm.concepts.ParticipantUtils;
45 import org.onap.policy.clamp.models.acm.concepts.StateChangeResult;
46 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionDeploy;
47 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionDeployAck;
48 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionMigration;
49 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionStateChange;
50 import org.onap.policy.clamp.models.acm.messages.kafka.participant.ParticipantMessageType;
51 import org.onap.policy.clamp.models.acm.messages.kafka.participant.ParticipantPrimeAck;
52 import org.onap.policy.clamp.models.acm.messages.kafka.participant.PropertiesUpdate;
53 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.DeployOrder;
54 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.LockOrder;
55 import org.onap.policy.clamp.models.acm.persistence.provider.AcInstanceStateResolver;
56 import org.onap.policy.clamp.models.acm.utils.AcmUtils;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
59 import org.springframework.stereotype.Component;
62 * This class is responsible for managing the state of all automation compositions in the participant.
65 public class AutomationCompositionHandler {
66 private static final Logger LOGGER = LoggerFactory.getLogger(AutomationCompositionHandler.class);
68 private final CacheProvider cacheProvider;
69 private final ParticipantMessagePublisher publisher;
70 private final ThreadHandler listener;
71 private final AcInstanceStateResolver acInstanceStateResolver;
74 * Constructor, set the participant ID and messageSender.
76 * @param cacheProvider the Cache Provider
77 * @param publisher the ParticipantMessage Publisher
78 * @param listener the ThreadHandler Listener
80 public AutomationCompositionHandler(CacheProvider cacheProvider, ParticipantMessagePublisher publisher,
81 ThreadHandler listener) {
82 this.cacheProvider = cacheProvider;
83 this.publisher = publisher;
84 this.listener = listener;
85 this.acInstanceStateResolver = new AcInstanceStateResolver();
89 * Handle a automation composition state change message.
91 * @param stateChangeMsg the state change message
93 public void handleAutomationCompositionStateChange(AutomationCompositionStateChange stateChangeMsg) {
94 if (stateChangeMsg.getAutomationCompositionId() == null) {
98 var automationComposition = cacheProvider.getAutomationComposition(stateChangeMsg.getAutomationCompositionId());
100 if (automationComposition == null) {
101 if (DeployOrder.DELETE.equals(stateChangeMsg.getDeployOrderedState())) {
102 var automationCompositionAck = new AutomationCompositionDeployAck(
103 ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
104 automationCompositionAck.setParticipantId(cacheProvider.getParticipantId());
105 automationCompositionAck.setMessage("Already deleted or never used");
106 automationCompositionAck.setResult(true);
107 automationCompositionAck.setStateChangeResult(StateChangeResult.NO_ERROR);
108 automationCompositionAck.setResponseTo(stateChangeMsg.getMessageId());
109 automationCompositionAck.setAutomationCompositionId(stateChangeMsg.getAutomationCompositionId());
110 publisher.sendAutomationCompositionAck(automationCompositionAck);
112 LOGGER.debug("Automation composition {} does not use this participant",
113 stateChangeMsg.getAutomationCompositionId());
118 if (!checkConsistantOrderState(automationComposition, stateChangeMsg.getDeployOrderedState(),
119 stateChangeMsg.getLockOrderedState())) {
120 LOGGER.warn("Not Consistant OrderState Automation composition {}",
121 stateChangeMsg.getAutomationCompositionId());
125 if (DeployOrder.NONE.equals(stateChangeMsg.getDeployOrderedState())) {
126 handleLockOrderState(stateChangeMsg.getMessageId(), automationComposition,
127 stateChangeMsg.getLockOrderedState(), stateChangeMsg.getStartPhase());
129 handleDeployOrderState(stateChangeMsg.getMessageId(), automationComposition,
130 stateChangeMsg.getDeployOrderedState(), stateChangeMsg.getStartPhase());
134 private boolean checkConsistantOrderState(AutomationComposition automationComposition, DeployOrder deployOrder,
135 LockOrder lockOrder) {
136 if (DeployOrder.UPDATE.equals(deployOrder)) {
139 return acInstanceStateResolver.resolve(deployOrder, lockOrder, automationComposition.getDeployState(),
140 automationComposition.getLockState(), automationComposition.getStateChangeResult()) != null;
144 * Method to handle state changes.
146 * @param messageId the messageId
147 * @param automationComposition participant response
148 * @param orderedState automation composition ordered state
149 * @param startPhaseMsg startPhase from message
151 private void handleDeployOrderState(UUID messageId, final AutomationComposition automationComposition,
152 DeployOrder orderedState, Integer startPhaseMsg) {
154 switch (orderedState) {
156 handleUndeployState(messageId, automationComposition, startPhaseMsg);
159 handleDeleteState(messageId, automationComposition, startPhaseMsg);
163 LOGGER.error("StateChange message has no state, state is null {}", automationComposition.getKey());
169 * Method to handle state changes.
171 * @param messageId the messageId
172 * @param automationComposition participant response
173 * @param orderedState automation composition ordered state
174 * @param startPhaseMsg startPhase from message
176 private void handleLockOrderState(UUID messageId, final AutomationComposition automationComposition,
177 LockOrder orderedState, Integer startPhaseMsg) {
179 switch (orderedState) {
181 handleLockState(messageId, automationComposition, startPhaseMsg);
184 handleUnlockState(messageId, automationComposition, startPhaseMsg);
187 LOGGER.error("StateChange message has no state, state is null {}", automationComposition.getKey());
193 * Handle a automation composition properties update message.
195 * @param updateMsg the properties update message
197 public void handleAcPropertyUpdate(PropertiesUpdate updateMsg) {
199 if (updateMsg.getParticipantUpdatesList().isEmpty()) {
200 LOGGER.warn("No AutomationCompositionElement updates in message {}",
201 updateMsg.getAutomationCompositionId());
205 for (var participantDeploy : updateMsg.getParticipantUpdatesList()) {
206 if (cacheProvider.getParticipantId().equals(participantDeploy.getParticipantId())) {
208 var acCopy = new AutomationComposition(cacheProvider.getAutomationComposition(
209 updateMsg.getAutomationCompositionId()));
210 updateExistingElementsOnThisParticipant(updateMsg.getAutomationCompositionId(), participantDeploy,
211 DeployState.UPDATING);
213 callParticipantUpdateProperty(updateMsg.getMessageId(), participantDeploy.getAcElementList(), acCopy);
219 * Handle a automation composition Deploy message.
221 * @param deployMsg the Deploy message
223 public void handleAutomationCompositionDeploy(AutomationCompositionDeploy deployMsg) {
225 if (deployMsg.getParticipantUpdatesList().isEmpty()) {
226 LOGGER.warn("No AutomationCompositionElement deploy in message {}", deployMsg.getAutomationCompositionId());
230 for (var participantDeploy : deployMsg.getParticipantUpdatesList()) {
231 if (cacheProvider.getParticipantId().equals(participantDeploy.getParticipantId())) {
232 if (deployMsg.isFirstStartPhase()) {
233 cacheProvider.initializeAutomationComposition(deployMsg.getCompositionId(),
234 deployMsg.getAutomationCompositionId(), participantDeploy);
236 callParticipanDeploy(deployMsg.getMessageId(), participantDeploy.getAcElementList(),
237 deployMsg.getStartPhase(), deployMsg.getAutomationCompositionId());
242 private void callParticipanDeploy(UUID messageId, List<AcElementDeploy> acElementDeployList,
243 Integer startPhaseMsg, UUID instanceId) {
244 var automationComposition = cacheProvider.getAutomationComposition(instanceId);
245 for (var elementDeploy : acElementDeployList) {
246 var element = automationComposition.getElements().get(elementDeploy.getId());
247 var compositionInProperties = cacheProvider
248 .getCommonProperties(automationComposition.getCompositionId(), element.getDefinition());
249 int startPhase = ParticipantUtils.findStartPhase(compositionInProperties);
250 if (startPhaseMsg.equals(startPhase)) {
251 var compositionElement = createCompositionElementDto(automationComposition.getCompositionId(),
252 element, compositionInProperties);
253 var instanceElement = new InstanceElementDto(instanceId, elementDeploy.getId(),
254 elementDeploy.getToscaServiceTemplateFragment(),
255 elementDeploy.getProperties(), element.getOutProperties());
256 listener.deploy(messageId, compositionElement, instanceElement);
261 private CompositionElementDto createCompositionElementDto(UUID compositionId, AutomationCompositionElement element,
262 Map<String, Object> compositionInProperties) {
263 var compositionOutProperties = cacheProvider.getAcElementsDefinitions()
264 .get(compositionId).get(element.getDefinition()).getOutProperties();
265 return new CompositionElementDto(compositionId,
266 element.getDefinition(), compositionInProperties, compositionOutProperties);
269 private Map<UUID, CompositionElementDto> getCompositionElementDtoMap(AutomationComposition automationComposition,
270 UUID compositionId) {
271 Map<UUID, CompositionElementDto> map = new HashMap<>();
272 for (var element : automationComposition.getElements().values()) {
273 var compositionInProperties = cacheProvider.getCommonProperties(compositionId, element.getDefinition());
274 var compositionElement = createCompositionElementDto(compositionId, element, compositionInProperties);
275 map.put(element.getId(), compositionElement);
280 private Map<UUID, CompositionElementDto> getCompositionElementDtoMap(AutomationComposition automationComposition) {
281 return getCompositionElementDtoMap(automationComposition, automationComposition.getCompositionId());
284 private Map<UUID, InstanceElementDto> getInstanceElementDtoMap(AutomationComposition automationComposition) {
285 Map<UUID, InstanceElementDto> map = new HashMap<>();
286 for (var element : automationComposition.getElements().values()) {
287 var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(),
288 null, element.getProperties(), element.getOutProperties());
289 map.put(element.getId(), instanceElement);
294 private void callParticipantUpdateProperty(UUID messageId, List<AcElementDeploy> acElements,
295 AutomationComposition acCopy) {
296 var instanceElementDtoMap = getInstanceElementDtoMap(acCopy);
297 var instanceElementDtoMapUpdated = getInstanceElementDtoMap(
298 cacheProvider.getAutomationComposition(acCopy.getInstanceId()));
299 var compositionElementDtoMap = getCompositionElementDtoMap(acCopy);
300 for (var acElement : acElements) {
301 listener.update(messageId, compositionElementDtoMap.get(acElement.getId()),
302 instanceElementDtoMap.get(acElement.getId()), instanceElementDtoMapUpdated.get(acElement.getId()));
306 private void updateExistingElementsOnThisParticipant(UUID instanceId, ParticipantDeploy participantDeploy,
307 DeployState deployState) {
308 var acElementList = cacheProvider.getAutomationComposition(instanceId).getElements();
309 for (var element : participantDeploy.getAcElementList()) {
310 var acElement = acElementList.get(element.getId());
311 AcmUtils.recursiveMerge(acElement.getProperties(), element.getProperties());
312 acElement.setDeployState(deployState);
313 acElement.setDefinition(element.getDefinition());
318 * Method to handle when the new state from participant is UNINITIALISED state.
320 * @param messageId the messageId
321 * @param automationComposition participant response
322 * @param startPhaseMsg startPhase from message
324 private void handleUndeployState(UUID messageId, final AutomationComposition automationComposition,
325 Integer startPhaseMsg) {
326 automationComposition.setCompositionTargetId(null);
327 for (var element : automationComposition.getElements().values()) {
328 var compositionInProperties = cacheProvider
329 .getCommonProperties(automationComposition.getCompositionId(), element.getDefinition());
330 int startPhase = ParticipantUtils.findStartPhase(compositionInProperties);
331 if (startPhaseMsg.equals(startPhase)) {
332 element.setDeployState(DeployState.UNDEPLOYING);
333 var compositionElement = createCompositionElementDto(automationComposition.getCompositionId(),
334 element, compositionInProperties);
335 var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(),
336 null, element.getProperties(), element.getOutProperties());
337 listener.undeploy(messageId, compositionElement, instanceElement);
342 private void handleDeleteState(UUID messageId, final AutomationComposition automationComposition,
343 Integer startPhaseMsg) {
344 for (var element : automationComposition.getElements().values()) {
345 var compositionInProperties = cacheProvider
346 .getCommonProperties(automationComposition.getCompositionId(), element.getDefinition());
347 int startPhase = ParticipantUtils.findStartPhase(compositionInProperties);
348 if (startPhaseMsg.equals(startPhase)) {
349 element.setDeployState(DeployState.DELETING);
350 var compositionElement = createCompositionElementDto(automationComposition.getCompositionId(),
351 element, compositionInProperties);
352 var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(),
353 null, element.getProperties(), element.getOutProperties());
354 listener.delete(messageId, compositionElement, instanceElement);
360 * Method to handle when the new state from participant is PASSIVE state.
362 * @param messageId the messageId
363 * @param automationComposition participant response
364 * @param startPhaseMsg startPhase from message
366 private void handleLockState(UUID messageId, final AutomationComposition automationComposition,
367 Integer startPhaseMsg) {
368 for (var element : automationComposition.getElements().values()) {
369 var compositionInProperties = cacheProvider
370 .getCommonProperties(automationComposition.getCompositionId(), element.getDefinition());
371 int startPhase = ParticipantUtils.findStartPhase(compositionInProperties);
372 if (startPhaseMsg.equals(startPhase)) {
373 element.setLockState(LockState.LOCKING);
374 var compositionElement = createCompositionElementDto(automationComposition.getCompositionId(),
375 element, compositionInProperties);
376 var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(),
377 null, element.getProperties(), element.getOutProperties());
378 listener.lock(messageId, compositionElement, instanceElement);
384 * Method to handle when the new state from participant is RUNNING state.
386 * @param messageId the messageId
387 * @param automationComposition participant response
388 * @param startPhaseMsg startPhase from message
390 private void handleUnlockState(UUID messageId, final AutomationComposition automationComposition,
391 Integer startPhaseMsg) {
392 for (var element : automationComposition.getElements().values()) {
393 var compositionInProperties = cacheProvider
394 .getCommonProperties(automationComposition.getCompositionId(), element.getDefinition());
395 int startPhase = ParticipantUtils.findStartPhase(compositionInProperties);
396 if (startPhaseMsg.equals(startPhase)) {
397 element.setLockState(LockState.UNLOCKING);
398 var compositionElement = createCompositionElementDto(automationComposition.getCompositionId(),
399 element, compositionInProperties);
400 var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(),
401 null, element.getProperties(), element.getOutProperties());
402 listener.unlock(messageId, compositionElement, instanceElement);
408 * Handles prime a Composition Definition.
410 * @param messageId the messageId
411 * @param compositionId the compositionId
412 * @param list the list of AutomationCompositionElementDefinition
414 public void prime(UUID messageId, UUID compositionId, List<AutomationCompositionElementDefinition> list) {
415 var inPropertiesMap = list.stream().collect(Collectors.toMap(
416 AutomationCompositionElementDefinition::getAcElementDefinitionId,
417 el -> el.getAutomationCompositionElementToscaNodeTemplate().getProperties()));
418 var outPropertiesMap = list.stream().collect(Collectors.toMap(
419 AutomationCompositionElementDefinition::getAcElementDefinitionId,
420 AutomationCompositionElementDefinition::getOutProperties));
421 listener.prime(messageId, new CompositionDto(compositionId, inPropertiesMap, outPropertiesMap));
425 * Handles deprime a Composition Definition.
427 * @param messageId the messageId
428 * @param compositionId the compositionId
430 public void deprime(UUID messageId, UUID compositionId) {
431 var acElementsDefinitions = cacheProvider.getAcElementsDefinitions().get(compositionId);
432 if (acElementsDefinitions == null) {
433 // this participant does not handle this composition
434 var participantPrimeAck = new ParticipantPrimeAck();
435 participantPrimeAck.setCompositionId(compositionId);
436 participantPrimeAck.setMessage("Already deprimed or never primed");
437 participantPrimeAck.setResult(true);
438 participantPrimeAck.setResponseTo(messageId);
439 participantPrimeAck.setCompositionState(AcTypeState.COMMISSIONED);
440 participantPrimeAck.setStateChangeResult(StateChangeResult.NO_ERROR);
441 participantPrimeAck.setParticipantId(cacheProvider.getParticipantId());
442 participantPrimeAck.setState(ParticipantState.ON_LINE);
443 publisher.sendParticipantPrimeAck(participantPrimeAck);
446 var list = new ArrayList<>(acElementsDefinitions.values());
447 var inPropertiesMap = list.stream().collect(Collectors.toMap(
448 AutomationCompositionElementDefinition::getAcElementDefinitionId,
449 el -> el.getAutomationCompositionElementToscaNodeTemplate().getProperties()));
450 var outPropertiesMap = list.stream().collect(Collectors.toMap(
451 AutomationCompositionElementDefinition::getAcElementDefinitionId,
452 AutomationCompositionElementDefinition::getOutProperties));
453 listener.deprime(messageId, new CompositionDto(compositionId, inPropertiesMap, outPropertiesMap));
457 * Handles restarted scenario.
459 * @param messageId the messageId
460 * @param compositionId the compositionId
461 * @param list the list of AutomationCompositionElementDefinition
462 * @param state the state of the composition
463 * @param automationCompositionList list of ParticipantRestartAc
465 public void restarted(UUID messageId, UUID compositionId, List<AutomationCompositionElementDefinition> list,
466 AcTypeState state, List<ParticipantRestartAc> automationCompositionList) {
468 for (var automationcomposition : automationCompositionList) {
469 cacheProvider.initializeAutomationComposition(compositionId, automationcomposition);
471 var inPropertiesMap = list.stream().collect(Collectors.toMap(
472 AutomationCompositionElementDefinition::getAcElementDefinitionId,
473 el -> el.getAutomationCompositionElementToscaNodeTemplate().getProperties()));
474 var outPropertiesMap = list.stream().collect(Collectors.toMap(
475 AutomationCompositionElementDefinition::getAcElementDefinitionId,
476 AutomationCompositionElementDefinition::getOutProperties));
477 var composition = new CompositionDto(compositionId, inPropertiesMap, outPropertiesMap);
478 listener.restarted(messageId, composition, state, automationCompositionList);
482 * Handles AutomationComposition Migration.
484 * @param migrationMsg the AutomationCompositionMigration
486 public void handleAutomationCompositionMigration(AutomationCompositionMigration migrationMsg) {
487 if (migrationMsg.getAutomationCompositionId() == null || migrationMsg.getCompositionTargetId() == null) {
491 var automationComposition = cacheProvider.getAutomationComposition(migrationMsg.getAutomationCompositionId());
492 if (automationComposition == null) {
493 LOGGER.debug("Automation composition {} does not use this participant",
494 migrationMsg.getAutomationCompositionId());
497 var acCopy = new AutomationComposition(automationComposition);
498 automationComposition.setCompositionTargetId(migrationMsg.getCompositionTargetId());
499 for (var participantDeploy : migrationMsg.getParticipantUpdatesList()) {
500 if (cacheProvider.getParticipantId().equals(participantDeploy.getParticipantId())) {
502 updateExistingElementsOnThisParticipant(migrationMsg.getAutomationCompositionId(), participantDeploy,
503 DeployState.MIGRATING);
505 callParticipantMigrate(migrationMsg.getMessageId(), participantDeploy.getAcElementList(),
506 acCopy, migrationMsg.getCompositionTargetId());
511 private void callParticipantMigrate(UUID messageId, List<AcElementDeploy> acElements,
512 AutomationComposition acCopy, UUID compositionTargetId) {
513 var compositionElementMap = getCompositionElementDtoMap(acCopy);
514 var instanceElementMap = getInstanceElementDtoMap(acCopy);
515 var automationComposition = cacheProvider.getAutomationComposition(acCopy.getInstanceId());
516 var compositionElementTargetMap = getCompositionElementDtoMap(automationComposition, compositionTargetId);
517 var instanceElementMigrateMap = getInstanceElementDtoMap(automationComposition);
519 for (var acElement : acElements) {
520 listener.migrate(messageId, compositionElementMap.get(acElement.getId()),
521 compositionElementTargetMap.get(acElement.getId()),
522 instanceElementMap.get(acElement.getId()), instanceElementMigrateMap.get(acElement.getId()));