From 270908eebbdbd369c0d73a1b6ce69d7f814000a4 Mon Sep 17 00:00:00 2001 From: FrancescoFioraEst Date: Thu, 27 Nov 2025 14:20:07 +0000 Subject: [PATCH] Add MigrationState validation for migration Add MigrationState validation for migration. Add set DEFAULT in rollback completed for REMOVED elements. Issue-ID: POLICY-5515 Change-Id: Ib77b7bac5d78224d21e4ad15157118107ca88f14 Signed-off-by: FrancescoFioraEst --- .../onap/policy/clamp/models/acm/utils/AcmUtils.java | 20 ++++++++++++++++++++ .../policy/clamp/models/acm/utils/AcmUtilsTest.java | 12 ++++++++++++ .../AutomationCompositionInstantiationProvider.java | 1 + .../runtime/supervision/SupervisionAcHandler.java | 17 +++-------------- .../runtime/supervision/scanner/AbstractScanner.java | 3 ++- ...tomationCompositionInstantiationProviderTest.java | 3 +++ .../supervision/scanner/StageScannerTest.java | 3 +++ 7 files changed, 44 insertions(+), 15 deletions(-) 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 1a81fab96..e92c9b507 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 @@ -71,6 +71,9 @@ import org.slf4j.LoggerFactory; @NoArgsConstructor(access = AccessLevel.PRIVATE) public final class AcmUtils { public static final String ENTRY = "entry "; + private static final String NOT_VALID_INSTANCE = + "Instance cannot be deployed; There are elements in an invalid Migration state." + + "(ElementId: %s, MigrationState: %s)"; private static final StringToMapConverter MAP_CONVERTER = new StringToMapConverter(); private static final Logger LOGGER = LoggerFactory.getLogger(AcmUtils.class); @@ -408,6 +411,23 @@ public final class AcmUtils { return message; } + /** + * Check that the AutomationComposition has all elements in MigrationState as DEFAULT. + * + * @param automationComposition the AutomationComposition + */ + public static void checkMigrationState(AutomationComposition automationComposition) { + var result = automationComposition.getElements().values() + .stream() + .filter(element -> !MigrationState.DEFAULT.equals(element.getMigrationState())) + .findAny(); + // check if elements are in a valid state to be deployed + if (result.isPresent()) { + var msg = String.format(NOT_VALID_INSTANCE, result.get().getId(), result.get().getMigrationState()); + throw new PfModelRuntimeException(Response.Status.BAD_REQUEST, msg); + } + } + /** * Recursive Merge. * 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 da87177c6..087c51186 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 @@ -22,6 +22,7 @@ package org.onap.policy.clamp.models.acm.utils; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; @@ -39,8 +40,10 @@ 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.AutomationCompositionElement; +import org.onap.policy.clamp.models.acm.concepts.MigrationState; import org.onap.policy.clamp.models.acm.document.concepts.DocToscaServiceTemplate; import org.onap.policy.clamp.models.acm.messages.rest.instantiation.DeployOrder; +import org.onap.policy.models.base.PfModelRuntimeException; import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier; import org.onap.policy.models.tosca.authorative.concepts.ToscaDataType; import org.onap.policy.models.tosca.authorative.concepts.ToscaNodeTemplate; @@ -194,6 +197,15 @@ class AcmUtilsTest { assertEquals(message.substring(0, 255), AcmUtils.validatedMessage(message)); } + @Test + void testCheckMigrationState() { + var automationComposition = getDummyAutomationComposition(); + assertDoesNotThrow(() -> AcmUtils.checkMigrationState(automationComposition)); + automationComposition.getElements().values().iterator().next().setMigrationState(MigrationState.REMOVED); + assertThatThrownBy(() -> AcmUtils.checkMigrationState(automationComposition)) + .isInstanceOf(PfModelRuntimeException.class); + } + private AutomationComposition getDummyAutomationComposition() { var automationComposition = new AutomationComposition(); automationComposition.setCompositionId(UUID.randomUUID()); 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 a3b90ddbb..60fa172f6 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 @@ -132,6 +132,7 @@ public class AutomationCompositionInstantiationProvider { AutomationCompositionProvider.validateInstanceEndpoint(compositionId, acFromDb); var acDefinition = acDefinitionProvider.getAcDefinition(compositionId); AcDefinitionProvider.checkPrimedComposition(acDefinition); + AcmUtils.checkMigrationState(acFromDb); if (DeployState.UNDEPLOYED.equals(acFromDb.getDeployState())) { LOGGER.info("Updating undeployed instance with id {}", instanceId); acFromDb.setElements(automationComposition.getElements()); 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 5dc13a9ba..6e782cef4 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 @@ -22,7 +22,6 @@ package org.onap.policy.clamp.acm.runtime.supervision; import io.micrometer.core.annotation.Timed; import io.opentelemetry.context.Context; -import jakarta.ws.rs.core.Response; import java.util.HashMap; import java.util.Map; import java.util.UUID; @@ -41,7 +40,6 @@ 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.StateChangeResult; import org.onap.policy.clamp.models.acm.concepts.SubState; import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionDeployAck; @@ -49,8 +47,8 @@ import org.onap.policy.clamp.models.acm.persistence.provider.AutomationCompositi import org.onap.policy.clamp.models.acm.persistence.provider.MessageProvider; import org.onap.policy.clamp.models.acm.utils.AcmStageUtils; import org.onap.policy.clamp.models.acm.utils.AcmStateUtils; +import org.onap.policy.clamp.models.acm.utils.AcmUtils; import org.onap.policy.clamp.models.acm.utils.TimestampHelper; -import org.onap.policy.models.base.PfModelRuntimeException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @@ -86,23 +84,14 @@ public class SupervisionAcHandler { */ public void deploy(AutomationComposition automationComposition, AutomationCompositionDefinition acDefinition) { LOGGER.info("Deployment request received for instanceID: {}", automationComposition.getInstanceId()); - - var elements = automationComposition.getElements().values(); - // check if elements are in a valid state to be deployed - elements.stream().filter(element -> !MigrationState.DEFAULT.equals(element.getMigrationState())) - .findAny().ifPresent(element -> { - var msg = String.format("Instance cannot be deployed; There are elements in an invalid Migration state." - + "(ElementId: %s, MigrationState: %s)", element.getId(), element.getMigrationState()); - LOGGER.warn(msg); - throw new PfModelRuntimeException(Response.Status.BAD_REQUEST, msg); - }); + AcmUtils.checkMigrationState(automationComposition); if (StateChangeResult.FAILED.equals(automationComposition.getStateChangeResult()) && DeployState.DEPLOYING.equals(automationComposition.getDeployState()) && automationComposition.getElements().size() > 1) { automationComposition.setLastMsg(TimestampHelper.now()); - for (var element : elements) { + for (var element : automationComposition.getElements().values()) { if (!DeployState.DEPLOYED.equals(element.getDeployState())) { element.setDeployState(DeployState.DEPLOYING); element.setMessage(null); 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 42df83499..1df511d6a 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 @@ -69,7 +69,8 @@ public abstract class AbstractScanner { || DeployState.MIGRATION_REVERTING.equals(automationComposition.getDeployState())) { automationComposition.setCompositionTargetId(null); for (var acElement : automationComposition.getElements().values()) { - if (MigrationState.NEW.equals(acElement.getMigrationState())) { + if (MigrationState.NEW.equals(acElement.getMigrationState()) + || MigrationState.REMOVED.equals(acElement.getMigrationState())) { acElement.setMigrationState(MigrationState.DEFAULT); } } 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 9e63cd25c..2d10e2cd5 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 @@ -51,6 +51,7 @@ import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionDefinition import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionRollback; 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.StateChangeResult; import org.onap.policy.clamp.models.acm.concepts.SubState; import org.onap.policy.clamp.models.acm.messages.rest.instantiation.AcInstanceStateUpdate; @@ -317,6 +318,8 @@ class AutomationCompositionInstantiationProviderTest { automationCompositionTarget.setPrecheck(false); AcmStateUtils.setCascadedState(automationComposition, DeployState.DEPLOYED, LockState.LOCKED, SubState.NONE); + automationComposition.getElements().values() + .forEach(el -> el.setMigrationState(MigrationState.DEFAULT)); var instantiationResponse = instantiationProvider.updateAutomationComposition(compositionId, automationCompositionTarget); diff --git a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/StageScannerTest.java b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/StageScannerTest.java index 42951b18c..f392a12ac 100644 --- a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/StageScannerTest.java +++ b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/StageScannerTest.java @@ -44,6 +44,7 @@ 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.SubState; import org.onap.policy.clamp.models.acm.persistence.provider.AutomationCompositionProvider; import org.onap.policy.clamp.models.acm.utils.TimestampHelper; @@ -160,9 +161,11 @@ class StageScannerTest { // first element is migrated clearInvocations(acProvider); element.setDeployState(DeployState.DEPLOYED); + element.setMigrationState(MigrationState.REMOVED); supervisionScanner.scanStage(automationComposition, acDefinition, new UpdateSync(), UUID.randomUUID()); verify(acProvider).updateAutomationComposition(any(AutomationComposition.class)); + assertEquals(MigrationState.DEFAULT, element.getMigrationState()); assertEquals(DeployState.DEPLOYED, automationComposition.getDeployState()); } -- 2.16.6