From: rameshiyer27 Date: Sat, 4 Oct 2025 13:48:24 +0000 (+0100) Subject: Fix runtime and intermediary to support Migration with new participant X-Git-Url: https://gerrit.onap.org/r/gitweb?a=commitdiff_plain;h=2c0c0da480980b43418c13763add39074ad1544a;p=policy%2Fclamp.git Fix runtime and intermediary to support Migration with new participant - Removed elements are retained in the migration state REMOVED until they are deleted by the participant. - New elements are retained in the migration state NEW - All the element states are reverted to DEFAULT after successful Migration - Validations are fixed to ignore the REMOVED elements present for the target composition - startPhase and stage values are updated as 0 for REMOVED elements. - startPhase for undeploy of NEW elements are set to 0 - Intermediary is retaining the removed elements in cache. - commpon properties for NEW and REMOVED elements are fetched from their respective compositions in Intermediary. - INFO logging for message executor and cache provider for better visibility of the flow. Issue-ID: POLICY-5463 Change-Id: I090c5053655263b8e5a2a3a46c8f2f887f9c9883 Signed-off-by: rameshiyer27 --- diff --git a/models/src/main/java/org/onap/policy/clamp/models/acm/concepts/ParticipantUtils.java b/models/src/main/java/org/onap/policy/clamp/models/acm/concepts/ParticipantUtils.java index ded715b67..d7069e2f8 100644 --- a/models/src/main/java/org/onap/policy/clamp/models/acm/concepts/ParticipantUtils.java +++ b/models/src/main/java/org/onap/policy/clamp/models/acm/concepts/ParticipantUtils.java @@ -87,12 +87,15 @@ public final class ParticipantUtils { ToscaServiceTemplate toscaServiceTemplate) { Set minStage = new HashSet<>(); for (var element : automationComposition.getElements().values()) { - var toscaNodeTemplate = toscaServiceTemplate.getToscaTopologyTemplate().getNodeTemplates() - .get(element.getDefinition().getName()); - var stage = DeployState.MIGRATING.equals(automationComposition.getDeployState()) - ? ParticipantUtils.findStageSetMigrate(toscaNodeTemplate.getProperties()) - : ParticipantUtils.findStageSetPrepare(toscaNodeTemplate.getProperties()); - minStage.addAll(stage); + if (! MigrationState.REMOVED.equals(element.getMigrationState())) { + var toscaNodeTemplate = toscaServiceTemplate.getToscaTopologyTemplate().getNodeTemplates() + .get(element.getDefinition().getName()); + var stage = DeployState.MIGRATING.equals(automationComposition.getDeployState()) + ? ParticipantUtils.findStageSetMigrate(toscaNodeTemplate.getProperties()) + : ParticipantUtils.findStageSetPrepare(toscaNodeTemplate.getProperties()); + minStage.addAll(stage); + } + } return minStage.stream().min(Integer::compare).orElse(0); } diff --git a/models/src/main/java/org/onap/policy/clamp/models/acm/utils/AcmUtils.java b/models/src/main/java/org/onap/policy/clamp/models/acm/utils/AcmUtils.java index 987e6c723..c7bc598f5 100644 --- a/models/src/main/java/org/onap/policy/clamp/models/acm/utils/AcmUtils.java +++ b/models/src/main/java/org/onap/policy/clamp/models/acm/utils/AcmUtils.java @@ -47,6 +47,7 @@ import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElement; import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElementDefinition; import org.onap.policy.clamp.models.acm.concepts.DeployState; import org.onap.policy.clamp.models.acm.concepts.LockState; +import org.onap.policy.clamp.models.acm.concepts.MigrationState; import org.onap.policy.clamp.models.acm.concepts.NodeTemplateState; import org.onap.policy.clamp.models.acm.concepts.ParticipantDefinition; import org.onap.policy.clamp.models.acm.concepts.ParticipantDeploy; @@ -198,7 +199,7 @@ public final class AcmUtils { * @return the result of validation */ public static BeanValidationResult validateAutomationComposition(AutomationComposition automationComposition, - ToscaServiceTemplate serviceTemplate, String toscaCompositionName) { + ToscaServiceTemplate serviceTemplate, String toscaCompositionName, boolean migrateOperation) { var result = new BeanValidationResult(ENTRY + automationComposition.getName(), automationComposition); var map = getMapToscaNodeTemplates(serviceTemplate); @@ -221,13 +222,14 @@ public final class AcmUtils { .collect(Collectors.toMap(ToscaConceptIdentifier::getName, UnaryOperator.identity())); // @formatter:on - if (definitions.size() != automationComposition.getElements().size()) { + if (definitions.size() != automationComposition.getElements().size() + && !migrateOperation) { result.setResult(ValidationStatus.INVALID, - "Elements of the instance not matching with the elements of the composition"); + "Elements of the instance not matching with the elements of the composition"); } for (var element : automationComposition.getElements().values()) { - result.addResult(validateDefinition(definitions, element.getDefinition())); + result.addResult(validateDefinition(definitions, element.getDefinition(), element.getMigrationState())); } } @@ -236,12 +238,13 @@ public final class AcmUtils { } private static ValidationResult validateDefinition(Map definitions, - ToscaConceptIdentifier definition) { + ToscaConceptIdentifier definition, + MigrationState migrationState) { var result = new BeanValidationResult(ENTRY + definition.getName(), definition); var identifier = definitions.get(definition.getName()); - if (identifier == null) { + if (identifier == null && MigrationState.DEFAULT.equals(migrationState)) { result.setResult(ValidationStatus.INVALID, "Not found"); - } else if (!identifier.equals(definition)) { + } else if (! definition.equals(identifier) && MigrationState.DEFAULT.equals(migrationState)) { result.setResult(ValidationStatus.INVALID, "Version not matching"); } return (result.isClean() ? null : result); @@ -435,7 +438,7 @@ public final class AcmUtils { * @param deployOrder the DeployOrder */ public static List createParticipantDeployList(AutomationComposition automationComposition, - DeployOrder deployOrder, List removedElements) { + DeployOrder deployOrder) { Map> map = new HashMap<>(); for (var element : automationComposition.getElements().values()) { var acElementDeploy = createAcElementDeploy(element, deployOrder); @@ -449,16 +452,6 @@ public final class AcmUtils { participantDeploy.setAcElementList(entry.getValue()); participantDeploys.add(participantDeploy); } - // Include the participantIds for the removed elements - for (var element : removedElements) { - if (map.get(element.getParticipantId()) == null) { - var participantDeploy = new ParticipantDeploy(); - participantDeploy.setParticipantId(element.getParticipantId()); - participantDeploys.add(participantDeploy); - map.put(element.getParticipantId(), new ArrayList<>()); - } - - } return participantDeploys; } diff --git a/models/src/test/java/org/onap/policy/clamp/models/acm/utils/AcmUtilsTest.java b/models/src/test/java/org/onap/policy/clamp/models/acm/utils/AcmUtilsTest.java index ea5c85071..0fe5bae3f 100644 --- a/models/src/test/java/org/onap/policy/clamp/models/acm/utils/AcmUtilsTest.java +++ b/models/src/test/java/org/onap/policy/clamp/models/acm/utils/AcmUtilsTest.java @@ -41,7 +41,6 @@ import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionDefinition import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElement; import org.onap.policy.clamp.models.acm.concepts.DeployState; import org.onap.policy.clamp.models.acm.concepts.LockState; -import org.onap.policy.clamp.models.acm.concepts.ParticipantDeploy; import org.onap.policy.clamp.models.acm.concepts.SubState; import org.onap.policy.clamp.models.acm.document.concepts.DocToscaServiceTemplate; import org.onap.policy.clamp.models.acm.messages.rest.instantiation.DeployOrder; @@ -135,13 +134,13 @@ class AcmUtilsTest { var doc = new DocToscaServiceTemplate(CommonTestData.getToscaServiceTemplate(TOSCA_TEMPLATE_YAML)); var automationComposition = CommonTestData.getJsonObject(AC_INSTANTIATION_JSON, AutomationComposition.class); var result = AcmUtils.validateAutomationComposition(automationComposition, doc.toAuthorative(), - AUTOMATION_COMPOSITION_NODE_TYPE); + AUTOMATION_COMPOSITION_NODE_TYPE, false); assertTrue(result.isValid()); var element = automationComposition.getElements().values().iterator().next(); automationComposition.getElements().remove(element.getId()); result = AcmUtils.validateAutomationComposition(automationComposition, doc.toAuthorative(), - AUTOMATION_COMPOSITION_NODE_TYPE); + AUTOMATION_COMPOSITION_NODE_TYPE, false); assertFalse(result.isValid()); assertThat(result.getMessage()).contains("not matching"); } @@ -151,7 +150,7 @@ class AcmUtilsTest { var automationComposition = getDummyAutomationComposition(); var toscaServiceTemplate = getDummyToscaServiceTemplate(); var result = AcmUtils.validateAutomationComposition(automationComposition, - toscaServiceTemplate, AUTOMATION_COMPOSITION_NODE_TYPE); + toscaServiceTemplate, AUTOMATION_COMPOSITION_NODE_TYPE, false); assertNotNull(result); assertFalse(result.isValid()); @@ -161,12 +160,12 @@ class AcmUtilsTest { nodeTemplates.put("org.onap.dcae.acm.DCAEMicroserviceAutomationCompositionParticipant", nodeTemplate); toscaServiceTemplate.getToscaTopologyTemplate().setNodeTemplates(nodeTemplates); result = AcmUtils.validateAutomationComposition(automationComposition, toscaServiceTemplate, - AUTOMATION_COMPOSITION_NODE_TYPE); + AUTOMATION_COMPOSITION_NODE_TYPE, false); assertFalse(result.isValid()); var doc = new DocToscaServiceTemplate(CommonTestData.getToscaServiceTemplate(TOSCA_TEMPLATE_YAML)); result = AcmUtils.validateAutomationComposition(automationComposition, doc.toAuthorative(), - AUTOMATION_COMPOSITION_NODE_TYPE); + AUTOMATION_COMPOSITION_NODE_TYPE, false); assertFalse(result.isValid()); } @@ -225,7 +224,7 @@ class AcmUtilsTest { @Test void testCreateAcElementDeployList() { var automationComposition = getDummyAutomationComposition(); - var result = AcmUtils.createParticipantDeployList(automationComposition, DeployOrder.DEPLOY, List.of()); + var result = AcmUtils.createParticipantDeployList(automationComposition, DeployOrder.DEPLOY); assertThat(result).hasSameSizeAs(automationComposition.getElements().values()); for (var participantDeploy : result) { for (var element : participantDeploy.getAcElementList()) { @@ -234,26 +233,6 @@ class AcmUtilsTest { } } - @Test - void testAcDeployListWithRemovedElements() { - var removedElement1 = CommonTestData.getJsonObject( - "src/test/resources/json/AutomationCompositionElementNoOrderedState.json", - AutomationCompositionElement.class); - var participantId1 = UUID.randomUUID(); - var participantId2 = UUID.randomUUID(); - assert removedElement1 != null; - removedElement1.setParticipantId(participantId1); - var removedElement2 = new AutomationCompositionElement(removedElement1); - removedElement2.setParticipantId(participantId2); - - var automationComposition = getDummyAutomationComposition(); - var result = AcmUtils.createParticipantDeployList(automationComposition, DeployOrder.DEPLOY, - List.of(removedElement1, removedElement2)); - assertThat(result).hasSize(automationComposition.getElements().values().size() + 2); - var participantIds = result.stream().map(ParticipantDeploy::getParticipantId).toList(); - assertThat(participantIds).containsAll(List.of(participantId1, participantId2)); - } - @Test void testCreateAcElementRestart() { var element = getDummyAutomationComposition().getElements().values().iterator().next(); diff --git a/participant/participant-impl/participant-impl-simulator/src/main/java/org/onap/policy/clamp/acm/participant/sim/main/handler/SimulatorService.java b/participant/participant-impl/participant-impl-simulator/src/main/java/org/onap/policy/clamp/acm/participant/sim/main/handler/SimulatorService.java index d4743cf8a..69ab0dbd9 100644 --- a/participant/participant-impl/participant-impl-simulator/src/main/java/org/onap/policy/clamp/acm/participant/sim/main/handler/SimulatorService.java +++ b/participant/participant-impl/participant-impl-simulator/src/main/java/org/onap/policy/clamp/acm/participant/sim/main/handler/SimulatorService.java @@ -296,7 +296,7 @@ public class SimulatorService { DeployState.DELETED, null, StateChangeResult.NO_ERROR, "Migration - Deleted"); } else { intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId, - DeployState.UNDEPLOYED, null, StateChangeResult.FAILED, "Migration - Delete failed!"); + DeployState.DEPLOYED, null, StateChangeResult.FAILED, "Migration - Delete failed!"); } } diff --git a/participant/participant-impl/participant-impl-simulator/src/test/java/org/onap/policy/clamp/acm/participant/sim/main/handler/SimulatorServiceTest.java b/participant/participant-impl/participant-impl-simulator/src/test/java/org/onap/policy/clamp/acm/participant/sim/main/handler/SimulatorServiceTest.java index 29d0f76ec..49b01956e 100644 --- a/participant/participant-impl/participant-impl-simulator/src/test/java/org/onap/policy/clamp/acm/participant/sim/main/handler/SimulatorServiceTest.java +++ b/participant/participant-impl/participant-impl-simulator/src/test/java/org/onap/policy/clamp/acm/participant/sim/main/handler/SimulatorServiceTest.java @@ -184,7 +184,7 @@ class SimulatorServiceTest { simulatorService.getConfig().setMigrateSuccess(false); simulatorService.deleteInMigration(instanceId, elementId); verify(intermediaryApi).updateAutomationCompositionElementState(instanceId, elementId, - DeployState.UNDEPLOYED, null, StateChangeResult.FAILED, "Migration - Delete failed!"); + DeployState.DEPLOYED, null, StateChangeResult.FAILED, "Migration - Delete failed!"); } diff --git a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AcDefinitionHandler.java b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AcDefinitionHandler.java index e0f8cd317..c4ebfbfb2 100644 --- a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AcDefinitionHandler.java +++ b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AcDefinitionHandler.java @@ -151,7 +151,7 @@ public class AcDefinitionHandler { } private void checkComposition(ParticipantSync participantSyncMsg) { - // edge case scenario in migration whit remove/add elements, + // edge case scenario in migration with remove/add elements, // when composition or target composition doesn't contain elements from this participant for (var msg : cacheProvider.getMessagesOnHold().values()) { if (participantSyncMsg.getCompositionId().equals(msg.getCompositionTargetId())) { diff --git a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AcSubStateHandler.java b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AcSubStateHandler.java index f00c87b1f..20b2a2438 100644 --- a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AcSubStateHandler.java +++ b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AcSubStateHandler.java @@ -59,16 +59,27 @@ public class AcSubStateHandler { if (migrationMsg.getAutomationCompositionId() == null || migrationMsg.getCompositionTargetId() == null) { return; } - + var acTargetDefinition = cacheProvider.getAcElementsDefinitions().get(migrationMsg.getCompositionTargetId()); var automationComposition = cacheProvider.getAutomationComposition(migrationMsg.getAutomationCompositionId()); if (automationComposition == null) { - LOGGER.debug("Automation composition {} does not use this participant", + if (acTargetDefinition == null) { + LOGGER.warn("Automation composition {} does not use this participant", + migrationMsg.getAutomationCompositionId()); + return; + } + } else { + LOGGER.info("Migration Precheck invoked on an existing participant for the instance {}", migrationMsg.getAutomationCompositionId()); - return; } - automationComposition.setSubState(SubState.MIGRATION_PRECHECKING); for (var participantDeploy : migrationMsg.getParticipantUpdatesList()) { if (cacheProvider.getParticipantId().equals(participantDeploy.getParticipantId())) { + if (automationComposition == null) { // New element with new participant + automationComposition = cacheProvider.createAcInstance(migrationMsg.getCompositionId(), + migrationMsg.getCompositionTargetId(), migrationMsg.getAutomationCompositionId(), + participantDeploy, DeployState.MIGRATING, SubState.MIGRATION_PRECHECKING, + migrationMsg.getRevisionIdInstance()); + LOGGER.info("New participant with new element type added for Migration Precheck"); + } callParticipantMigratePrecheck(migrationMsg.getMessageId(), participantDeploy.getAcElementList(), automationComposition, migrationMsg.getCompositionTargetId()); @@ -153,7 +164,7 @@ public class AcSubStateHandler { if (acPrepareMsg.isPreDeploy()) { for (var participantPrepare : acPrepareMsg.getParticipantList()) { if (cacheProvider.getParticipantId().equals(participantPrepare.getParticipantId())) { - cacheProvider.initializeAutomationComposition(acPrepareMsg.getCompositionId(), + cacheProvider.initializeAutomationComposition(acPrepareMsg.getCompositionId(), null, acPrepareMsg.getAutomationCompositionId(), participantPrepare, DeployState.UNDEPLOYED, SubState.PREPARING, acPrepareMsg.getRevisionIdInstance()); callParticipanPrepare(acPrepareMsg.getMessageId(), participantPrepare.getAcElementList(), diff --git a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AutomationCompositionHandler.java b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AutomationCompositionHandler.java index 0b076e56b..5b2f74fce 100644 --- a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AutomationCompositionHandler.java +++ b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AutomationCompositionHandler.java @@ -21,21 +21,23 @@ package org.onap.policy.clamp.acm.participant.intermediary.handler; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.UUID; -import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.onap.policy.clamp.acm.participant.intermediary.api.CompositionElementDto; import org.onap.policy.clamp.acm.participant.intermediary.api.ElementState; import org.onap.policy.clamp.acm.participant.intermediary.api.InstanceElementDto; import org.onap.policy.clamp.acm.participant.intermediary.comm.ParticipantMessagePublisher; +import org.onap.policy.clamp.acm.participant.intermediary.handler.cache.AcDefinition; import org.onap.policy.clamp.acm.participant.intermediary.handler.cache.CacheProvider; import org.onap.policy.clamp.models.acm.concepts.AcElementDeploy; import org.onap.policy.clamp.models.acm.concepts.AutomationComposition; -import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElement; import org.onap.policy.clamp.models.acm.concepts.DeployState; import org.onap.policy.clamp.models.acm.concepts.LockState; +import org.onap.policy.clamp.models.acm.concepts.MigrationState; import org.onap.policy.clamp.models.acm.concepts.ParticipantDeploy; import org.onap.policy.clamp.models.acm.concepts.ParticipantUtils; import org.onap.policy.clamp.models.acm.concepts.StateChangeResult; @@ -59,6 +61,7 @@ import org.springframework.stereotype.Component; @RequiredArgsConstructor public class AutomationCompositionHandler { private static final Logger LOGGER = LoggerFactory.getLogger(AutomationCompositionHandler.class); + private static final String AC_NOT_USED = "Automation composition {} does not use this participant"; private final CacheProvider cacheProvider; private final ParticipantMessagePublisher publisher; @@ -85,8 +88,7 @@ public class AutomationCompositionHandler { automationCompositionAck.setAutomationCompositionId(stateChangeMsg.getAutomationCompositionId()); publisher.sendAutomationCompositionAck(automationCompositionAck); } else { - LOGGER.debug("Automation composition {} does not use this participant", - stateChangeMsg.getAutomationCompositionId()); + LOGGER.warn(AC_NOT_USED, stateChangeMsg.getAutomationCompositionId()); } return; } @@ -184,39 +186,57 @@ public class AutomationCompositionHandler { } } - private void migrateExistingElementsOnThisParticipant(UUID instanceId, UUID compositionTargetId, - ParticipantDeploy participantDeploy, int stage) { - var automationComposition = cacheProvider.getAutomationComposition(instanceId); - var acElementList = automationComposition.getElements(); + private void migrateExistingElementsOnThisParticipant(AutomationComposition automationComposition, + UUID compositionTargetId, ParticipantDeploy participantDeploy, + int stage, boolean newParticipant) { for (var element : participantDeploy.getAcElementList()) { + UUID compIdForCommonProperties = null; + if (MigrationState.REMOVED.equals(element.getMigrationState())) { + compIdForCommonProperties = automationComposition.getCompositionId(); + } else { + compIdForCommonProperties = compositionTargetId; + } var compositionInProperties = - cacheProvider.getCommonProperties(compositionTargetId, element.getDefinition()); + cacheProvider.getCommonProperties(compIdForCommonProperties, element.getDefinition()); var stageSet = ParticipantUtils.findStageSetMigrate(compositionInProperties); + if (MigrationState.REMOVED.equals(element.getMigrationState())) { + stageSet = Set.of(0); + } if (stageSet.contains(stage)) { - var acElement = acElementList.get(element.getId()); - if (acElement == null) { - var newElement = CacheProvider.createAutomationCompositionElement(element); - newElement.setParticipantId(participantDeploy.getParticipantId()); - newElement.setDeployState(DeployState.MIGRATING); - newElement.setLockState(LockState.LOCKED); - newElement.setStage(stage); - - acElementList.put(element.getId(), newElement); - LOGGER.info("New Ac Element with id {} is added in Migration", element.getId()); - } else { - AcmUtils.recursiveMerge(acElement.getProperties(), element.getProperties()); - acElement.setDeployState(DeployState.MIGRATING); - acElement.setStage(stage); - acElement.setDefinition(element.getDefinition()); - } + migrateElement(element, automationComposition, compositionTargetId, stage, newParticipant, + participantDeploy); } } - // Check for missing elements and remove them from cache - var elementsToRemove = findElementsToRemove(participantDeploy.getAcElementList(), acElementList); - for (var key : elementsToRemove) { - acElementList.remove(key); - LOGGER.info("Element with id {} is removed in Migration", key); + } + + private void migrateElement(AcElementDeploy element, AutomationComposition automationComposition, + UUID compositionTargetId, int stage, boolean newParticipant, + ParticipantDeploy participantDeploy) { + var acElementList = automationComposition.getElements(); + automationComposition.setCompositionTargetId(compositionTargetId); + automationComposition.setDeployState(DeployState.MIGRATING); + var acElement = acElementList.get(element.getId()); + if (acElement == null) { // NEW element with existing participant + var newElement = CacheProvider.createAutomationCompositionElement(element); + newElement.setParticipantId(participantDeploy.getParticipantId()); + newElement.setDeployState(DeployState.MIGRATING); + newElement.setLockState(LockState.LOCKED); + newElement.setStage(stage); + newElement.setMigrationState(MigrationState.NEW); + + acElementList.put(element.getId(), newElement); + LOGGER.info("New Ac Element with id {} is added in Migration", element.getId()); + } else { + acElement.setStage(stage); + acElement.setMigrationState(element.getMigrationState()); + if (! newParticipant) { //DEFAULT element + AcmUtils.recursiveMerge(acElement.getProperties(), element.getProperties()); + acElement.setDeployState(DeployState.MIGRATING); + acElement.setDefinition(element.getDefinition()); + } + LOGGER.info("Cache updated for the migration of element with id {}", element.getId()); } + } private void updateExistingElementsOnThisParticipant(UUID instanceId, ParticipantDeploy participantDeploy) { @@ -230,12 +250,6 @@ public class AutomationCompositionHandler { } } - private List findElementsToRemove(List acElementDeployList, - Map acElementList) { - var acElementDeploySet = acElementDeployList.stream().map(AcElementDeploy::getId).collect(Collectors.toSet()); - return acElementList.keySet().stream().filter(id -> !acElementDeploySet.contains(id)).toList(); - } - /** * Method to handle when the new state from participant is UNINITIALISED state. * @@ -245,16 +259,24 @@ public class AutomationCompositionHandler { */ private void handleUndeployState(UUID messageId, final AutomationComposition automationComposition, Integer startPhaseMsg) { - automationComposition.setCompositionTargetId(null); automationComposition.setDeployState(DeployState.UNDEPLOYING); for (var element : automationComposition.getElements().values()) { - var compositionInProperties = cacheProvider.getCommonProperties(automationComposition.getCompositionId(), - element.getDefinition()); + UUID compositionId = null; + if (MigrationState.NEW.equals(element.getMigrationState())) { + compositionId = automationComposition.getCompositionTargetId(); + } else { + compositionId = automationComposition.getCompositionId(); + } + var compositionInProperties = cacheProvider.getCommonProperties(compositionId, element.getDefinition()); int startPhase = ParticipantUtils.findStartPhase(compositionInProperties); + if (MigrationState.NEW.equals(element.getMigrationState())) { + // Undeploy newly added element on a Failed Migration + startPhase = 0; + } if (startPhaseMsg.equals(startPhase)) { element.setDeployState(DeployState.UNDEPLOYING); var compositionElement = - cacheProvider.createCompositionElementDto(automationComposition.getCompositionId(), element); + cacheProvider.createCompositionElementDto(compositionId, element); var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(), element.getProperties(), element.getOutProperties()); listener.undeploy(messageId, compositionElement, instanceElement); @@ -288,76 +310,131 @@ public class AutomationCompositionHandler { */ public void handleAutomationCompositionMigration(AutomationCompositionMigration migrationMsg) { var automationComposition = cacheProvider.getAutomationComposition(migrationMsg.getAutomationCompositionId()); + var acTargetDefinition = cacheProvider.getAcElementsDefinitions().get(migrationMsg.getCompositionTargetId()); + if (Boolean.FALSE.equals(migrationMsg.getRollback())) { + handleMigration(automationComposition, acTargetDefinition, migrationMsg); + } else { + handleRollback(automationComposition, migrationMsg); + } + } + + private void handleRollback(AutomationComposition automationComposition, + AutomationCompositionMigration migrationMsg) { + AutomationComposition acCopy = null; if (automationComposition == null) { - LOGGER.debug("Automation composition {} does not use this participant", - migrationMsg.getAutomationCompositionId()); + LOGGER.warn(AC_NOT_USED, migrationMsg.getAutomationCompositionId()); return; + } else { + LOGGER.info("Rollback operation invoked for the instance {}", migrationMsg.getAutomationCompositionId()); + acCopy = new AutomationComposition(automationComposition); + automationComposition.setCompositionTargetId(migrationMsg.getCompositionTargetId()); + automationComposition.setDeployState(DeployState.MIGRATION_REVERTING); } - var acCopy = new AutomationComposition(automationComposition); - automationComposition.setCompositionTargetId(migrationMsg.getCompositionTargetId()); - automationComposition.setDeployState(DeployState.MIGRATING); for (var participantDeploy : migrationMsg.getParticipantUpdatesList()) { if (cacheProvider.getParticipantId().equals(participantDeploy.getParticipantId())) { + migrateExistingElementsOnThisParticipant(automationComposition, migrationMsg.getCompositionTargetId(), + participantDeploy, migrationMsg.getStage(), false); - migrateExistingElementsOnThisParticipant(migrationMsg.getAutomationCompositionId(), - migrationMsg.getCompositionTargetId(), participantDeploy, migrationMsg.getStage()); - - callParticipantMigrate(migrationMsg.getMessageId(), participantDeploy.getAcElementList(), acCopy, - migrationMsg.getCompositionTargetId(), migrationMsg.getStage(), - Boolean.TRUE.equals(migrationMsg.getRollback())); + callParticipantMigrate(migrationMsg, participantDeploy.getAcElementList(), acCopy); } + } } - private void callParticipantMigrate(UUID messageId, List acElements, AutomationComposition acCopy, - UUID compositionTargetId, int stage, boolean rollback) { - var compositionElementMap = cacheProvider.getCompositionElementDtoMap(acCopy); - var instanceElementMap = cacheProvider.getInstanceElementDtoMap(acCopy); - var automationComposition = cacheProvider.getAutomationComposition(acCopy.getInstanceId()); - var compositionElementTargetMap = - cacheProvider.getCompositionElementDtoMap(automationComposition, compositionTargetId); - var instanceElementMigrateMap = cacheProvider.getInstanceElementDtoMap(automationComposition); + private void handleMigration(AutomationComposition automationComposition, AcDefinition acTargetDefinition, + AutomationCompositionMigration migrationMsg) { + AutomationComposition acCopy = null; + if (automationComposition == null) { + if (acTargetDefinition == null) { + LOGGER.warn(AC_NOT_USED, migrationMsg.getAutomationCompositionId()); + return; + } + } else { + LOGGER.info("Migration invoked on an existing participant for the instance {}", + migrationMsg.getAutomationCompositionId()); + acCopy = new AutomationComposition(automationComposition); + } + var newParticipant = false; + for (var participantDeploy : migrationMsg.getParticipantUpdatesList()) { + if (cacheProvider.getParticipantId().equals(participantDeploy.getParticipantId())) { + if (automationComposition == null) { + // New element with new participant added in Migration + LOGGER.info("Participant newly added in Migration for the instance {}", + migrationMsg.getAutomationCompositionId()); + newParticipant = true; + cacheProvider.initializeAutomationComposition(migrationMsg.getCompositionId(), + migrationMsg.getCompositionTargetId(), migrationMsg.getAutomationCompositionId(), + participantDeploy, DeployState.MIGRATING, SubState.NONE, + migrationMsg.getRevisionIdInstance()); + automationComposition = cacheProvider + .getAutomationComposition(migrationMsg.getAutomationCompositionId()); + } + migrateExistingElementsOnThisParticipant(automationComposition, migrationMsg.getCompositionTargetId(), + participantDeploy, migrationMsg.getStage(), newParticipant); + + callParticipantMigrate(migrationMsg, participantDeploy.getAcElementList(), acCopy); + } + } + } - // Call migrate for newly added and updated elements + private void callParticipantMigrate(AutomationCompositionMigration migrationMsg, List acElements, + AutomationComposition formerAcInstance) { + var latestAcFromCache = cacheProvider.getAutomationComposition(migrationMsg.getAutomationCompositionId()); + var instanceElementTargetMap = cacheProvider.getInstanceElementDtoMap(latestAcFromCache); + var compositionElementTargetMap = cacheProvider.getCompositionElementDtoMap(latestAcFromCache, + migrationMsg.getCompositionTargetId()); + Map compositionElementMap = new HashMap<>(); + Map instanceElementMap = new HashMap<>(); + if (formerAcInstance != null) { //Existing participant + compositionElementMap = cacheProvider.getCompositionElementDtoMap(formerAcInstance); + instanceElementMap = cacheProvider.getInstanceElementDtoMap(formerAcInstance); + } + // Call migrate for new and existing elements for (var acElement : acElements) { + UUID compIdForCommonProperties = null; + if (MigrationState.REMOVED.equals(acElement.getMigrationState())) { + compIdForCommonProperties = latestAcFromCache.getCompositionId(); + } else { + compIdForCommonProperties = migrationMsg.getCompositionTargetId(); + } var compositionInProperties = - cacheProvider.getCommonProperties(compositionTargetId, acElement.getDefinition()); + cacheProvider.getCommonProperties(compIdForCommonProperties, acElement.getDefinition()); var stageSet = ParticipantUtils.findStageSetMigrate(compositionInProperties); - if (stageSet.contains(stage)) { - if (instanceElementMap.get(acElement.getId()) == null) { - var compositionElementDto = - new CompositionElementDto(acCopy.getCompositionId(), acElement.getDefinition(), Map.of(), - Map.of(), ElementState.NOT_PRESENT); - var instanceElementDto = - new InstanceElementDto(acCopy.getInstanceId(), acElement.getId(), Map.of(), Map.of(), - ElementState.NOT_PRESENT); + if (MigrationState.REMOVED.equals(acElement.getMigrationState())) { + stageSet = Set.of(0); + } + var rollback = Boolean.TRUE.equals(migrationMsg.getRollback()); + if (stageSet.contains(migrationMsg.getStage())) { + if (MigrationState.NEW.equals(acElement.getMigrationState())) { + var compositionElementDto = new CompositionElementDto(migrationMsg.getCompositionId(), + acElement.getDefinition(), Map.of(), Map.of(), ElementState.NOT_PRESENT); + var instanceElementDto = new InstanceElementDto(migrationMsg.getAutomationCompositionId(), + acElement.getId(), Map.of(), Map.of(), ElementState.NOT_PRESENT); var compositionElementTargetDto = CacheProvider.changeStateToNew(compositionElementTargetMap.get(acElement.getId())); - var instanceElementMigrateDto = - CacheProvider.changeStateToNew(instanceElementMigrateMap.get(acElement.getId())); + var instanceElementTargetDto = + CacheProvider.changeStateToNew(instanceElementTargetMap.get(acElement.getId())); + + listenerMigrate(migrationMsg.getMessageId(), compositionElementDto, compositionElementTargetDto, + instanceElementDto, instanceElementTargetDto, migrationMsg.getStage(), rollback); - listenerMigrate(messageId, compositionElementDto, compositionElementTargetDto, instanceElementDto, - instanceElementMigrateDto, stage, rollback); - } else { - listenerMigrate(messageId, compositionElementMap.get(acElement.getId()), + } else if (MigrationState.REMOVED.equals(acElement.getMigrationState())) { + var compositionDtoTarget = new CompositionElementDto(migrationMsg.getCompositionTargetId(), + acElement.getDefinition(), Map.of(), Map.of(), ElementState.REMOVED); + var instanceElementDtoTarget = new InstanceElementDto(migrationMsg.getAutomationCompositionId(), + acElement.getId(), Map.of(), Map.of(), ElementState.REMOVED); + listenerMigrate(migrationMsg.getMessageId(), compositionElementMap.get(acElement.getId()), + compositionDtoTarget, instanceElementMap.get(acElement.getId()), instanceElementDtoTarget, + migrationMsg.getStage(), rollback); + + } else { // DEFAULT case + listenerMigrate(migrationMsg.getMessageId(), compositionElementMap.get(acElement.getId()), compositionElementTargetMap.get(acElement.getId()), - instanceElementMap.get(acElement.getId()), instanceElementMigrateMap.get(acElement.getId()), - stage, rollback); + instanceElementMap.get(acElement.getId()), instanceElementTargetMap.get(acElement.getId()), + migrationMsg.getStage(), rollback); } } } - if (stage == 0) { - // Call migrate for removed elements - List removedElements = findElementsToRemove(acElements, acCopy.getElements()); - for (var elementId : removedElements) { - var compositionDtoTarget = new CompositionElementDto(compositionTargetId, - acCopy.getElements().get(elementId).getDefinition(), Map.of(), Map.of(), ElementState.REMOVED); - var instanceDtoTarget = new InstanceElementDto(acCopy.getInstanceId(), elementId, Map.of(), Map.of(), - ElementState.REMOVED); - listenerMigrate(messageId, compositionElementMap.get(elementId), compositionDtoTarget, - instanceElementMap.get(elementId), instanceDtoTarget, 0, rollback); - } - } } private void listenerMigrate(UUID messageId, CompositionElementDto compositionElement, @@ -367,6 +444,7 @@ public class AutomationCompositionHandler { listener.rollback(messageId, compositionElement, compositionElementTarget, instanceElement, instanceElementMigrate, stage); } else { + LOGGER.info("Invoking migration of element on the participant for {}", instanceElement.elementId()); listener.migrate(messageId, compositionElement, compositionElementTarget, instanceElement, instanceElementMigrate, stage); } diff --git a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/MsgExecutor.java b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/MsgExecutor.java index 2da33a9b4..cead60a1b 100644 --- a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/MsgExecutor.java +++ b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/MsgExecutor.java @@ -87,7 +87,7 @@ public class MsgExecutor { message.setCompositionId(null); message.setRevisionIdComposition(null); } else { - LOGGER.debug("Composition {} missing or outdated", message.getCompositionId()); + LOGGER.info("Composition {} missing or outdated", message.getCompositionId()); result = false; } } @@ -98,7 +98,7 @@ public class MsgExecutor { message.setCompositionTargetId(null); message.setRevisionIdCompositionTarget(null); } else { - LOGGER.debug("Composition Target {} missing or outdated", message.getCompositionTargetId()); + LOGGER.info("Composition Target {} missing or outdated", message.getCompositionTargetId()); result = false; } } @@ -108,7 +108,7 @@ public class MsgExecutor { message.setInstanceId(null); message.setRevisionIdInstance(null); } else { - LOGGER.debug("Instance {} missing or outdated", message.getInstanceId()); + LOGGER.info("Instance {} missing or outdated", message.getInstanceId()); result = false; } } diff --git a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/ParticipantHandler.java b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/ParticipantHandler.java index 73a329116..ce69b0b2d 100644 --- a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/ParticipantHandler.java +++ b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/ParticipantHandler.java @@ -258,7 +258,6 @@ public class ParticipantHandler { LOGGER.debug("Ignore ParticipantSync message {}", participantSyncMsg.getMessageId()); return; } - LOGGER.debug("ParticipantSync message received for participantId {}", participantSyncMsg.getParticipantId()); acDefinitionHandler.handleParticipantSync(participantSyncMsg); msgExecutor.check(); } diff --git a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/ThreadHandler.java b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/ThreadHandler.java index 1c177089e..6e9febc65 100644 --- a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/ThreadHandler.java +++ b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/ThreadHandler.java @@ -87,6 +87,7 @@ public class ThreadHandler implements Closeable { cleanExecution(instanceElement.elementId(), messageId); var result = executor.submit(() -> this.deployProcess(compositionElement, instanceElement)); executionMap.put(instanceElement.elementId(), result); + LOGGER.info("Deploy process successfully started on the participant for {}", instanceElement.elementId()); } private void deployProcess(CompositionElementDto compositionElement, InstanceElementDto instanceElement) { @@ -103,7 +104,7 @@ public class ThreadHandler implements Closeable { } /** - * Handle an udeploy on a automation composition element. + * Handle an undeploy on a automation composition element. * * @param messageId the messageId * @param compositionElement the information of the Automation Composition Definition Element @@ -113,6 +114,7 @@ public class ThreadHandler implements Closeable { cleanExecution(instanceElement.elementId(), messageId); var result = executor.submit(() -> this.undeployProcess(compositionElement, instanceElement)); executionMap.put(instanceElement.elementId(), result); + LOGGER.info("Undeploy process successfully started on the participant for {}", instanceElement.elementId()); } private void undeployProcess(CompositionElementDto compositionElement, InstanceElementDto instanceElement) { @@ -191,6 +193,7 @@ public class ThreadHandler implements Closeable { cleanExecution(instanceElement.elementId(), messageId); var result = executor.submit(() -> this.deleteProcess(compositionElement, instanceElement)); executionMap.put(instanceElement.elementId(), result); + LOGGER.info("Delete process successfully started on the participant for {}", instanceElement.elementId()); } private void deleteProcess(CompositionElementDto compositionElement, InstanceElementDto instanceElement) { @@ -220,6 +223,7 @@ public class ThreadHandler implements Closeable { var result = executor.submit(() -> this.updateProcess(compositionElement, instanceElement, instanceElementUpdated)); executionMap.put(instanceElement.elementId(), result); + LOGGER.info("Update process successfully started on the participant for {}", instanceElement.elementId()); } private void updateProcess(CompositionElementDto compositionElement, InstanceElementDto instanceElement, @@ -265,6 +269,7 @@ public class ThreadHandler implements Closeable { cleanExecution(composition.compositionId(), messageId); var result = executor.submit(() -> this.primeProcess(composition)); executionMap.put(composition.compositionId(), result); + LOGGER.info("Priming process successfully started on the participant for {}", composition.compositionId()); } private void primeProcess(CompositionDto composition) { @@ -272,9 +277,9 @@ public class ThreadHandler implements Closeable { listener.prime(composition); executionMap.remove(composition.compositionId()); } catch (Exception e) { - LOGGER.error("Composition Defintion prime failed {} {}", composition.compositionId(), e.getMessage()); + LOGGER.error("Composition Definition prime failed {} {}", composition.compositionId(), e.getMessage()); intermediaryApi.updateCompositionState(composition.compositionId(), AcTypeState.COMMISSIONED, - StateChangeResult.FAILED, "Composition Defintion prime failed"); + StateChangeResult.FAILED, "Composition Definition prime failed"); } } @@ -288,6 +293,7 @@ public class ThreadHandler implements Closeable { cleanExecution(composition.compositionId(), messageId); var result = executor.submit(() -> this.deprimeProcess(composition)); executionMap.put(composition.compositionId(), result); + LOGGER.info("De-priming process successfully started on the participant for {}", composition.compositionId()); } private void deprimeProcess(CompositionDto composition) { @@ -295,9 +301,9 @@ public class ThreadHandler implements Closeable { listener.deprime(composition); executionMap.remove(composition.compositionId()); } catch (Exception e) { - LOGGER.error("Composition Defintion deprime failed {} {}", composition.compositionId(), e.getMessage()); + LOGGER.error("Composition Definition deprime failed {} {}", composition.compositionId(), e.getMessage()); intermediaryApi.updateCompositionState(composition.compositionId(), AcTypeState.PRIMED, - StateChangeResult.FAILED, "Composition Defintion deprime failed"); + StateChangeResult.FAILED, "Composition Definition deprime failed"); } } @@ -331,6 +337,7 @@ public class ThreadHandler implements Closeable { this.migrateProcess(compositionElement, compositionElementTarget, instanceElement, instanceElementMigrate, stage)); executionMap.put(instanceElement.elementId(), result); + LOGGER.info("Migration process successfully started on the participant for {}", instanceElement.elementId()); } private void migrateProcess(CompositionElementDto compositionElement, @@ -366,6 +373,8 @@ public class ThreadHandler implements Closeable { this.migratePrecheckProcess(compositionElement, compositionElementTarget, instanceElement, instanceElementMigrate)); executionMap.put(instanceElement.elementId(), result); + LOGGER.info("Migration Precheck process successfully started on the participant for {}", + instanceElement.elementId()); } private void migratePrecheckProcess(CompositionElementDto compositionElement, @@ -396,6 +405,7 @@ public class ThreadHandler implements Closeable { cleanExecution(instanceElement.elementId(), messageId); var result = executor.submit(() -> this.reviewProcess(compositionElement, instanceElement)); executionMap.put(instanceElement.elementId(), result); + LOGGER.info("Review process successfully started on the participant for {}", instanceElement.elementId()); } private void reviewProcess(CompositionElementDto compositionElement, InstanceElementDto instanceElement) { @@ -424,6 +434,7 @@ public class ThreadHandler implements Closeable { cleanExecution(instanceElement.elementId(), messageId); var result = executor.submit(() -> this.prepareProcess(compositionElement, instanceElement, stage)); executionMap.put(instanceElement.elementId(), result); + LOGGER.info("Prepare process successfully started on the participant for {}", instanceElement.elementId()); } private void prepareProcess(CompositionElementDto compositionElement, InstanceElementDto instanceElement, @@ -457,6 +468,7 @@ public class ThreadHandler implements Closeable { this.rollbackProcess(compositionElement, compositionElementRollback, instanceElement, instanceElementRollback, stage)); executionMap.put(instanceElement.elementId(), result); + LOGGER.info("Rollback process successfully started on the participant for {}", instanceElement.elementId()); } private void rollbackProcess(CompositionElementDto compositionElement, diff --git a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/cache/CacheProvider.java b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/cache/CacheProvider.java index 19f66ab24..240cbe891 100644 --- a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/cache/CacheProvider.java +++ b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/cache/CacheProvider.java @@ -46,11 +46,15 @@ import org.onap.policy.clamp.models.acm.concepts.SubState; import org.onap.policy.models.base.PfUtils; import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier; import org.onap.policy.models.tosca.authorative.concepts.ToscaNodeTemplate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component public class CacheProvider { + private static final Logger LOGGER = LoggerFactory.getLogger(CacheProvider.class); + @Getter private final UUID participantId; @@ -131,6 +135,7 @@ public class CacheProvider { acDefinition.getElements().put(acElementDefinition.getAcElementDefinitionId(), acElementDefinition); } acElementsDefinitions.put(compositionId, acDefinition); + LOGGER.info("Updated cache for the composition id {}", compositionId); } public void removeElementDefinition(@NonNull UUID compositionId) { @@ -177,8 +182,9 @@ public class CacheProvider { */ public void initializeAutomationComposition(@NonNull UUID compositionId, @NonNull UUID instanceId, ParticipantDeploy participantDeploy, UUID revisionId) { - initializeAutomationComposition(compositionId, instanceId, participantDeploy, + initializeAutomationComposition(compositionId, null, instanceId, participantDeploy, DeployState.DEPLOYING, SubState.NONE, revisionId); + } /** @@ -191,31 +197,16 @@ public class CacheProvider { * @param subState the SubState * @param revisionId the identification of the last update */ - public void initializeAutomationComposition(@NonNull UUID compositionId, @NonNull UUID instanceId, + public void initializeAutomationComposition(@NonNull UUID compositionId, UUID compositionTargetId, + @NonNull UUID instanceId, ParticipantDeploy participantDeploy, DeployState deployState, SubState subState, UUID revisionId) { - var acLast = automationCompositions.get(instanceId); - Map acElementMap = new LinkedHashMap<>(); - for (var element : participantDeploy.getAcElementList()) { - var acElement = createAutomationCompositionElement(element); - acElement.setParticipantId(getParticipantId()); - acElement.setDeployState(deployState); - acElement.setSubState(subState); - var acElementLast = acLast != null ? acLast.getElements().get(element.getId()) : null; - if (acElementLast != null) { - acElement.setOutProperties(acElementLast.getOutProperties()); - acElement.setOperationalState(acElementLast.getOperationalState()); - acElement.setUseState(acElementLast.getUseState()); - } - acElementMap.put(element.getId(), acElement); - } - var automationComposition = new AutomationComposition(); - automationComposition.setCompositionId(compositionId); - automationComposition.setInstanceId(instanceId); - automationComposition.setElements(acElementMap); - automationComposition.setDeployState(deployState); - automationComposition.setSubState(subState); - automationComposition.setRevisionId(revisionId); + + var automationComposition = createAcInstance(compositionId, compositionTargetId, instanceId, participantDeploy, + deployState, subState, revisionId); + automationCompositions.put(instanceId, automationComposition); + LOGGER.info("Initialized participant cache for the {} operation of the instance {}", deployState, instanceId); + } /** @@ -242,9 +233,9 @@ public class CacheProvider { acElement.setUseState(element.getUseState()); acElement.setProperties(element.getProperties()); acElement.setOutProperties(element.getOutProperties()); + acElement.setMigrationState(element.getMigrationState()); acElementMap.put(element.getId(), acElement); } - var automationComposition = new AutomationComposition(); automationComposition.setCompositionId(compositionId); automationComposition.setCompositionTargetId(participantRestartAc.getCompositionTargetId()); @@ -255,6 +246,55 @@ public class CacheProvider { automationComposition.setStateChangeResult(participantRestartAc.getStateChangeResult()); automationComposition.setRevisionId(participantRestartAc.getRevisionId()); automationCompositions.put(automationComposition.getInstanceId(), automationComposition); + LOGGER.info("Updated participant cache for the instance id {}", + participantRestartAc.getAutomationCompositionId()); + } + + /** + * Create an AutomationComposition. + * @param compositionId compositionId + * @param compositionTargetId compositionTargetId + * @param instanceId instanceId + * @param participantDeploy participantDeploy + * @param deployState deployState + * @param subState subState + * @param revisionId revisionId + * @return AutomationComposition + */ + public AutomationComposition createAcInstance(@NonNull UUID compositionId, UUID compositionTargetId, + @NonNull UUID instanceId, ParticipantDeploy participantDeploy, + DeployState deployState, SubState subState, UUID revisionId) { + var acLast = automationCompositions.get(instanceId); + Map acElementMap = new LinkedHashMap<>(); + for (var element : participantDeploy.getAcElementList()) { + var acElement = createAutomationCompositionElement(element); + acElement.setParticipantId(getParticipantId()); + acElement.setDeployState(deployState); + acElement.setSubState(subState); + var acElementLast = acLast != null ? acLast.getElements().get(element.getId()) : null; + if (acElementLast != null) { + acElement.setOutProperties(acElementLast.getOutProperties()); + acElement.setOperationalState(acElementLast.getOperationalState()); + acElement.setUseState(acElementLast.getUseState()); + } + acElementMap.put(element.getId(), acElement); + } + var automationComposition = acLast != null ? acLast : new AutomationComposition(); + automationComposition.setCompositionId(compositionId); + automationComposition.setInstanceId(instanceId); + if (acLast != null) { + automationComposition.getElements().putAll(acElementMap); + } else { + automationComposition.setElements(acElementMap); + } + automationComposition.setDeployState(deployState); + automationComposition.setSubState(subState); + automationComposition.setRevisionId(revisionId); + if (compositionTargetId != null) { + automationComposition.setCompositionTargetId(compositionTargetId); + } + + return automationComposition; } /** @@ -270,6 +310,7 @@ public class CacheProvider { acElement.setProperties(element.getProperties()); acElement.setSubState(SubState.NONE); acElement.setLockState(LockState.LOCKED); + acElement.setMigrationState(element.getMigrationState()); return acElement; } @@ -303,7 +344,8 @@ public class CacheProvider { var acDefinition = acElementsDefinitions.get(compositionId); Map map = new HashMap<>(); for (var element : automationComposition.getElements().values()) { - var acDefinitionElement = acDefinition.getElements().get(element.getDefinition()); + var acDefinitionElement = (acDefinition != null) ? acDefinition.getElements().get(element.getDefinition()) : + null; var compositionElement = (acDefinitionElement != null) ? new CompositionElementDto(compositionId, element.getDefinition(), acDefinitionElement.getAutomationCompositionElementToscaNodeTemplate().getProperties(), diff --git a/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AutomationCompositionHandlerTest.java b/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AutomationCompositionHandlerTest.java index e7d6e36bf..1553d3075 100644 --- a/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AutomationCompositionHandlerTest.java +++ b/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AutomationCompositionHandlerTest.java @@ -40,8 +40,10 @@ import org.onap.policy.clamp.acm.participant.intermediary.handler.cache.CachePro import org.onap.policy.clamp.acm.participant.intermediary.main.parameters.CommonTestData; import org.onap.policy.clamp.models.acm.concepts.AcElementDeploy; import org.onap.policy.clamp.models.acm.concepts.AutomationComposition; +import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElement; import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElementDefinition; import org.onap.policy.clamp.models.acm.concepts.DeployState; +import org.onap.policy.clamp.models.acm.concepts.MigrationState; import org.onap.policy.clamp.models.acm.concepts.ParticipantDeploy; import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionDeploy; import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionDeployAck; @@ -272,7 +274,8 @@ class AutomationCompositionHandlerTest { void handleAutomationCompositionMigrationTest() { var automationComposition = CommonTestData.getTestAutomationCompositionMap().values().iterator().next(); automationComposition.setCompositionId(UUID.randomUUID()); - automationComposition.setInstanceId(UUID.randomUUID()); + var instanceId = UUID.randomUUID(); + automationComposition.setInstanceId(instanceId); automationComposition.setCompositionTargetId(UUID.randomUUID()); var definitions = CommonTestData.createAutomationCompositionElementDefinitionList(automationComposition); @@ -291,6 +294,14 @@ class AutomationCompositionHandlerTest { automationComposition.getElements().size(), false); testMigration(cacheProviderRollback, automationComposition, 0, automationComposition.getElements().size(), true); + + // New participant with new elements added in Migration + cacheProvider.removeAutomationComposition(instanceId); + for (var element : automationComposition.getElements().entrySet()) { + element.getValue().setMigrationState(MigrationState.NEW); + } + testMigration(cacheProvider, automationComposition, 0, + automationComposition.getElements().size(), false); } @Test @@ -302,10 +313,17 @@ class AutomationCompositionHandlerTest { var acMigrate = new AutomationComposition(automationComposition); acMigrate.setCompositionTargetId(UUID.randomUUID()); - // replacing first element with new one + // remove element var element = acMigrate.getElements().values().iterator().next(); - element.setDefinition(new ToscaConceptIdentifier("policy.clamp.new.element", "1.0.0")); - element.setId(UUID.randomUUID()); + element.setMigrationState(MigrationState.REMOVED); + + //Add element + var newElement = new AutomationCompositionElement(element); + newElement.setDefinition(new ToscaConceptIdentifier("policy.clamp.new.element", "1.2.4")); + newElement.setId(UUID.randomUUID()); + newElement.setMigrationState(MigrationState.NEW); + + acMigrate.getElements().put(newElement.getId(), newElement); var migrateDefinitions = CommonTestData.createAutomationCompositionElementDefinitionList(acMigrate); @@ -321,10 +339,11 @@ class AutomationCompositionHandlerTest { automationComposition.getInstanceId(), definitions, acMigrate.getCompositionTargetId(), migrateDefinitions); - testMigration(cacheProvider, acMigrate, 0, acMigrate.getElements().size() + 1, false); - testMigration(cacheProviderRollback, acMigrate, 0, acMigrate.getElements().size() + 1, true); + testMigration(cacheProvider, acMigrate, 0, acMigrate.getElements().size(), false); + testMigration(cacheProviderRollback, acMigrate, 0, acMigrate.getElements().size(), true); } + @Test void handleAcMigrationStageTest() { var automationComposition = CommonTestData.getTestAutomationCompositionMap().values().iterator().next(); @@ -334,10 +353,18 @@ class AutomationCompositionHandlerTest { var acMigrate = new AutomationComposition(automationComposition); acMigrate.setCompositionTargetId(UUID.randomUUID()); - // replacing first element with new one + // remove element var element = acMigrate.getElements().values().iterator().next(); - element.setDefinition(new ToscaConceptIdentifier("policy.clamp.new.element", "1.2.4")); - element.setId(UUID.randomUUID()); + element.setMigrationState(MigrationState.REMOVED); + + //Add element + var newElement = new AutomationCompositionElement(element); + newElement.setDefinition(new ToscaConceptIdentifier("policy.clamp.new.element", "1.2.4")); + newElement.setId(UUID.randomUUID()); + newElement.setMigrationState(MigrationState.NEW); + + acMigrate.getElements().put(newElement.getId(), newElement); + // replacing definition version acMigrate.getElements().values().forEach(el -> el.setDefinition( diff --git a/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/ThreadHandlerTest.java b/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/ThreadHandlerTest.java index 4ac0b43bf..f7950959c 100644 --- a/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/ThreadHandlerTest.java +++ b/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/ThreadHandlerTest.java @@ -86,13 +86,13 @@ class ThreadHandlerTest { var messageId = UUID.randomUUID(); threadHandler.prime(messageId, composition); verify(intermediaryApi, timeout(TIMEOUT)).updateCompositionState(compositionId, AcTypeState.COMMISSIONED, - StateChangeResult.FAILED, "Composition Defintion prime failed"); + StateChangeResult.FAILED, "Composition Definition prime failed"); clearInvocations(listener); doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener).deprime(composition); threadHandler.deprime(messageId, composition); verify(intermediaryApi, timeout(TIMEOUT)).updateCompositionState(compositionId, AcTypeState.PRIMED, - StateChangeResult.FAILED, "Composition Defintion deprime failed"); + StateChangeResult.FAILED, "Composition Definition deprime failed"); } } diff --git a/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/cache/CacheProviderTest.java b/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/cache/CacheProviderTest.java index badc33ec9..39d1b557d 100644 --- a/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/cache/CacheProviderTest.java +++ b/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/cache/CacheProviderTest.java @@ -67,10 +67,10 @@ class CacheProviderTest { var deployState = DeployState.DEPLOYED; var subState = SubState.NONE; - assertThatThrownBy(() -> cacheProvider.initializeAutomationComposition(null, instanceId, participantDeploy, - deployState, subState, null)).isInstanceOf(NullPointerException.class); - assertThatThrownBy(() -> cacheProvider.initializeAutomationComposition(instanceId, null, participantDeploy, - deployState, subState, null)).isInstanceOf(NullPointerException.class); + assertThatThrownBy(() -> cacheProvider.initializeAutomationComposition(null, instanceId, null, + participantDeploy, deployState, subState, null)).isInstanceOf(NullPointerException.class); + assertThatThrownBy(() -> cacheProvider.initializeAutomationComposition(instanceId, null, null, + participantDeploy, deployState, subState, null)).isInstanceOf(NullPointerException.class); } @Test diff --git a/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/main/parameters/CommonTestData.java b/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/main/parameters/CommonTestData.java index bcde4c5c6..edcb51590 100644 --- a/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/main/parameters/CommonTestData.java +++ b/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/main/parameters/CommonTestData.java @@ -1,6 +1,6 @@ /*- * ============LICENSE_START======================================================= - * Copyright (C) 2021-2024 Nordix Foundation. + * Copyright (C) 2021-2025 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -281,6 +281,7 @@ public class CommonTestData { acElement.setId(element.getId()); acElement.setDefinition(element.getDefinition()); acElement.setProperties(element.getProperties()); + acElement.setMigrationState(element.getMigrationState()); participantDeploy.getAcElementList().add(acElement); } return participantDeploy; diff --git a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/instantiation/AutomationCompositionInstantiationProvider.java b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/instantiation/AutomationCompositionInstantiationProvider.java index d79a84f40..83af86d43 100644 --- a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/instantiation/AutomationCompositionInstantiationProvider.java +++ b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/instantiation/AutomationCompositionInstantiationProvider.java @@ -39,6 +39,7 @@ import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElement; import org.onap.policy.clamp.models.acm.concepts.AutomationCompositions; import org.onap.policy.clamp.models.acm.concepts.DeployState; import org.onap.policy.clamp.models.acm.concepts.LockState; +import org.onap.policy.clamp.models.acm.concepts.MigrationState; import org.onap.policy.clamp.models.acm.concepts.ParticipantUtils; import org.onap.policy.clamp.models.acm.concepts.StateChangeResult; import org.onap.policy.clamp.models.acm.concepts.SubState; @@ -97,9 +98,11 @@ public class AutomationCompositionInstantiationProvider { var acDefinition = acDefinitionProvider.getAcDefinition(compositionId); AcDefinitionProvider.checkPrimedComposition(acDefinition); - var validationResult = validateAutomationComposition(automationComposition, acDefinition); + var validationResult = validateAutomationComposition(automationComposition, acDefinition, false); if (!validationResult.isValid()) { throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult()); + } else { + associateParticipantId(automationComposition, acDefinition, null); } encryptInstanceProperties(automationComposition, compositionId); automationComposition = automationCompositionProvider.createAutomationComposition(automationComposition); @@ -124,23 +127,25 @@ public class AutomationCompositionInstantiationProvider { public InstantiationResponse updateAutomationComposition(UUID compositionId, AutomationComposition automationComposition) { var instanceId = automationComposition.getInstanceId(); - var acToUpdate = automationCompositionProvider.getAutomationComposition(instanceId); - AutomationCompositionProvider.validateInstanceEndpoint(compositionId, acToUpdate); + var acFromDb = automationCompositionProvider.getAutomationComposition(instanceId); + AutomationCompositionProvider.validateInstanceEndpoint(compositionId, acFromDb); var acDefinition = acDefinitionProvider.getAcDefinition(compositionId); AcDefinitionProvider.checkPrimedComposition(acDefinition); - if (DeployState.UNDEPLOYED.equals(acToUpdate.getDeployState())) { + if (DeployState.UNDEPLOYED.equals(acFromDb.getDeployState())) { LOGGER.info("Updating undeployed instance with id {}", instanceId); - acToUpdate.setElements(automationComposition.getElements()); - acToUpdate.setName(automationComposition.getName()); - acToUpdate.setVersion(automationComposition.getVersion()); - acToUpdate.setDescription(automationComposition.getDescription()); - acToUpdate.setDerivedFrom(automationComposition.getDerivedFrom()); - var validationResult = validateAutomationComposition(acToUpdate, acDefinition); + acFromDb.setElements(automationComposition.getElements()); + acFromDb.setName(automationComposition.getName()); + acFromDb.setVersion(automationComposition.getVersion()); + acFromDb.setDescription(automationComposition.getDescription()); + acFromDb.setDerivedFrom(automationComposition.getDerivedFrom()); + var validationResult = validateAutomationComposition(acFromDb, acDefinition, false); if (!validationResult.isValid()) { throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult()); + } else { + associateParticipantId(acFromDb, acDefinition, null); } - encryptInstanceProperties(acToUpdate, compositionId); - automationComposition = automationCompositionProvider.updateAutomationComposition(acToUpdate); + encryptInstanceProperties(acFromDb, compositionId); + automationComposition = automationCompositionProvider.updateAutomationComposition(acFromDb); return createInstantiationResponse(automationComposition); } @@ -158,17 +163,17 @@ public class AutomationCompositionInstantiationProvider { } } var result = acInstanceStateResolver.resolve(deployOrder, LockOrder.NONE, subOrder, - acToUpdate.getDeployState(), acToUpdate.getLockState(), acToUpdate.getSubState(), - acToUpdate.getStateChangeResult()); + acFromDb.getDeployState(), acFromDb.getLockState(), acFromDb.getSubState(), + acFromDb.getStateChangeResult()); return switch (result) { - case "UPDATE" -> updateDeployedAutomationComposition(automationComposition, acToUpdate, acDefinition); + case "UPDATE" -> updateDeployedAutomationComposition(automationComposition, acFromDb, acDefinition); - case "MIGRATE" -> migrateAutomationComposition(automationComposition, acToUpdate, acDefinition); + case "MIGRATE" -> migrateAutomationComposition(automationComposition, acFromDb, acDefinition); - case "MIGRATE_PRECHECK" -> migratePrecheckAc(automationComposition, acToUpdate, acDefinition); + case "MIGRATE_PRECHECK" -> migratePrecheckAc(automationComposition, acFromDb, acDefinition); default -> throw new PfModelRuntimeException(Status.BAD_REQUEST, - "Not allowed to " + deployOrder + " in the state " + acToUpdate.getDeployState()); + "Not allowed to " + deployOrder + " in the state " + acFromDb.getDeployState()); }; } @@ -196,9 +201,11 @@ public class AutomationCompositionInstantiationProvider { AcmUtils.recursiveMerge(dbAcElement.getProperties(), element.getValue().getProperties()); } - var validationResult = validateAutomationComposition(acToBeUpdated, acDefinition); + var validationResult = validateAutomationComposition(acToBeUpdated, acDefinition, false); if (!validationResult.isValid()) { throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult()); + } else { + associateParticipantId(acToBeUpdated, acDefinition, null); } updateAcForProperties(acToBeUpdated); @@ -212,43 +219,39 @@ public class AutomationCompositionInstantiationProvider { } private InstantiationResponse migrateAutomationComposition( - AutomationComposition automationComposition, AutomationComposition acToBeUpdated, + AutomationComposition automationComposition, AutomationComposition acFromDb, AutomationCompositionDefinition acDefinition) { LOGGER.info("Migrating instance with id {}", automationComposition.getInstanceId()); - if (!DeployState.DEPLOYED.equals(acToBeUpdated.getDeployState())) { + if (!DeployState.DEPLOYED.equals(acFromDb.getDeployState())) { throw new PfModelRuntimeException(Status.BAD_REQUEST, - "Not allowed to migrate in the state " + acToBeUpdated.getDeployState()); + "Not allowed to migrate in the state " + acFromDb.getDeployState()); } // make copy for rollback - automationCompositionProvider.copyAcElementsBeforeUpdate(acToBeUpdated); + automationCompositionProvider.copyAcElementsBeforeUpdate(acFromDb); var acDefinitionTarget = acDefinitionProvider.getAcDefinition(automationComposition.getCompositionTargetId()); AcDefinitionProvider.checkPrimedComposition(acDefinitionTarget); // Iterate and update the element property values - var elementsRemoved = updateElementsProperties(automationComposition, acToBeUpdated, acDefinitionTarget); + updateElementsProperties(automationComposition, acFromDb, acDefinitionTarget, acDefinition); - updateAcForMigration(acToBeUpdated, acDefinitionTarget, DeployState.MIGRATING); + updateAcForMigration(acFromDb, acDefinitionTarget, DeployState.MIGRATING); - var acToPublish = new AutomationComposition(acToBeUpdated); - encryptInstanceProperties(acToBeUpdated, acToBeUpdated.getCompositionTargetId()); + var acToPublish = new AutomationComposition(acFromDb); + encryptInstanceProperties(acFromDb, acFromDb.getCompositionTargetId()); - var ac = automationCompositionProvider.updateAutomationComposition(acToBeUpdated); - for (var element : elementsRemoved) { - automationCompositionProvider.deleteAutomationCompositionElement(element.getId()); - } + var ac = automationCompositionProvider.updateAutomationComposition(acFromDb); // Publish migrate event to the participants - supervisionAcHandler.migrate(acToPublish, acDefinition.getRevisionId(), acDefinitionTarget.getRevisionId(), - elementsRemoved); + supervisionAcHandler.migrate(acToPublish, acDefinition.getRevisionId(), acDefinitionTarget.getRevisionId()); return createInstantiationResponse(ac); } - private void updateAcForMigration(AutomationComposition acToBeUpdated, + private void updateAcForMigration(AutomationComposition acFromDb, AutomationCompositionDefinition acDefinition, DeployState deployState) { - AcmUtils.setCascadedState(acToBeUpdated, deployState, LockState.LOCKED); - acToBeUpdated.setStateChangeResult(StateChangeResult.NO_ERROR); - var stage = ParticipantUtils.getFirstStage(acToBeUpdated, acDefinition.getServiceTemplate()); - acToBeUpdated.setPhase(stage); + AcmUtils.setCascadedState(acFromDb, deployState, LockState.LOCKED); + acFromDb.setStateChangeResult(StateChangeResult.NO_ERROR); + var stage = ParticipantUtils.getFirstStage(acFromDb, acDefinition.getServiceTemplate()); + acFromDb.setPhase(stage); } private void updateAcForProperties(AutomationComposition acToBeUpdated) { @@ -273,11 +276,10 @@ public class AutomationCompositionInstantiationProvider { var acDefinitionTarget = acDefinitionProvider.getAcDefinition(automationComposition.getCompositionTargetId()); AcDefinitionProvider.checkPrimedComposition(acDefinitionTarget); // Iterate and update the element property values - var removedElements = updateElementsProperties(automationComposition, copyAc, acDefinitionTarget); + updateElementsProperties(automationComposition, copyAc, acDefinitionTarget, acDefinition); // Publish migrate event to the participants - supervisionAcHandler.migratePrecheck(copyAc, acDefinition.getRevisionId(), acDefinitionTarget.getRevisionId(), - removedElements); + supervisionAcHandler.migratePrecheck(copyAc, acDefinition.getRevisionId(), acDefinitionTarget.getRevisionId()); AcmUtils.setCascadedState(acToBeUpdated, DeployState.DEPLOYED, LockState.LOCKED, SubState.MIGRATION_PRECHECKING); @@ -294,23 +296,15 @@ public class AutomationCompositionInstantiationProvider { * @return the result of validation */ private BeanValidationResult validateAutomationComposition(AutomationComposition automationComposition, - AutomationCompositionDefinition acDefinition) { + AutomationCompositionDefinition acDefinition, boolean migrateOperation) { var result = new BeanValidationResult("AutomationComposition", automationComposition); participantProvider.checkRegisteredParticipant(acDefinition); result.addResult(AcmUtils.validateAutomationComposition(automationComposition, acDefinition.getServiceTemplate(), - acRuntimeParameterGroup.getAcmParameters().getToscaCompositionName())); + acRuntimeParameterGroup.getAcmParameters().getToscaCompositionName(), migrateOperation)); result.addResult(automationCompositionProvider.validateElementIds(automationComposition)); - if (result.isValid()) { - for (var element : automationComposition.getElements().values()) { - var name = element.getDefinition().getName(); - var participantId = acDefinition.getElementStateMap().get(name).getParticipantId(); - element.setParticipantId(participantId); - } - } - return result; } @@ -443,49 +437,54 @@ public class AutomationCompositionInstantiationProvider { public void rollback(UUID compositionId, UUID instanceId) { LOGGER.info("Rollback automation composition request received for CompositionID: {} and InstanceID: {}", compositionId, instanceId); - var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId); - AutomationCompositionProvider.validateInstanceEndpoint(compositionId, automationComposition); + var acFromDb = automationCompositionProvider.getAutomationComposition(instanceId); + AutomationCompositionProvider.validateInstanceEndpoint(compositionId, acFromDb); if (!DeployOrder.MIGRATION_REVERT.name().equals(acInstanceStateResolver.resolve( DeployOrder.MIGRATION_REVERT, LockOrder.NONE, - SubOrder.NONE, automationComposition.getDeployState(), automationComposition.getLockState(), - automationComposition.getSubState(), automationComposition.getStateChangeResult()))) { + SubOrder.NONE, acFromDb.getDeployState(), acFromDb.getLockState(), + acFromDb.getSubState(), acFromDb.getStateChangeResult()))) { throw new PfModelRuntimeException(Status.BAD_REQUEST, "Invalid state for rollback"); } var automationCompositionToRollback = automationCompositionProvider.getAutomationCompositionRollback(instanceId); - var acToBeUpdated = new AutomationComposition(automationComposition); - acToBeUpdated.setCompositionTargetId(automationCompositionToRollback.getCompositionId()); - acToBeUpdated.setElements(automationCompositionToRollback.getElements().values().stream() + var acFromDbCopy = new AutomationComposition(acFromDb); + acFromDbCopy.setCompositionTargetId(automationCompositionToRollback.getCompositionId()); + acFromDbCopy.setElements(automationCompositionToRollback.getElements().values().stream() .collect(Collectors.toMap(AutomationCompositionElement::getId, AutomationCompositionElement::new))); - var acDefinitionTarget = acDefinitionProvider.getAcDefinition(acToBeUpdated.getCompositionTargetId()); - var validationResult = validateAutomationComposition(acToBeUpdated, acDefinitionTarget); + var acDefinitionTarget = acDefinitionProvider.getAcDefinition(acFromDbCopy.getCompositionTargetId()); + var validationResult = validateAutomationComposition(acFromDbCopy, acDefinitionTarget, true); if (!validationResult.isValid()) { throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult()); } - - updateAcForMigration(acToBeUpdated, acDefinitionTarget, DeployState.MIGRATION_REVERTING); - var elementsRemoved = getElementRemoved(automationComposition, acToBeUpdated); - automationCompositionProvider.updateAutomationComposition(acToBeUpdated); - for (var element : elementsRemoved) { - automationCompositionProvider.deleteAutomationCompositionElement(element.getId()); + // Include new elements from migration for the participant undeploy + for (var element : acFromDb.getElements().values()) { + if (MigrationState.NEW.equals(element.getMigrationState())) { + acFromDbCopy.getElements().put(element.getId(), element); + } + if (MigrationState.REMOVED.equals(element.getMigrationState())) { + acFromDbCopy.getElements().get(element.getId()).setMigrationState(MigrationState.REMOVED); + } } - var acDefinition = acDefinitionProvider.getAcDefinition(acToBeUpdated.getCompositionId()); - supervisionAcHandler.migrate(acToBeUpdated, acDefinition.getRevisionId(), acDefinitionTarget.getRevisionId(), - elementsRemoved); + + updateAcForMigration(acFromDbCopy, acDefinitionTarget, DeployState.MIGRATION_REVERTING); + automationCompositionProvider.updateAutomationComposition(acFromDbCopy); + var acDefinition = acDefinitionProvider.getAcDefinition(acFromDbCopy.getCompositionId()); + supervisionAcHandler.migrate(acFromDbCopy, acDefinition.getRevisionId(), acDefinitionTarget.getRevisionId()); } - private List updateElementsProperties(AutomationComposition automationComposition, - AutomationComposition acToBeUpdated, AutomationCompositionDefinition acDefinitionTarget) { + private void updateElementsProperties(AutomationComposition automationComposition, + AutomationComposition acFromDb, AutomationCompositionDefinition acDefinitionTarget, + AutomationCompositionDefinition acDefinition) { for (var element : automationComposition.getElements().entrySet()) { var elementId = element.getKey(); - var dbAcElement = acToBeUpdated.getElements().get(elementId); - // Add additional elements if present for migration + var dbAcElement = acFromDb.getElements().get(elementId); if (dbAcElement == null) { LOGGER.info("New Ac element {} added in Migration", elementId); - acToBeUpdated.getElements().put(elementId, element.getValue()); + element.getValue().setMigrationState(MigrationState.NEW); + acFromDb.getElements().put(elementId, element.getValue()); } else { AcmUtils.recursiveMerge(dbAcElement.getProperties(), element.getValue().getProperties()); var newDefinition = element.getValue().getDefinition().asConceptKey(); @@ -495,16 +494,34 @@ public class AutomationCompositionInstantiationProvider { dbAcElement.setDefinition(element.getValue().getDefinition()); } } - // Remove elements which are not present in the new Ac instance - var elementsRemoved = getElementRemoved(acToBeUpdated, automationComposition); - elementsRemoved.forEach(element -> acToBeUpdated.getElements().remove(element.getId())); + // Update migrationState for the removed elements + var elementsRemoved = getElementRemoved(acFromDb, automationComposition); + elementsRemoved.forEach(element -> acFromDb.getElements().get(element.getId()) + .setMigrationState(MigrationState.REMOVED)); - var validationResult = validateAutomationComposition(acToBeUpdated, acDefinitionTarget); + var validationResult = validateAutomationComposition(acFromDb, acDefinitionTarget, true); if (!validationResult.isValid()) { throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult()); + } else { + associateParticipantId(acFromDb, acDefinitionTarget, acDefinition); + } + acFromDb.setCompositionTargetId(automationComposition.getCompositionTargetId()); + } + + private void associateParticipantId(AutomationComposition acFromDb, + AutomationCompositionDefinition acDefinitionTarget, + AutomationCompositionDefinition oldAcDefinition) { + for (var element : acFromDb.getElements().values()) { + var name = element.getDefinition().getName(); + var migrationState = element.getMigrationState(); + if (MigrationState.REMOVED.equals(migrationState) && oldAcDefinition != null) { + var participantId = oldAcDefinition.getElementStateMap().get(name).getParticipantId(); + element.setParticipantId(participantId); + } else { + var participantId = acDefinitionTarget.getElementStateMap().get(name).getParticipantId(); + element.setParticipantId(participantId); + } } - acToBeUpdated.setCompositionTargetId(automationComposition.getCompositionTargetId()); - return elementsRemoved; } private AutomationCompositionDefinition getAcDefinition(UUID compositionId, diff --git a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionAcHandler.java b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionAcHandler.java index a7defea00..a979bf874 100644 --- a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionAcHandler.java +++ b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionAcHandler.java @@ -24,7 +24,6 @@ import io.micrometer.core.annotation.Timed; import io.opentelemetry.context.Context; import jakarta.ws.rs.core.Response; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.UUID; import java.util.concurrent.ExecutorService; @@ -40,7 +39,6 @@ import org.onap.policy.clamp.common.acm.utils.AcmThreadFactory; import org.onap.policy.clamp.models.acm.concepts.AcElementDeployAck; import org.onap.policy.clamp.models.acm.concepts.AutomationComposition; import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionDefinition; -import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElement; import org.onap.policy.clamp.models.acm.concepts.DeployState; import org.onap.policy.clamp.models.acm.concepts.LockState; import org.onap.policy.clamp.models.acm.concepts.MigrationState; @@ -147,7 +145,6 @@ public class SupervisionAcHandler { AcmUtils.setCascadedState(automationComposition, DeployState.UNDEPLOYING, LockState.NONE); } automationComposition.setStateChangeResult(StateChangeResult.NO_ERROR); - automationComposition.setCompositionTargetId(null); var startPhase = ParticipantUtils.getFirstStartPhase(automationComposition, acDefinition.getServiceTemplate()); automationComposition.setPhase(startPhase); automationCompositionProvider.updateAutomationComposition(automationComposition); @@ -368,11 +365,11 @@ public class SupervisionAcHandler { * @param revisionIdCompositionTarget the last Update from Composition Target */ public void migrate(AutomationComposition automationComposition, UUID revisionIdComposition, - UUID revisionIdCompositionTarget, List removedElements) { + UUID revisionIdCompositionTarget) { executor.execute(() -> { encryptionUtils.decryptInstanceProperties(automationComposition); acCompositionMigrationPublisher.send(automationComposition, automationComposition.getPhase(), - revisionIdComposition, revisionIdCompositionTarget, removedElements); + revisionIdComposition, revisionIdCompositionTarget); }); } @@ -384,8 +381,8 @@ public class SupervisionAcHandler { * @param revisionIdCompositionTarget the last Update from Composition Target */ public void migratePrecheck(AutomationComposition automationComposition, UUID revisionIdComposition, - UUID revisionIdCompositionTarget, List removedElements) { + UUID revisionIdCompositionTarget) { executor.execute(() -> acCompositionMigrationPublisher.send(automationComposition, 0, - revisionIdComposition, revisionIdCompositionTarget, removedElements)); + revisionIdComposition, revisionIdCompositionTarget)); } } diff --git a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/comm/AcElementPropertiesPublisher.java b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/comm/AcElementPropertiesPublisher.java index 3f7704f0b..68a70fa01 100644 --- a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/comm/AcElementPropertiesPublisher.java +++ b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/comm/AcElementPropertiesPublisher.java @@ -22,7 +22,6 @@ package org.onap.policy.clamp.acm.runtime.supervision.comm; import io.micrometer.core.annotation.Timed; import java.time.Instant; -import java.util.List; import java.util.UUID; import java.util.stream.Collectors; import lombok.AllArgsConstructor; @@ -59,8 +58,7 @@ public class AcElementPropertiesPublisher extends AbstractParticipantPublisher

