2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2021-2025 OpenInfra Foundation Europe. All rights reserved.
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.List;
27 import java.util.UUID;
28 import lombok.RequiredArgsConstructor;
29 import org.onap.policy.clamp.acm.participant.intermediary.api.CompositionElementDto;
30 import org.onap.policy.clamp.acm.participant.intermediary.api.ElementState;
31 import org.onap.policy.clamp.acm.participant.intermediary.api.InstanceElementDto;
32 import org.onap.policy.clamp.acm.participant.intermediary.comm.ParticipantMessagePublisher;
33 import org.onap.policy.clamp.acm.participant.intermediary.handler.cache.CacheProvider;
34 import org.onap.policy.clamp.models.acm.concepts.AcElementDeploy;
35 import org.onap.policy.clamp.models.acm.concepts.AutomationComposition;
36 import org.onap.policy.clamp.models.acm.concepts.DeployState;
37 import org.onap.policy.clamp.models.acm.concepts.LockState;
38 import org.onap.policy.clamp.models.acm.concepts.MigrationState;
39 import org.onap.policy.clamp.models.acm.concepts.ParticipantDeploy;
40 import org.onap.policy.clamp.models.acm.concepts.StateChangeResult;
41 import org.onap.policy.clamp.models.acm.concepts.SubState;
42 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionDeploy;
43 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionDeployAck;
44 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionMigration;
45 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionStateChange;
46 import org.onap.policy.clamp.models.acm.messages.kafka.participant.ParticipantMessageType;
47 import org.onap.policy.clamp.models.acm.messages.kafka.participant.PropertiesUpdate;
48 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.DeployOrder;
49 import org.onap.policy.clamp.models.acm.utils.AcmStageUtils;
50 import org.onap.policy.clamp.models.acm.utils.AcmUtils;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
53 import org.springframework.stereotype.Component;
56 * This class is responsible for managing the state of all automation compositions in the participant.
59 @RequiredArgsConstructor
60 public class AutomationCompositionHandler {
61 private static final Logger LOGGER = LoggerFactory.getLogger(AutomationCompositionHandler.class);
62 private static final String AC_NOT_USED = "Automation composition {} does not use this participant";
64 private final CacheProvider cacheProvider;
65 private final ParticipantMessagePublisher publisher;
66 private final ThreadHandler listener;
69 * Handle a automation composition state change message.
71 * @param stateChangeMsg the state change message
73 public void handleAutomationCompositionStateChange(AutomationCompositionStateChange stateChangeMsg) {
74 var automationComposition = cacheProvider.getAutomationComposition(stateChangeMsg.getAutomationCompositionId());
76 if (automationComposition == null) {
77 if (DeployOrder.DELETE.equals(stateChangeMsg.getDeployOrderedState())) {
78 var automationCompositionAck = new AutomationCompositionDeployAck(
79 ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
80 automationCompositionAck.setParticipantId(cacheProvider.getParticipantId());
81 automationCompositionAck.setReplicaId(cacheProvider.getReplicaId());
82 automationCompositionAck.setMessage("Already deleted or never used");
83 automationCompositionAck.setResult(true);
84 automationCompositionAck.setStateChangeResult(StateChangeResult.NO_ERROR);
85 automationCompositionAck.setResponseTo(stateChangeMsg.getMessageId());
86 automationCompositionAck.setAutomationCompositionId(stateChangeMsg.getAutomationCompositionId());
87 publisher.sendAutomationCompositionAck(automationCompositionAck);
89 LOGGER.warn(AC_NOT_USED, stateChangeMsg.getAutomationCompositionId());
94 switch (stateChangeMsg.getDeployOrderedState()) {
95 case UNDEPLOY -> handleUndeployState(stateChangeMsg.getMessageId(), automationComposition,
96 stateChangeMsg.getStartPhase());
97 case DELETE -> handleDeleteState(stateChangeMsg.getMessageId(), automationComposition,
98 stateChangeMsg.getStartPhase());
100 LOGGER.error("StateChange message has no state, state is null {}", automationComposition.getKey());
105 * Handle a automation composition properties update message.
107 * @param updateMsg the properties update message
109 public void handleAcPropertyUpdate(PropertiesUpdate updateMsg) {
111 if (updateMsg.getParticipantUpdatesList().isEmpty()) {
112 LOGGER.warn("No AutomationCompositionElement updates in message {}",
113 updateMsg.getAutomationCompositionId());
117 for (var participantDeploy : updateMsg.getParticipantUpdatesList()) {
118 if (cacheProvider.getParticipantId().equals(participantDeploy.getParticipantId())) {
119 var automationComposition =
120 cacheProvider.getAutomationComposition(updateMsg.getAutomationCompositionId());
121 automationComposition.setDeployState(DeployState.UPDATING);
122 var acCopy = new AutomationComposition(automationComposition);
123 updateExistingElementsOnThisParticipant(updateMsg.getAutomationCompositionId(), participantDeploy);
125 callParticipantUpdateProperty(updateMsg.getMessageId(), participantDeploy.getAcElementList(), acCopy);
131 * Handle a automation composition Deploy message.
133 * @param deployMsg the Deploy message
135 public void handleAutomationCompositionDeploy(AutomationCompositionDeploy deployMsg) {
137 if (deployMsg.getParticipantUpdatesList().isEmpty()) {
138 LOGGER.warn("No AutomationCompositionElement deploy in message {}", deployMsg.getAutomationCompositionId());
142 for (var participantDeploy : deployMsg.getParticipantUpdatesList()) {
143 if (cacheProvider.getParticipantId().equals(participantDeploy.getParticipantId())) {
144 if (deployMsg.isFirstStartPhase()) {
145 cacheProvider.initializeAutomationComposition(deployMsg.getCompositionId(),
146 deployMsg.getAutomationCompositionId(), participantDeploy,
147 deployMsg.getRevisionIdInstance());
149 callParticipantDeploy(deployMsg.getMessageId(), participantDeploy.getAcElementList(),
150 deployMsg.getStartPhase(), deployMsg.getAutomationCompositionId());
155 private void callParticipantDeploy(UUID messageId, List<AcElementDeploy> acElementDeployList, Integer startPhaseMsg,
157 var automationComposition = cacheProvider.getAutomationComposition(instanceId);
158 automationComposition.setDeployState(DeployState.DEPLOYING);
159 for (var elementDeploy : acElementDeployList) {
160 var element = automationComposition.getElements().get(elementDeploy.getId());
161 var compositionInProperties = cacheProvider.getCommonProperties(automationComposition.getCompositionId(),
162 element.getDefinition());
163 int startPhase = AcmStageUtils.findStartPhase(compositionInProperties);
164 if (startPhaseMsg.equals(startPhase)) {
165 var compositionElement =
166 cacheProvider.createCompositionElementDto(automationComposition.getCompositionId(), element);
167 var instanceElement =
168 new InstanceElementDto(instanceId, elementDeploy.getId(), elementDeploy.getProperties(),
169 element.getOutProperties());
170 listener.deploy(messageId, compositionElement, instanceElement);
175 private void callParticipantUpdateProperty(UUID messageId, List<AcElementDeploy> acElements,
176 AutomationComposition acCopy) {
177 var instanceElementDtoMap = cacheProvider.getInstanceElementDtoMap(acCopy);
178 var instanceElementDtoMapUpdated =
179 cacheProvider.getInstanceElementDtoMap(cacheProvider.getAutomationComposition(acCopy.getInstanceId()));
180 var compositionElementDtoMap = cacheProvider.getCompositionElementDtoMap(acCopy);
181 for (var acElement : acElements) {
182 listener.update(messageId, compositionElementDtoMap.get(acElement.getId()),
183 instanceElementDtoMap.get(acElement.getId()), instanceElementDtoMapUpdated.get(acElement.getId()));
187 private void migrateAutomationComposition(AutomationComposition automationComposition,
188 ParticipantDeploy participantDeploy, int stage) {
189 for (var element : participantDeploy.getAcElementList()) {
190 var stageSet = getMigrationStageSet(element, automationComposition.getCompositionTargetId());
191 if (stageSet.contains(stage)) {
192 migrateElement(element, automationComposition, stage, participantDeploy);
197 private void rollbackAutomationComposition(AutomationComposition automationComposition,
198 ParticipantDeploy participantDeploy, int stage) {
199 for (var element : participantDeploy.getAcElementList()) {
200 var stageSet = getRollbackStageSet(element, automationComposition.getCompositionId());
201 if (stageSet.contains(stage)) {
202 migrateElement(element, automationComposition, stage, participantDeploy);
207 private void migrateElement(AcElementDeploy element, AutomationComposition automationComposition,
208 int stage, ParticipantDeploy participantDeploy) {
209 var acElementList = automationComposition.getElements();
210 var acElement = acElementList.get(element.getId());
211 if (acElement == null) { // NEW element with existing participant
212 var newElement = CacheProvider.createAutomationCompositionElement(element);
213 newElement.setParticipantId(participantDeploy.getParticipantId());
214 newElement.setDeployState(automationComposition.getDeployState());
215 newElement.setLockState(LockState.LOCKED);
216 newElement.setStage(stage);
217 newElement.setMigrationState(MigrationState.NEW);
219 acElementList.put(element.getId(), newElement);
220 LOGGER.info("New Ac Element with id {} is added in Migration", element.getId());
222 acElement.setStage(stage);
223 acElement.setMigrationState(element.getMigrationState());
224 acElement.setDeployState(automationComposition.getDeployState());
225 acElement.setDefinition(element.getDefinition());
226 if (MigrationState.DEFAULT.equals(element.getMigrationState())) { //DEFAULT element
227 AcmUtils.recursiveMerge(acElement.getProperties(), element.getProperties());
229 LOGGER.info("Cache updated for the migration of element with id {}", element.getId());
233 private void updateExistingElementsOnThisParticipant(UUID instanceId, ParticipantDeploy participantDeploy) {
234 var acElementList = cacheProvider.getAutomationComposition(instanceId).getElements();
235 for (var element : participantDeploy.getAcElementList()) {
236 var acElement = acElementList.get(element.getId());
237 AcmUtils.recursiveMerge(acElement.getProperties(), element.getProperties());
238 acElement.setDeployState(DeployState.UPDATING);
239 acElement.setSubState(SubState.NONE);
240 acElement.setDefinition(element.getDefinition());
245 * Method to handle when the new state from participant is UNINITIALISED state.
247 * @param messageId the messageId
248 * @param automationComposition participant response
249 * @param startPhaseMsg startPhase from message
251 private void handleUndeployState(UUID messageId, final AutomationComposition automationComposition,
252 Integer startPhaseMsg) {
253 automationComposition.setDeployState(DeployState.UNDEPLOYING);
254 for (var element : automationComposition.getElements().values()) {
255 var compositionId = MigrationState.NEW.equals(element.getMigrationState())
256 ? automationComposition.getCompositionTargetId() : automationComposition.getCompositionId();
257 var compositionInProperties = cacheProvider.getCommonProperties(compositionId, element.getDefinition());
258 var startPhase = AcmStageUtils.findStartPhase(compositionInProperties);
259 if (MigrationState.NEW.equals(element.getMigrationState())) {
260 // Undeploy newly added element on a Failed Migration
263 if (startPhaseMsg.equals(startPhase)) {
264 element.setDeployState(DeployState.UNDEPLOYING);
265 var compositionElement =
266 cacheProvider.createCompositionElementDto(compositionId, element);
267 var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(),
268 element.getProperties(), element.getOutProperties());
269 listener.undeploy(messageId, compositionElement, instanceElement);
274 private void handleDeleteState(UUID messageId, final AutomationComposition automationComposition,
275 Integer startPhaseMsg) {
276 automationComposition.setDeployState(DeployState.DELETING);
277 for (var element : automationComposition.getElements().values()) {
278 var compositionInProperties = cacheProvider.getCommonProperties(automationComposition.getCompositionId(),
279 element.getDefinition());
280 int startPhase = AcmStageUtils.findStartPhase(compositionInProperties);
281 if (startPhaseMsg.equals(startPhase)) {
282 element.setDeployState(DeployState.DELETING);
283 element.setSubState(SubState.NONE);
284 var compositionElement =
285 cacheProvider.createCompositionElementDto(automationComposition.getCompositionId(), element);
286 var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(),
287 element.getProperties(), element.getOutProperties());
288 listener.delete(messageId, compositionElement, instanceElement);
294 * Handles AutomationComposition Migration.
296 * @param migrationMsg the AutomationCompositionMigration
298 public void handleAutomationCompositionMigration(AutomationCompositionMigration migrationMsg) {
299 var automationComposition = cacheProvider.getAutomationComposition(migrationMsg.getAutomationCompositionId());
300 if (Boolean.FALSE.equals(migrationMsg.getRollback())) {
301 handleMigration(automationComposition, migrationMsg);
303 handleRollback(automationComposition, migrationMsg);
307 private void handleRollback(AutomationComposition automationComposition,
308 AutomationCompositionMigration migrationMsg) {
309 LOGGER.info("Rollback operation invoked for the instance {}", migrationMsg.getAutomationCompositionId());
310 automationComposition.setCompositionTargetId(migrationMsg.getCompositionTargetId());
311 automationComposition.setDeployState(DeployState.MIGRATION_REVERTING);
312 var automationCompositionCopy = new AutomationComposition(automationComposition);
313 for (var participantDeploy : migrationMsg.getParticipantUpdatesList()) {
314 if (cacheProvider.getParticipantId().equals(participantDeploy.getParticipantId())) {
315 rollbackAutomationComposition(automationComposition, participantDeploy, migrationMsg.getStage());
316 callParticipantRollback(migrationMsg, participantDeploy.getAcElementList(), automationCompositionCopy);
322 private void handleMigration(AutomationComposition automationComposition,
323 AutomationCompositionMigration migrationMsg) {
324 LOGGER.info("Migration invoked for the instance {}", migrationMsg.getAutomationCompositionId());
325 automationComposition.setCompositionTargetId(migrationMsg.getCompositionTargetId());
326 automationComposition.setDeployState(DeployState.MIGRATING);
327 var automationCompositionCopy = new AutomationComposition(automationComposition);
328 for (var participantDeploy : migrationMsg.getParticipantUpdatesList()) {
329 if (cacheProvider.getParticipantId().equals(participantDeploy.getParticipantId())) {
330 migrateAutomationComposition(automationComposition, participantDeploy, migrationMsg.getStage());
331 callParticipantMigrate(migrationMsg, participantDeploy.getAcElementList(), automationCompositionCopy);
336 private void callParticipantMigrate(AutomationCompositionMigration migrationMsg, List<AcElementDeploy> acElements,
337 AutomationComposition automationCompositionCopy) {
338 var automationComposition = cacheProvider.getAutomationComposition(automationCompositionCopy.getInstanceId());
339 var instanceElementTargetMap = cacheProvider.getInstanceElementDtoMap(automationComposition);
340 var compositionElementTargetMap = cacheProvider.getCompositionElementDtoMap(automationComposition,
341 automationCompositionCopy.getCompositionTargetId());
342 var compositionElementMap = cacheProvider.getCompositionElementDtoMap(automationCompositionCopy);
343 var instanceElementMap = cacheProvider.getInstanceElementDtoMap(automationCompositionCopy);
344 for (var acElement : acElements) {
345 var stageSet = getMigrationStageSet(acElement, automationComposition.getCompositionTargetId());
346 var newElement = MigrationState.NEW.equals(acElement.getMigrationState());
347 if (stageSet.contains(migrationMsg.getStage())) {
349 var compositionElementDto = new CompositionElementDto(automationCompositionCopy.getCompositionId(),
350 acElement.getDefinition(), Map.of(), Map.of(), ElementState.NOT_PRESENT);
351 var instanceElementDto = new InstanceElementDto(automationCompositionCopy.getInstanceId(),
352 acElement.getId(), Map.of(), Map.of(), ElementState.NOT_PRESENT);
353 var compositionElementTargetDto =
354 CacheProvider.changeStateToNew(compositionElementTargetMap.get(acElement.getId()));
355 var instanceElementTargetDto =
356 CacheProvider.changeStateToNew(instanceElementTargetMap.get(acElement.getId()));
358 listenerMigrate(migrationMsg.getMessageId(), compositionElementDto, compositionElementTargetDto,
359 instanceElementDto, instanceElementTargetDto, migrationMsg.getStage());
361 } else if (MigrationState.REMOVED.equals(acElement.getMigrationState())) {
362 var compositionDtoTarget = new CompositionElementDto(migrationMsg.getCompositionTargetId(),
363 acElement.getDefinition(), Map.of(), Map.of(), ElementState.REMOVED);
364 var instanceElementDtoTarget = new InstanceElementDto(migrationMsg.getAutomationCompositionId(),
365 acElement.getId(), Map.of(), Map.of(), ElementState.REMOVED);
366 listenerMigrate(migrationMsg.getMessageId(), compositionElementMap.get(acElement.getId()),
367 compositionDtoTarget, instanceElementMap.get(acElement.getId()), instanceElementDtoTarget,
368 migrationMsg.getStage());
370 } else { // DEFAULT case
371 listenerMigrate(migrationMsg.getMessageId(), compositionElementMap.get(acElement.getId()),
372 compositionElementTargetMap.get(acElement.getId()),
373 instanceElementMap.get(acElement.getId()), instanceElementTargetMap.get(acElement.getId()),
374 migrationMsg.getStage());
380 private Set<Integer> getMigrationStageSet(AcElementDeploy acElement, UUID compositionTargetId) {
381 if (MigrationState.REMOVED.equals(acElement.getMigrationState())) {
384 var commonProperties = cacheProvider.getCommonProperties(compositionTargetId, acElement.getDefinition());
385 return AcmStageUtils.findStageSetMigrate(commonProperties);
389 private void callParticipantRollback(AutomationCompositionMigration migrationMsg, List<AcElementDeploy> acElements,
390 AutomationComposition automationCompositionCopy) {
391 var automationComposition = cacheProvider.getAutomationComposition(automationCompositionCopy.getInstanceId());
392 var instanceElementTargetMap = cacheProvider.getInstanceElementDtoMap(automationComposition);
393 var compositionElementTargetMap = cacheProvider.getCompositionElementDtoMap(automationComposition);
394 var compositionElementMap = cacheProvider.getCompositionElementDtoMap(automationCompositionCopy,
395 automationCompositionCopy.getCompositionTargetId());
396 var instanceElementMap = cacheProvider.getInstanceElementDtoMap(automationCompositionCopy);
397 for (var acElement : acElements) {
398 var stageSet = getRollbackStageSet(acElement, automationComposition.getCompositionId());
399 var removed = MigrationState.NEW.equals(acElement.getMigrationState());
401 var commonProperties = cacheProvider
402 .getCommonProperties(automationComposition.getCompositionId(), acElement.getDefinition());
403 stageSet = AcmStageUtils.findStageSetMigrate(commonProperties);
405 var newElement = MigrationState.REMOVED.equals(acElement.getMigrationState());
406 if (stageSet.contains(migrationMsg.getStage())) {
408 var compositionElementDto =
409 new CompositionElementDto(automationCompositionCopy.getCompositionTargetId(),
410 acElement.getDefinition(), Map.of(), Map.of(), ElementState.NOT_PRESENT);
411 var instanceElementDto = new InstanceElementDto(automationCompositionCopy.getInstanceId(),
412 acElement.getId(), Map.of(), Map.of(), ElementState.NOT_PRESENT);
413 var compositionElementTargetDto =
414 CacheProvider.changeStateToNew(compositionElementTargetMap.get(acElement.getId()));
415 var instanceElementTargetDto =
416 CacheProvider.changeStateToNew(instanceElementTargetMap.get(acElement.getId()));
418 listenerRollback(migrationMsg.getMessageId(), compositionElementDto, compositionElementTargetDto,
419 instanceElementDto, instanceElementTargetDto, migrationMsg.getStage());
421 } else if (removed) {
422 var compositionDtoTarget = new CompositionElementDto(
423 automationCompositionCopy.getCompositionId(), acElement.getDefinition(),
424 Map.of(), Map.of(), ElementState.REMOVED);
425 var instanceElementDtoTarget = new InstanceElementDto(automationCompositionCopy.getInstanceId(),
426 acElement.getId(), Map.of(), Map.of(), ElementState.REMOVED);
427 listenerRollback(migrationMsg.getMessageId(), compositionElementMap.get(acElement.getId()),
428 compositionDtoTarget, instanceElementMap.get(acElement.getId()), instanceElementDtoTarget,
429 migrationMsg.getStage());
431 } else { // DEFAULT case
432 listenerRollback(migrationMsg.getMessageId(), compositionElementMap.get(acElement.getId()),
433 compositionElementTargetMap.get(acElement.getId()),
434 instanceElementMap.get(acElement.getId()), instanceElementTargetMap.get(acElement.getId()),
435 migrationMsg.getStage());
441 private Set<Integer> getRollbackStageSet(AcElementDeploy acElement, UUID compositionId) {
442 if (MigrationState.NEW.equals(acElement.getMigrationState())) {
445 var commonProperties = cacheProvider.getCommonProperties(compositionId, acElement.getDefinition());
446 return AcmStageUtils.findStageSetMigrate(commonProperties);
450 private void listenerMigrate(UUID messageId, CompositionElementDto compositionElement,
451 CompositionElementDto compositionElementTarget, InstanceElementDto instanceElement,
452 InstanceElementDto instanceElementMigrate, int stage) {
453 LOGGER.info("Invoking migration of element on the participant for {}", instanceElement.elementId());
454 listener.migrate(messageId, compositionElement, compositionElementTarget, instanceElement,
455 instanceElementMigrate, stage);
458 private void listenerRollback(UUID messageId, CompositionElementDto compositionElement,
459 CompositionElementDto compositionElementTarget, InstanceElementDto instanceElement,
460 InstanceElementDto instanceElementMigrate, int stage) {
461 LOGGER.info("Invoking rollback of element on the participant for {}", instanceElement.elementId());
462 listener.rollback(messageId, compositionElement, compositionElementTarget, instanceElement,
463 instanceElementMigrate, stage);