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.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
58 import org.springframework.stereotype.Component;
61 * This class is responsible for managing the state of all automation compositions in the participant.
64 public class AutomationCompositionHandler {
65 private static final Logger LOGGER = LoggerFactory.getLogger(AutomationCompositionHandler.class);
67 private final CacheProvider cacheProvider;
68 private final ParticipantMessagePublisher publisher;
69 private final ThreadHandler listener;
70 private final AcInstanceStateResolver acInstanceStateResolver;
73 * Constructor, set the participant ID and messageSender.
75 * @param cacheProvider the Cache Provider
76 * @param publisher the ParticipantMessage Publisher
77 * @param listener the ThreadHandler Listener
79 public AutomationCompositionHandler(CacheProvider cacheProvider, ParticipantMessagePublisher publisher,
80 ThreadHandler listener) {
81 this.cacheProvider = cacheProvider;
82 this.publisher = publisher;
83 this.listener = listener;
84 this.acInstanceStateResolver = new AcInstanceStateResolver();
88 * Handle a automation composition state change message.
90 * @param stateChangeMsg the state change message
92 public void handleAutomationCompositionStateChange(AutomationCompositionStateChange stateChangeMsg) {
93 if (stateChangeMsg.getAutomationCompositionId() == null) {
97 var automationComposition = cacheProvider.getAutomationComposition(stateChangeMsg.getAutomationCompositionId());
99 if (automationComposition == null) {
100 if (DeployOrder.DELETE.equals(stateChangeMsg.getDeployOrderedState())) {
101 var automationCompositionAck = new AutomationCompositionDeployAck(
102 ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
103 automationCompositionAck.setParticipantId(cacheProvider.getParticipantId());
104 automationCompositionAck.setMessage("Already deleted or never used");
105 automationCompositionAck.setResult(true);
106 automationCompositionAck.setStateChangeResult(StateChangeResult.NO_ERROR);
107 automationCompositionAck.setResponseTo(stateChangeMsg.getMessageId());
108 automationCompositionAck.setAutomationCompositionId(stateChangeMsg.getAutomationCompositionId());
109 publisher.sendAutomationCompositionAck(automationCompositionAck);
111 LOGGER.debug("Automation composition {} does not use this participant",
112 stateChangeMsg.getAutomationCompositionId());
117 if (!checkConsistantOrderState(automationComposition, stateChangeMsg.getDeployOrderedState(),
118 stateChangeMsg.getLockOrderedState())) {
119 LOGGER.warn("Not Consistant OrderState Automation composition {}",
120 stateChangeMsg.getAutomationCompositionId());
124 if (DeployOrder.NONE.equals(stateChangeMsg.getDeployOrderedState())) {
125 handleLockOrderState(stateChangeMsg.getMessageId(), automationComposition,
126 stateChangeMsg.getLockOrderedState(), stateChangeMsg.getStartPhase());
128 handleDeployOrderState(stateChangeMsg.getMessageId(), automationComposition,
129 stateChangeMsg.getDeployOrderedState(), stateChangeMsg.getStartPhase());
133 private boolean checkConsistantOrderState(AutomationComposition automationComposition, DeployOrder deployOrder,
134 LockOrder lockOrder) {
135 if (DeployOrder.UPDATE.equals(deployOrder)) {
138 return acInstanceStateResolver.resolve(deployOrder, lockOrder, automationComposition.getDeployState(),
139 automationComposition.getLockState(), automationComposition.getStateChangeResult()) != null;
143 * Method to handle state changes.
145 * @param messageId the messageId
146 * @param automationComposition participant response
147 * @param orderedState automation composition ordered state
148 * @param startPhaseMsg startPhase from message
150 private void handleDeployOrderState(UUID messageId, final AutomationComposition automationComposition,
151 DeployOrder orderedState, Integer startPhaseMsg) {
153 switch (orderedState) {
155 handleUndeployState(messageId, automationComposition, startPhaseMsg);
158 handleDeleteState(messageId, automationComposition, startPhaseMsg);
162 LOGGER.error("StateChange message has no state, state is null {}", automationComposition.getKey());
168 * Method to handle state changes.
170 * @param messageId the messageId
171 * @param automationComposition participant response
172 * @param orderedState automation composition ordered state
173 * @param startPhaseMsg startPhase from message
175 private void handleLockOrderState(UUID messageId, final AutomationComposition automationComposition,
176 LockOrder orderedState, Integer startPhaseMsg) {
178 switch (orderedState) {
180 handleLockState(messageId, automationComposition, startPhaseMsg);
183 handleUnlockState(messageId, automationComposition, startPhaseMsg);
186 LOGGER.error("StateChange message has no state, state is null {}", automationComposition.getKey());
192 * Handle a automation composition properties update message.
194 * @param updateMsg the properties update message
196 public void handleAcPropertyUpdate(PropertiesUpdate updateMsg) {
198 if (updateMsg.getParticipantUpdatesList().isEmpty()) {
199 LOGGER.warn("No AutomationCompositionElement updates in message {}",
200 updateMsg.getAutomationCompositionId());
204 for (var participantDeploy : updateMsg.getParticipantUpdatesList()) {
205 if (cacheProvider.getParticipantId().equals(participantDeploy.getParticipantId())) {
207 var acCopy = new AutomationComposition(cacheProvider.getAutomationComposition(
208 updateMsg.getAutomationCompositionId()));
209 updateExistingElementsOnThisParticipant(updateMsg.getAutomationCompositionId(), participantDeploy,
210 DeployState.UPDATING);
212 callParticipantUpdateProperty(updateMsg.getMessageId(), participantDeploy.getAcElementList(), acCopy);
218 * Handle a automation composition Deploy message.
220 * @param deployMsg the Deploy message
222 public void handleAutomationCompositionDeploy(AutomationCompositionDeploy deployMsg) {
224 if (deployMsg.getParticipantUpdatesList().isEmpty()) {
225 LOGGER.warn("No AutomationCompositionElement deploy in message {}", deployMsg.getAutomationCompositionId());
229 for (var participantDeploy : deployMsg.getParticipantUpdatesList()) {
230 if (cacheProvider.getParticipantId().equals(participantDeploy.getParticipantId())) {
231 if (deployMsg.isFirstStartPhase()) {
232 cacheProvider.initializeAutomationComposition(deployMsg.getCompositionId(),
233 deployMsg.getAutomationCompositionId(), participantDeploy);
235 callParticipanDeploy(deployMsg.getMessageId(), participantDeploy.getAcElementList(),
236 deployMsg.getStartPhase(), deployMsg.getAutomationCompositionId());
241 private void callParticipanDeploy(UUID messageId, List<AcElementDeploy> acElementDeployList,
242 Integer startPhaseMsg, UUID instanceId) {
243 var automationComposition = cacheProvider.getAutomationComposition(instanceId);
244 for (var elementDeploy : acElementDeployList) {
245 var element = automationComposition.getElements().get(elementDeploy.getId());
246 var compositionInProperties = cacheProvider
247 .getCommonProperties(automationComposition.getCompositionId(), element.getDefinition());
248 int startPhase = ParticipantUtils.findStartPhase(compositionInProperties);
249 if (startPhaseMsg.equals(startPhase)) {
250 var compositionElement = createCompositionElementDto(automationComposition.getCompositionId(),
251 element, compositionInProperties);
252 var instanceElement = new InstanceElementDto(instanceId, elementDeploy.getId(),
253 elementDeploy.getToscaServiceTemplateFragment(),
254 elementDeploy.getProperties(), element.getOutProperties());
255 listener.deploy(messageId, compositionElement, instanceElement);
260 private CompositionElementDto createCompositionElementDto(UUID compositionId, AutomationCompositionElement element,
261 Map<String, Object> compositionInProperties) {
262 var compositionOutProperties = cacheProvider.getAcElementsDefinitions()
263 .get(compositionId).get(element.getDefinition()).getOutProperties();
264 return new CompositionElementDto(compositionId,
265 element.getDefinition(), compositionInProperties, compositionOutProperties);
268 private Map<UUID, CompositionElementDto> getCompositionElementDtoMap(AutomationComposition automationComposition,
269 UUID compositionId) {
270 Map<UUID, CompositionElementDto> map = new HashMap<>();
271 for (var element : automationComposition.getElements().values()) {
272 var compositionInProperties = cacheProvider.getCommonProperties(compositionId, element.getDefinition());
273 var compositionElement = createCompositionElementDto(compositionId, element, compositionInProperties);
274 map.put(element.getId(), compositionElement);
279 private Map<UUID, CompositionElementDto> getCompositionElementDtoMap(AutomationComposition automationComposition) {
280 return getCompositionElementDtoMap(automationComposition, automationComposition.getCompositionId());
283 private Map<UUID, InstanceElementDto> getInstanceElementDtoMap(AutomationComposition automationComposition) {
284 Map<UUID, InstanceElementDto> map = new HashMap<>();
285 for (var element : automationComposition.getElements().values()) {
286 var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(),
287 null, element.getProperties(), element.getOutProperties());
288 map.put(element.getId(), instanceElement);
293 private void callParticipantUpdateProperty(UUID messageId, List<AcElementDeploy> acElements,
294 AutomationComposition acCopy) {
295 var instanceElementDtoMap = getInstanceElementDtoMap(acCopy);
296 var instanceElementDtoMapUpdated = getInstanceElementDtoMap(
297 cacheProvider.getAutomationComposition(acCopy.getInstanceId()));
298 var compositionElementDtoMap = getCompositionElementDtoMap(acCopy);
299 for (var acElement : acElements) {
300 listener.update(messageId, compositionElementDtoMap.get(acElement.getId()),
301 instanceElementDtoMap.get(acElement.getId()), instanceElementDtoMapUpdated.get(acElement.getId()));
305 private void updateExistingElementsOnThisParticipant(UUID instanceId, ParticipantDeploy participantDeploy,
306 DeployState deployState) {
307 var acElementList = cacheProvider.getAutomationComposition(instanceId).getElements();
308 for (var element : participantDeploy.getAcElementList()) {
309 var acElement = acElementList.get(element.getId());
310 acElement.getProperties().putAll(element.getProperties());
311 acElement.setDeployState(deployState);
312 acElement.setDefinition(element.getDefinition());
317 * Method to handle when the new state from participant is UNINITIALISED state.
319 * @param messageId the messageId
320 * @param automationComposition participant response
321 * @param startPhaseMsg startPhase from message
323 private void handleUndeployState(UUID messageId, final AutomationComposition automationComposition,
324 Integer startPhaseMsg) {
325 automationComposition.setCompositionTargetId(null);
326 for (var element : automationComposition.getElements().values()) {
327 var compositionInProperties = cacheProvider
328 .getCommonProperties(automationComposition.getCompositionId(), element.getDefinition());
329 int startPhase = ParticipantUtils.findStartPhase(compositionInProperties);
330 if (startPhaseMsg.equals(startPhase)) {
331 element.setDeployState(DeployState.UNDEPLOYING);
332 var compositionElement = createCompositionElementDto(automationComposition.getCompositionId(),
333 element, compositionInProperties);
334 var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(),
335 null, element.getProperties(), element.getOutProperties());
336 listener.undeploy(messageId, compositionElement, instanceElement);
341 private void handleDeleteState(UUID messageId, final AutomationComposition automationComposition,
342 Integer startPhaseMsg) {
343 for (var element : automationComposition.getElements().values()) {
344 var compositionInProperties = cacheProvider
345 .getCommonProperties(automationComposition.getCompositionId(), element.getDefinition());
346 int startPhase = ParticipantUtils.findStartPhase(compositionInProperties);
347 if (startPhaseMsg.equals(startPhase)) {
348 element.setDeployState(DeployState.DELETING);
349 var compositionElement = createCompositionElementDto(automationComposition.getCompositionId(),
350 element, compositionInProperties);
351 var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(),
352 null, element.getProperties(), element.getOutProperties());
353 listener.delete(messageId, compositionElement, instanceElement);
359 * Method to handle when the new state from participant is PASSIVE state.
361 * @param messageId the messageId
362 * @param automationComposition participant response
363 * @param startPhaseMsg startPhase from message
365 private void handleLockState(UUID messageId, final AutomationComposition automationComposition,
366 Integer startPhaseMsg) {
367 for (var element : automationComposition.getElements().values()) {
368 var compositionInProperties = cacheProvider
369 .getCommonProperties(automationComposition.getCompositionId(), element.getDefinition());
370 int startPhase = ParticipantUtils.findStartPhase(compositionInProperties);
371 if (startPhaseMsg.equals(startPhase)) {
372 element.setLockState(LockState.LOCKING);
373 var compositionElement = createCompositionElementDto(automationComposition.getCompositionId(),
374 element, compositionInProperties);
375 var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(),
376 null, element.getProperties(), element.getOutProperties());
377 listener.lock(messageId, compositionElement, instanceElement);
383 * Method to handle when the new state from participant is RUNNING state.
385 * @param messageId the messageId
386 * @param automationComposition participant response
387 * @param startPhaseMsg startPhase from message
389 private void handleUnlockState(UUID messageId, final AutomationComposition automationComposition,
390 Integer startPhaseMsg) {
391 for (var element : automationComposition.getElements().values()) {
392 var compositionInProperties = cacheProvider
393 .getCommonProperties(automationComposition.getCompositionId(), element.getDefinition());
394 int startPhase = ParticipantUtils.findStartPhase(compositionInProperties);
395 if (startPhaseMsg.equals(startPhase)) {
396 element.setLockState(LockState.UNLOCKING);
397 var compositionElement = createCompositionElementDto(automationComposition.getCompositionId(),
398 element, compositionInProperties);
399 var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(),
400 null, element.getProperties(), element.getOutProperties());
401 listener.unlock(messageId, compositionElement, instanceElement);
407 * Handles prime a Composition Definition.
409 * @param messageId the messageId
410 * @param compositionId the compositionId
411 * @param list the list of AutomationCompositionElementDefinition
413 public void prime(UUID messageId, UUID compositionId, List<AutomationCompositionElementDefinition> list) {
414 var inPropertiesMap = list.stream().collect(Collectors.toMap(
415 AutomationCompositionElementDefinition::getAcElementDefinitionId,
416 el -> el.getAutomationCompositionElementToscaNodeTemplate().getProperties()));
417 var outPropertiesMap = list.stream().collect(Collectors.toMap(
418 AutomationCompositionElementDefinition::getAcElementDefinitionId,
419 AutomationCompositionElementDefinition::getOutProperties));
420 listener.prime(messageId, new CompositionDto(compositionId, inPropertiesMap, outPropertiesMap));
424 * Handles deprime a Composition Definition.
426 * @param messageId the messageId
427 * @param compositionId the compositionId
429 public void deprime(UUID messageId, UUID compositionId) {
430 var acElementsDefinitions = cacheProvider.getAcElementsDefinitions().get(compositionId);
431 if (acElementsDefinitions == null) {
432 // this participant does not handle this composition
433 var participantPrimeAck = new ParticipantPrimeAck();
434 participantPrimeAck.setCompositionId(compositionId);
435 participantPrimeAck.setMessage("Already deprimed or never primed");
436 participantPrimeAck.setResult(true);
437 participantPrimeAck.setResponseTo(messageId);
438 participantPrimeAck.setCompositionState(AcTypeState.COMMISSIONED);
439 participantPrimeAck.setStateChangeResult(StateChangeResult.NO_ERROR);
440 participantPrimeAck.setParticipantId(cacheProvider.getParticipantId());
441 participantPrimeAck.setState(ParticipantState.ON_LINE);
442 publisher.sendParticipantPrimeAck(participantPrimeAck);
445 var list = new ArrayList<>(acElementsDefinitions.values());
446 var inPropertiesMap = list.stream().collect(Collectors.toMap(
447 AutomationCompositionElementDefinition::getAcElementDefinitionId,
448 el -> el.getAutomationCompositionElementToscaNodeTemplate().getProperties()));
449 var outPropertiesMap = list.stream().collect(Collectors.toMap(
450 AutomationCompositionElementDefinition::getAcElementDefinitionId,
451 AutomationCompositionElementDefinition::getOutProperties));
452 listener.deprime(messageId, new CompositionDto(compositionId, inPropertiesMap, outPropertiesMap));
456 * Handles restarted scenario.
458 * @param messageId the messageId
459 * @param compositionId the compositionId
460 * @param list the list of AutomationCompositionElementDefinition
461 * @param state the state of the composition
462 * @param automationCompositionList list of ParticipantRestartAc
464 public void restarted(UUID messageId, UUID compositionId, List<AutomationCompositionElementDefinition> list,
465 AcTypeState state, List<ParticipantRestartAc> automationCompositionList) {
467 for (var automationcomposition : automationCompositionList) {
468 cacheProvider.initializeAutomationComposition(compositionId, automationcomposition);
470 var inPropertiesMap = list.stream().collect(Collectors.toMap(
471 AutomationCompositionElementDefinition::getAcElementDefinitionId,
472 el -> el.getAutomationCompositionElementToscaNodeTemplate().getProperties()));
473 var outPropertiesMap = list.stream().collect(Collectors.toMap(
474 AutomationCompositionElementDefinition::getAcElementDefinitionId,
475 AutomationCompositionElementDefinition::getOutProperties));
476 var composition = new CompositionDto(compositionId, inPropertiesMap, outPropertiesMap);
477 listener.restarted(messageId, composition, state, automationCompositionList);
481 * Handles AutomationComposition Migration.
483 * @param migrationMsg the AutomationCompositionMigration
485 public void handleAutomationCompositionMigration(AutomationCompositionMigration migrationMsg) {
486 if (migrationMsg.getAutomationCompositionId() == null || migrationMsg.getCompositionTargetId() == null) {
490 var automationComposition = cacheProvider.getAutomationComposition(migrationMsg.getAutomationCompositionId());
491 if (automationComposition == null) {
492 LOGGER.debug("Automation composition {} does not use this participant",
493 migrationMsg.getAutomationCompositionId());
496 var acCopy = new AutomationComposition(automationComposition);
497 automationComposition.setCompositionTargetId(migrationMsg.getCompositionTargetId());
498 for (var participantDeploy : migrationMsg.getParticipantUpdatesList()) {
499 if (cacheProvider.getParticipantId().equals(participantDeploy.getParticipantId())) {
501 updateExistingElementsOnThisParticipant(migrationMsg.getAutomationCompositionId(), participantDeploy,
502 DeployState.MIGRATING);
504 callParticipantMigrate(migrationMsg.getMessageId(), participantDeploy.getAcElementList(),
505 acCopy, migrationMsg.getCompositionTargetId());
510 private void callParticipantMigrate(UUID messageId, List<AcElementDeploy> acElements,
511 AutomationComposition acCopy, UUID compositionTargetId) {
512 var compositionElementMap = getCompositionElementDtoMap(acCopy);
513 var instanceElementMap = getInstanceElementDtoMap(acCopy);
514 var automationComposition = cacheProvider.getAutomationComposition(acCopy.getInstanceId());
515 var compositionElementTargetMap = getCompositionElementDtoMap(automationComposition, compositionTargetId);
516 var instanceElementMigrateMap = getInstanceElementDtoMap(automationComposition);
518 for (var acElement : acElements) {
519 listener.migrate(messageId, compositionElementMap.get(acElement.getId()),
520 compositionElementTargetMap.get(acElement.getId()),
521 instanceElementMap.get(acElement.getId()), instanceElementMigrateMap.get(acElement.getId()));