removedElements) { + UUID revisionIdCompositionTarget) { var acMigration = new AutomationCompositionMigration(); acMigration.setRollback(DeployState.MIGRATION_REVERTING.equals(automationComposition.getDeployState())); acMigration.setPrecheck(Boolean.TRUE.equals(automationComposition.getPrecheck())); @@ -61,8 +59,7 @@ public class AutomationCompositionMigrationPublisher acMigration.setRevisionIdInstance(automationComposition.getRevisionId()); acMigration.setRevisionIdComposition(revisionIdComposition); acMigration.setRevisionIdCompositionTarget(revisionIdCompositionTarget); - var participantUpdatesList = AcmUtils.createParticipantDeployList(automationComposition, DeployOrder.MIGRATE, - removedElements); + var participantUpdatesList = AcmUtils.createParticipantDeployList(automationComposition, DeployOrder.MIGRATE); acMigration.setParticipantUpdatesList(participantUpdatesList); acMigration.setParticipantIdList(participantUpdatesList.stream() .map(ParticipantDeploy::getParticipantId).collect(Collectors.toSet())); diff --git a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/AbstractScanner.java b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/AbstractScanner.java index 27a05f3f8..5ad8ea9a4 100644 --- a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/AbstractScanner.java +++ b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/AbstractScanner.java @@ -26,6 +26,7 @@ import org.onap.policy.clamp.acm.runtime.main.utils.EncryptionUtils; import org.onap.policy.clamp.acm.runtime.supervision.comm.ParticipantSyncPublisher; import org.onap.policy.clamp.models.acm.concepts.AutomationComposition; import org.onap.policy.clamp.models.acm.concepts.DeployState; +import org.onap.policy.clamp.models.acm.concepts.MigrationState; import org.onap.policy.clamp.models.acm.concepts.ParticipantUtils; import org.onap.policy.clamp.models.acm.concepts.StateChangeResult; import org.onap.policy.clamp.models.acm.concepts.SubState; @@ -65,6 +66,12 @@ public abstract class AbstractScanner { // migration scenario automationComposition.setCompositionId(automationComposition.getCompositionTargetId()); automationComposition.setCompositionTargetId(null); + + for (var acElement : automationComposition.getElements().values()) { + if (MigrationState.NEW.equals(acElement.getMigrationState())) { + acElement.setMigrationState(MigrationState.DEFAULT); + } + } } automationComposition.setDeployState(AcmUtils.deployCompleted(deployState)); automationComposition.setLockState(AcmUtils.lockCompleted(deployState, automationComposition.getLockState())); diff --git a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/SimpleScanner.java b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/SimpleScanner.java index 8785b5958..226d39a17 100644 --- a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/SimpleScanner.java +++ b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/SimpleScanner.java @@ -75,15 +75,8 @@ public class SimpleScanner extends AbstractScanner { private UpdateSync handleAcStateChange(AutomationComposition automationComposition, DocMessage message) { var result = new UpdateSync(); var element = automationComposition.getElements().get(message.getInstanceElementId()); - if (element == null && isMigration(automationComposition) - && StateChangeResult.FAILED.equals(message.getStateChangeResult())) { - // fail delete element during migration - automationComposition.setStateChangeResult(StateChangeResult.FAILED); - result.setUpdated(true); - result.setToBeSync(true); - return result; - } - if (element == null || !validateStateMessage(automationComposition, message)) { + if (element == null) { + LOGGER.warn("Not a valid message, element is null for the element id {}", message.getInstanceElementId()); return result; } result.setUpdated(true); @@ -101,16 +94,6 @@ public class SimpleScanner extends AbstractScanner { return result; } - private boolean isMigration(AutomationComposition automationComposition) { - return DeployState.MIGRATING.equals(automationComposition.getDeployState()) - || DeployState.MIGRATION_REVERTING.equals(automationComposition.getDeployState()); - } - - private boolean validateStateMessage(AutomationComposition automationComposition, DocMessage message) { - return !DeployState.DELETED.equals(message.getDeployState()) - || (DeployState.DELETING.equals(automationComposition.getDeployState())); - } - private UpdateSync handleOutProperties(AutomationComposition automationComposition, DocMessage message) { var element = automationComposition.getElements().get(message.getInstanceElementId()); var result = new UpdateSync(); diff --git a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/StageScanner.java b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/StageScanner.java index 777130f94..9b79c1b30 100644 --- a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/StageScanner.java +++ b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/StageScanner.java @@ -20,8 +20,11 @@ package org.onap.policy.clamp.acm.runtime.supervision.scanner; +import java.util.ArrayList; import java.util.Comparator; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.UUID; import org.onap.policy.clamp.acm.runtime.main.parameters.AcRuntimeParameterGroup; import org.onap.policy.clamp.acm.runtime.main.utils.EncryptionUtils; @@ -30,8 +33,11 @@ import org.onap.policy.clamp.acm.runtime.supervision.comm.AutomationCompositionM import org.onap.policy.clamp.acm.runtime.supervision.comm.ParticipantSyncPublisher; import org.onap.policy.clamp.models.acm.concepts.AutomationComposition; import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionDefinition; +import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElement; import org.onap.policy.clamp.models.acm.concepts.DeployState; +import org.onap.policy.clamp.models.acm.concepts.MigrationState; import org.onap.policy.clamp.models.acm.concepts.ParticipantUtils; +import org.onap.policy.clamp.models.acm.concepts.StateChangeResult; import org.onap.policy.clamp.models.acm.concepts.SubState; import org.onap.policy.clamp.models.acm.persistence.provider.AutomationCompositionProvider; import org.onap.policy.clamp.models.acm.utils.AcmUtils; @@ -76,39 +82,69 @@ public class StageScanner extends AbstractScanner { AutomationCompositionDefinition acDefinition, UpdateSync updateSync, UUID revisionIdComposition) { var completed = true; var minStageNotCompleted = 1000; // min stage not completed + List elementsDeleted = new ArrayList<>(); for (var element : automationComposition.getElements().values()) { if (AcmUtils.isInTransitionalState(element.getDeployState(), element.getLockState(), element.getSubState())) { - var toscaNodeTemplate = acDefinition.getServiceTemplate().getToscaTopologyTemplate().getNodeTemplates() - .get(element.getDefinition().getName()); - var stageSet = DeployState.MIGRATING.equals(automationComposition.getDeployState()) - || DeployState.MIGRATION_REVERTING.equals(automationComposition.getDeployState()) - ? ParticipantUtils.findStageSetMigrate(toscaNodeTemplate.getProperties()) - : ParticipantUtils.findStageSetPrepare(toscaNodeTemplate.getProperties()); - var minStage = stageSet.stream().min(Comparator.comparing(Integer::valueOf)).orElse(0); + var minStage = calculateMinStage(element, automationComposition, acDefinition); int stage = element.getStage() != null ? element.getStage() : minStage; minStageNotCompleted = Math.min(minStageNotCompleted, stage); completed = false; + } else if (DeployState.DELETED.equals(element.getDeployState()) + && automationComposition.getStateChangeResult().equals(StateChangeResult.NO_ERROR)) { + // Migration with successful removal of element + elementsDeleted.add(element.getId()); } } - + removeDeletedElements(automationComposition, elementsDeleted, updateSync); if (completed) { complete(automationComposition, updateSync); } else { - LOGGER.debug("automation composition scan: transition from state {} to {} not completed", - automationComposition.getDeployState(), automationComposition.getLockState()); + processNextStage(automationComposition, updateSync, minStageNotCompleted, revisionIdComposition, + acDefinition); + } + } - if (minStageNotCompleted != automationComposition.getPhase()) { - savePhase(automationComposition, minStageNotCompleted); - updateSync.setUpdated(true); - saveAndSync(automationComposition, updateSync); - LOGGER.debug("retry message AutomationCompositionMigration"); - var acToSend = new AutomationComposition(automationComposition); - decryptInstanceProperties(acToSend); - sendNextStage(acToSend, minStageNotCompleted, revisionIdComposition, acDefinition); - } else { - handleTimeout(automationComposition, updateSync); - } + private void processNextStage(AutomationComposition automationComposition, UpdateSync updateSync, + int minStageNotCompleted, UUID revisionIdComposition, + AutomationCompositionDefinition acDefinition) { + LOGGER.debug("automation composition scan: transition from state {} to {} not completed", + automationComposition.getDeployState(), automationComposition.getLockState()); + + if (minStageNotCompleted != automationComposition.getPhase()) { + savePhase(automationComposition, minStageNotCompleted); + updateSync.setUpdated(true); + saveAndSync(automationComposition, updateSync); + LOGGER.debug("retry message AutomationCompositionMigration"); + var acToSend = new AutomationComposition(automationComposition); + decryptInstanceProperties(acToSend); + sendNextStage(acToSend, minStageNotCompleted, revisionIdComposition, acDefinition); + } else { + handleTimeout(automationComposition, updateSync); + } + } + + private Integer calculateMinStage(AutomationCompositionElement element, AutomationComposition automationComposition, + AutomationCompositionDefinition acDefinition) { + Set stageSet = new HashSet<>(); + if (! MigrationState.REMOVED.equals(element.getMigrationState())) { + var toscaNodeTemplate = acDefinition.getServiceTemplate().getToscaTopologyTemplate().getNodeTemplates() + .get(element.getDefinition().getName()); + stageSet = DeployState.MIGRATING.equals(automationComposition.getDeployState()) + || DeployState.MIGRATION_REVERTING.equals(automationComposition.getDeployState()) + ? ParticipantUtils.findStageSetMigrate(toscaNodeTemplate.getProperties()) + : ParticipantUtils.findStageSetPrepare(toscaNodeTemplate.getProperties()); + } + return stageSet.stream().min(Comparator.comparing(Integer::valueOf)).orElse(0); + } + + private void removeDeletedElements(AutomationComposition automationComposition, List elementsDeleted, + UpdateSync updateSync) { + for (var elementId : elementsDeleted) { + LOGGER.info("Deleting element {} in Migration ", elementId); + automationComposition.getElements().remove(elementId); + acProvider.deleteAutomationCompositionElement(elementId); + updateSync.setUpdated(true); } } @@ -119,7 +155,7 @@ public class StageScanner extends AbstractScanner { LOGGER.debug("retry message AutomationCompositionMigration"); // acDefinition for migration is the Composition target acMigrationPublisher.send(automationComposition, minStageNotCompleted, revisionIdComposition, - acDefinition.getRevisionId(), List.of()); + acDefinition.getRevisionId()); } if (SubState.PREPARING.equals(automationComposition.getSubState())) { LOGGER.debug("retry message AutomationCompositionPrepare"); diff --git a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/instantiation/AutomationCompositionInstantiationProviderTest.java b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/instantiation/AutomationCompositionInstantiationProviderTest.java index 6179cfbb8..ff0f9b005 100644 --- a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/instantiation/AutomationCompositionInstantiationProviderTest.java +++ b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/instantiation/AutomationCompositionInstantiationProviderTest.java @@ -311,7 +311,7 @@ class AutomationCompositionInstantiationProviderTest { automationCompositionTarget.setPrecheck(true); var preCheckResponse = instantiationProvider.updateAutomationComposition(compositionId, automationCompositionTarget); - verify(supervisionAcHandler).migratePrecheck(any(), any(), any(), any()); + verify(supervisionAcHandler).migratePrecheck(any(), any(), any()); InstantiationUtils.assertInstantiationResponse(preCheckResponse, automationCompositionTarget); automationCompositionTarget.setPrecheck(false); @@ -320,7 +320,7 @@ class AutomationCompositionInstantiationProviderTest { var instantiationResponse = instantiationProvider.updateAutomationComposition(compositionId, automationCompositionTarget); - verify(supervisionAcHandler).migrate(any(), any(), any(), any()); + verify(supervisionAcHandler).migrate(any(), any(), any()); InstantiationUtils.assertInstantiationResponse(instantiationResponse, automationCompositionTarget); } @@ -366,7 +366,7 @@ class AutomationCompositionInstantiationProviderTest { var instantiationResponse = instantiationProvider .updateAutomationComposition(automationComposition.getCompositionId(), automationComposition); - verify(supervisionAcHandler).migrate(any(), any(), any(), any()); + verify(supervisionAcHandler).migrate(any(), any(), any()); verify(acProvider).updateAutomationComposition(automationComposition); InstantiationUtils.assertInstantiationResponse(instantiationResponse, automationComposition); } @@ -405,7 +405,7 @@ class AutomationCompositionInstantiationProviderTest { var instantiationResponse = instantiationProvider .updateAutomationComposition(automationComposition.getCompositionId(), automationComposition); - verify(supervisionAcHandler).migratePrecheck(any(), any(), any(), any()); + verify(supervisionAcHandler).migratePrecheck(any(), any(), any()); verify(acProvider).updateAutomationComposition(automationComposition); InstantiationUtils.assertInstantiationResponse(instantiationResponse, automationComposition); diff --git a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/instantiation/InstantiationUtils.java b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/instantiation/InstantiationUtils.java index 564723b7c..8fa9498cf 100644 --- a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/instantiation/InstantiationUtils.java +++ b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/instantiation/InstantiationUtils.java @@ -1,6 +1,6 @@ /*- * ============LICENSE_START======================================================= - * Copyright (C) 2021-2024 Nordix Foundation. + * Copyright (C) 2021-2025 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,8 +25,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; -import java.util.ArrayList; -import java.util.UUID; import org.onap.policy.clamp.models.acm.concepts.AutomationComposition; import org.onap.policy.clamp.models.acm.messages.rest.instantiation.InstantiationResponse; import org.onap.policy.common.utils.coder.Coder; @@ -57,13 +55,6 @@ public class InstantiationUtils { // add suffix to name automationComposition.setName(automationComposition.getName() + suffix); - var elements = new ArrayList<>(automationComposition.getElements().values()); - automationComposition.getElements().clear(); - for (var element : elements) { - // set unique UUID to the element - element.setId(UUID.randomUUID()); - automationComposition.getElements().put(element.getId(), element); - } return automationComposition; } catch (CoderException e) { fail("Cannot read or decode " + path); diff --git a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionAcHandlerTest.java b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionAcHandlerTest.java index 3f0146486..b9c5f31f6 100644 --- a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionAcHandlerTest.java +++ b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionAcHandlerTest.java @@ -33,7 +33,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.onap.policy.clamp.acm.runtime.util.CommonTestData.TOSCA_SERVICE_TEMPLATE_YAML; -import java.util.List; import java.util.Map; import java.util.Optional; import java.util.UUID; @@ -539,9 +538,9 @@ class SupervisionAcHandlerTest { InstantiationUtils.getAutomationCompositionFromResource(AC_INSTANTIATION_CREATE_JSON, "Migrate"); assert automationComposition != null; automationComposition.setPhase(0); - handler.migrate(automationComposition, UUID.randomUUID(), UUID.randomUUID(), List.of()); + handler.migrate(automationComposition, UUID.randomUUID(), UUID.randomUUID()); verify(acCompositionMigrationPublisher, timeout(1000)) - .send(any(AutomationComposition.class), anyInt(), any(UUID.class), any(UUID.class), any()); + .send(any(AutomationComposition.class), anyInt(), any(UUID.class), any(UUID.class)); } @Test @@ -554,9 +553,9 @@ class SupervisionAcHandlerTest { mock(AcPreparePublisher.class), mock(MessageProvider.class), mock(EncryptionUtils.class)); var automationComposition = InstantiationUtils.getAutomationCompositionFromResource(AC_INSTANTIATION_CREATE_JSON, "Migrate"); - handler.migratePrecheck(automationComposition, UUID.randomUUID(), UUID.randomUUID(), List.of()); + handler.migratePrecheck(automationComposition, UUID.randomUUID(), UUID.randomUUID()); verify(acCompositionMigrationPublisher, timeout(1000)) - .send(any(AutomationComposition.class), anyInt(), any(UUID.class), any(UUID.class), any()); + .send(any(AutomationComposition.class), anyInt(), any(UUID.class), any(UUID.class)); } @Test diff --git a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionScannerTest.java b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionScannerTest.java index ca65a638c..4c3473da3 100644 --- a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionScannerTest.java +++ b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionScannerTest.java @@ -20,6 +20,7 @@ package org.onap.policy.clamp.acm.runtime.supervision; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.mock; @@ -28,6 +29,8 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.onap.policy.clamp.acm.runtime.util.CommonTestData.TOSCA_SERVICE_TEMPLATE_YAML; +import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -37,17 +40,23 @@ import java.util.Set; import java.util.UUID; import org.junit.jupiter.api.Test; import org.onap.policy.clamp.acm.runtime.instantiation.InstantiationUtils; +import org.onap.policy.clamp.acm.runtime.main.utils.EncryptionUtils; +import org.onap.policy.clamp.acm.runtime.supervision.comm.AcPreparePublisher; +import org.onap.policy.clamp.acm.runtime.supervision.comm.AutomationCompositionMigrationPublisher; +import org.onap.policy.clamp.acm.runtime.supervision.comm.ParticipantSyncPublisher; import org.onap.policy.clamp.acm.runtime.supervision.scanner.AcDefinitionScanner; import org.onap.policy.clamp.acm.runtime.supervision.scanner.MonitoringScanner; import org.onap.policy.clamp.acm.runtime.supervision.scanner.PhaseScanner; import org.onap.policy.clamp.acm.runtime.supervision.scanner.SimpleScanner; import org.onap.policy.clamp.acm.runtime.supervision.scanner.StageScanner; import org.onap.policy.clamp.acm.runtime.supervision.scanner.UpdateSync; +import org.onap.policy.clamp.acm.runtime.util.CommonTestData; import org.onap.policy.clamp.models.acm.concepts.AcTypeState; import org.onap.policy.clamp.models.acm.concepts.AutomationComposition; import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionDefinition; import org.onap.policy.clamp.models.acm.concepts.DeployState; import org.onap.policy.clamp.models.acm.concepts.LockState; +import org.onap.policy.clamp.models.acm.concepts.MigrationState; import org.onap.policy.clamp.models.acm.concepts.NodeTemplateState; import org.onap.policy.clamp.models.acm.concepts.StateChangeResult; import org.onap.policy.clamp.models.acm.concepts.SubState; @@ -123,6 +132,76 @@ class SupervisionScannerTest { verify(messageProvider).removeJob(JOB_ID); } + @Test + void testAcInstanceForMigrationSuccess() { + var automationComposition = InstantiationUtils.getAutomationCompositionFromResource(AC_JSON, "Crud"); + automationComposition.setDeployState(DeployState.MIGRATING); + automationComposition.setInstanceId(INSTANCE_ID); + automationComposition.setCompositionId(COMPOSITION_ID); + var compositionTargetId = UUID.randomUUID(); + automationComposition.setCompositionTargetId(compositionTargetId); + automationComposition.setLockState(LockState.LOCKED); + automationComposition.setLastMsg(TimestampHelper.now()); + automationComposition.setPhase(0); + List elementIds = new ArrayList<>(automationComposition.getElements().keySet()); + + Map migrationStateMap = new HashMap<>(); + List states = List.of(MigrationState.REMOVED, MigrationState.NEW, MigrationState.DEFAULT); + + for (int i = 0; i < elementIds.size(); i++) { + migrationStateMap.put(elementIds.get(i), states.get(i)); + } + + for (var entry : automationComposition.getElements().entrySet()) { + entry.getValue().setMigrationState(migrationStateMap.get(entry.getKey())); + } + + for (var element : automationComposition.getElements().values()) { + if (MigrationState.REMOVED.equals(migrationStateMap.get(element.getId()))) { + element.setDeployState(DeployState.DELETED); + element.setLockState(LockState.LOCKED); + } else { + element.setDeployState(DeployState.DEPLOYED); + element.setLockState(LockState.LOCKED); + } + } + + var automationCompositionProvider = mock(AutomationCompositionProvider.class); + Set set = new HashSet<>(); + set.add(automationComposition.getInstanceId()); + when(automationCompositionProvider.getAcInstancesInTransition()).thenReturn(set); + when(automationCompositionProvider.findAutomationComposition(automationComposition.getInstanceId())) + .thenReturn(Optional.of(automationComposition)); + + var acDefinitionTarget = createAutomationCompositionDefinition(AcTypeState.PRIMED); + acDefinitionTarget.setCompositionId(compositionTargetId); + var acDefinitionProvider = createAcDefinitionProvider(AcTypeState.PRIMED); + when(acDefinitionProvider.getAcDefinition(compositionTargetId)).thenReturn(acDefinitionTarget); + var acDefinition = new AutomationCompositionDefinition(); + acDefinition.setCompositionId(COMPOSITION_ID); + when(acDefinitionProvider.getAcDefinition(COMPOSITION_ID)).thenReturn(acDefinition); + var acRuntimeParameterGroup = CommonTestData.geParameterGroup("dbScanner"); + var stageScanner = new StageScanner(automationCompositionProvider, mock(ParticipantSyncPublisher.class), + mock(AutomationCompositionMigrationPublisher.class), mock(AcPreparePublisher.class), + acRuntimeParameterGroup, mock(EncryptionUtils.class)); + + var messageProvider = mock(MessageProvider.class); + when(messageProvider.createJob(automationComposition.getInstanceId())).thenReturn(Optional.of(JOB_ID)); + var monitoringScanner = new MonitoringScanner(automationCompositionProvider, acDefinitionProvider, + mock(AcDefinitionScanner.class), stageScanner, mock(SimpleScanner.class), mock(PhaseScanner.class), + messageProvider); + var supervisionScanner = new SupervisionScanner(automationCompositionProvider, acDefinitionProvider, + messageProvider, monitoringScanner); + + supervisionScanner.run(); + assertEquals(2, automationComposition.getElements().size()); + assertEquals(DeployState.DEPLOYED, automationComposition.getDeployState()); + for (var entry : automationComposition.getElements().entrySet()) { + assertEquals(MigrationState.DEFAULT, entry.getValue().getMigrationState()); + } + verify(messageProvider).removeJob(JOB_ID); + } + @Test void testAcDefinitionJobExist() { var acDefinitionProvider = createAcDefinitionProvider(AcTypeState.PRIMING); diff --git a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/comm/SupervisionMessagesTest.java b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/comm/SupervisionMessagesTest.java index 54c3ded96..84b80ae56 100644 --- a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/comm/SupervisionMessagesTest.java +++ b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/comm/SupervisionMessagesTest.java @@ -235,7 +235,7 @@ class SupervisionMessagesTest { publisher.active(topicSink); var automationComposition = InstantiationUtils.getAutomationCompositionFromResource(AC_INSTANTIATION_UPDATE_JSON, "Crud"); - publisher.send(automationComposition, 0, UUID.randomUUID(), UUID.randomUUID(), List.of()); + publisher.send(automationComposition, 0, UUID.randomUUID(), UUID.randomUUID()); verify(topicSink).send(anyString()); } diff --git a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/SimpleScannerTest.java b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/SimpleScannerTest.java index e8a025440..8e555795d 100644 --- a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/SimpleScannerTest.java +++ b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/SimpleScannerTest.java @@ -119,12 +119,12 @@ class SimpleScannerTest { assertFalse(result.isUpdated()); assertFalse(result.isToBeSync()); - // wrong Delete State + // Delete response from participant docMessage.setMessageType(ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK); docMessage.setInstanceElementId(elementId); docMessage.setDeployState(DeployState.DELETED); result = simpleScanner.scanMessage(automationComposition, docMessage); - assertFalse(result.isUpdated()); + assertTrue(result.isUpdated()); assertFalse(result.isToBeSync()); } @@ -270,7 +270,7 @@ class SimpleScannerTest { @Test void testScanMessageMigrationFail() { var automationComposition = createAutomationComposition(DeployState.MIGRATING, LockState.LOCKED); - var elementId = UUID.randomUUID(); + var elementId = automationComposition.getElements().entrySet().iterator().next().getKey(); var docMessage = new DocMessage(); docMessage.setMessageType(ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK); docMessage.setStateChangeResult(StateChangeResult.FAILED);