private Boolean precheck = false;
private Boolean rollback = false;
+ private Boolean firstStage = true;
private Integer stage = 0;
public AutomationCompositionMigration() {
public final class AcmStageUtils {
private static final String STAGE_MIGRATE = "migrate";
private static final String STAGE_PREPARE = "prepare";
+ private static final int MAX_STAGE = 1000;
/**
* Get the First StartPhase.
*/
public static int getFirstStartPhase(
AutomationComposition automationComposition, ToscaServiceTemplate toscaServiceTemplate) {
- var minStartPhase = 1000;
+ var minStartPhase = MAX_STAGE;
var maxStartPhase = 0;
for (var element : automationComposition.getElements().values()) {
var toscaNodeTemplate = toscaServiceTemplate.getToscaTopologyTemplate().getNodeTemplates()
return stageSet.stream().min(Integer::compare).orElse(0);
}
+ /**
+ * Get the Last Stage from AutomationComposition.
+ *
+ * @param automationComposition the automation composition
+ * @param toscaServiceTemplate the ToscaServiceTemplate
+ * @return the Last stage
+ */
+ public static int getLastStage(AutomationComposition automationComposition,
+ ToscaServiceTemplate toscaServiceTemplate) {
+ var stages = automationComposition.getElements().values().stream()
+ .map(element -> getLastStage(element, toscaServiceTemplate, 0));
+ return stages.max(Integer::compare).orElse(0);
+ }
+
+ /**
+ * Get the Last Stage from AutomationCompositionElement.
+ *
+ * @param element the automation composition element
+ * @param toscaServiceTemplate the ToscaServiceTemplate
+ * @param defaultValue default Value is not present
+ * @return the Last stage
+ */
+ public static int getLastStage(
+ AutomationCompositionElement element, ToscaServiceTemplate toscaServiceTemplate, int defaultValue) {
+ var toscaNodeTemplate = toscaServiceTemplate.getToscaTopologyTemplate().getNodeTemplates()
+ .get(element.getDefinition().getName());
+ if (toscaNodeTemplate == null) {
+ return defaultValue;
+ }
+ return getLastStage(toscaNodeTemplate.getProperties(), defaultValue);
+ }
+
+ /**
+ * Get the Last Stage.
+ *
+ * @param properties Map of properties
+ * @param defaultValue default Value is not present
+ * @return the Last stage
+ */
+ public static int getLastStage(Map<String, Object> properties, int defaultValue) {
+ var stageSet = findStageSetMigrate(properties);
+ return stageSet.stream().max(Integer::compare).orElse(defaultValue);
+ }
+
/**
* Finds startPhase from a map of properties.
*
package org.onap.policy.clamp.models.acm.utils;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.Map;
import org.junit.jupiter.api.Test;
automationComposition.setSubState(SubState.PREPARING);
result = AcmStageUtils.getFirstStage(automationComposition, serviceTemplate);
assertThat(result).isZero();
+
+ serviceTemplate.getToscaTopologyTemplate().getNodeTemplates().clear();
+ result = AcmStageUtils.getFirstStage(automationComposition.getElements().values().iterator().next(),
+ serviceTemplate);
+ assertThat(result).isZero();
+ }
+
+ @Test
+ void testGetLastStage() {
+ var serviceTemplate = CommonTestData.getToscaServiceTemplate(TOSCA_TEMPLATE_YAML);
+ var automationCompositions = CommonTestData.getJson(
+ ResourceUtils.getResourceAsString(AUTOMATION_COMPOSITION_JSON), AutomationCompositions.class);
+ assertThat(automationCompositions).isNotNull();
+ var automationComposition = automationCompositions.getAutomationCompositionList().get(0);
+ AcmStateUtils.setCascadedState(automationComposition, DeployState.MIGRATION_REVERTING, LockState.LOCKED);
+ var result = AcmStageUtils.getLastStage(automationComposition, serviceTemplate);
+ assertEquals(2, result);
+
+ serviceTemplate.getToscaTopologyTemplate().getNodeTemplates().clear();
+ result = AcmStageUtils.getLastStage(automationComposition.getElements().values().iterator().next(),
+ serviceTemplate, 1);
+ assertEquals(1, result);
}
@Test
var instanceElementRollback = new InstanceElementDto(INSTANCE_ELEMENT.instanceId(),
INSTANCE_ELEMENT.elementId(), Map.of(), new HashMap<>());
acElementHandler.rollbackMigration(COMPOSITION_ELEMENT, compositionElementRollback, INSTANCE_ELEMENT,
- instanceElementRollback, 1);
+ instanceElementRollback, 2);
verify(intermediaryApi).updateAutomationCompositionElementStage(
INSTANCE_ELEMENT.instanceId(), INSTANCE_ELEMENT.elementId(),
- StateChangeResult.NO_ERROR, 2, "stage 1 Migration rollback");
+ StateChangeResult.NO_ERROR, 1, "stage 2 Migration rollback");
}
@Test
@Override
public int getRollbackNextStage(CompositionElementDto compositionElementRollback, int lastStage) {
- return getMigrateNextStage(compositionElementRollback, lastStage);
+ var stageSet = AcmStageUtils.findStageSetMigrate(compositionElementRollback.inProperties());
+ var nextStage = -1;
+ for (var s : stageSet) {
+ if (s < lastStage) {
+ nextStage = Math.max(s, nextStage);
+ }
+ }
+ return nextStage == -1 ? lastStage : nextStage;
}
@Override
}
private void rollbackAutomationComposition(AutomationComposition automationComposition,
- ParticipantDeploy participantDeploy, int stage) {
+ ParticipantDeploy participantDeploy, int stage, int defaultValue) {
for (var element : participantDeploy.getAcElementList()) {
- var stageSet = getRollbackStageSet(element, automationComposition.getCompositionId());
+ var stageSet = getRollbackStageSet(element, automationComposition.getCompositionId(), defaultValue);
if (stageSet.contains(stage)) {
migrateElement(element, automationComposition, stage, participantDeploy);
}
automationComposition.setCompositionTargetId(migrationMsg.getCompositionTargetId());
automationComposition.setDeployState(DeployState.MIGRATION_REVERTING);
var automationCompositionCopy = new AutomationComposition(automationComposition);
+ var defaultValue = Boolean.TRUE.equals(migrationMsg.getFirstStage())
+ ? migrationMsg.getStage() : migrationMsg.getStage() + 1;
for (var participantDeploy : migrationMsg.getParticipantUpdatesList()) {
if (cacheProvider.getParticipantId().equals(participantDeploy.getParticipantId())) {
- rollbackAutomationComposition(automationComposition, participantDeploy, migrationMsg.getStage());
+ rollbackAutomationComposition(
+ automationComposition, participantDeploy, migrationMsg.getStage(), defaultValue);
callParticipantRollback(migrationMsg, participantDeploy.getAcElementList(), automationCompositionCopy);
}
private void callParticipantRollback(AutomationCompositionMigration migrationMsg, List<AcElementDeploy> acElements,
AutomationComposition automationCompositionCopy) {
+ var defaultValue = Boolean.TRUE.equals(migrationMsg.getFirstStage())
+ ? migrationMsg.getStage() : migrationMsg.getStage() + 1;
var automationComposition = cacheProvider.getAutomationComposition(automationCompositionCopy.getInstanceId());
var instanceElementTargetMap = cacheProvider.getInstanceElementDtoMap(automationComposition);
var compositionElementTargetMap = cacheProvider.getCompositionElementDtoMap(automationComposition);
automationCompositionCopy.getCompositionTargetId());
var instanceElementMap = cacheProvider.getInstanceElementDtoMap(automationCompositionCopy);
for (var acElement : acElements) {
- var stageSet = getRollbackStageSet(acElement, automationComposition.getCompositionId());
+ var stageSet = getRollbackStageSet(acElement, automationComposition.getCompositionId(), defaultValue);
var removed = MigrationState.NEW.equals(acElement.getMigrationState());
if (!removed) {
var commonProperties = cacheProvider
}
}
- private Set<Integer> getRollbackStageSet(AcElementDeploy acElement, UUID compositionId) {
+ private Set<Integer> getRollbackStageSet(AcElementDeploy acElement, UUID compositionId, int defaultValue) {
if (MigrationState.NEW.equals(acElement.getMigrationState())) {
- return Set.of(0);
+ return Set.of(defaultValue);
} else {
var commonProperties = cacheProvider.getCommonProperties(compositionId, acElement.getDefinition());
return AcmStageUtils.findStageSetMigrate(commonProperties);
Map<String, Object> stageSet = Map.of("stage", migrate);
var compositionElementTarget = new CompositionElementDto(UUID.randomUUID(), new ToscaConceptIdentifier(),
stageSet, Map.of());
- var result = apiImpl.getRollbackNextStage(compositionElementTarget, 0);
- assertEquals(2, result);
- result = apiImpl.getRollbackNextStage(compositionElementTarget, 2);
- assertEquals(2, result);
+ var result = apiImpl.getRollbackNextStage(compositionElementTarget, 2);
+ assertEquals(0, result);
+ result = apiImpl.getRollbackNextStage(compositionElementTarget, 0);
+ assertEquals(0, result);
}
}
int stage, int expectedMigrated, boolean rollback) {
var migrationMsg = new AutomationCompositionMigration();
migrationMsg.setStage(stage);
+ migrationMsg.setFirstStage(rollback && stage == 2);
migrationMsg.setCompositionId(acMigrate.getCompositionId());
migrationMsg.setAutomationCompositionId(acMigrate.getInstanceId());
migrationMsg.setCompositionTargetId(acMigrate.getCompositionTargetId());
cacheProvider.addElementDefinition(
automationComposition.getCompositionId(), acRollbackDefinitions, UUID.randomUUID());
- // expected the new element deleted
- testMigration(cacheProvider, acRollback, 0, 1, true);
-
// expected default elements
testMigration(cacheProvider, acRollback, 1, 4, true);
- // expected default elements
- testMigration(cacheProvider, acRollback, 2, 4, true);
+ // expected default elements and new element deleted
+ testMigration(cacheProvider, acRollback, 2, 5, true);
}
}
AutomationCompositionDefinition acDefinition, DeployState deployState) {
AcmStateUtils.setCascadedState(acFromDb, deployState, LockState.LOCKED);
acFromDb.setStateChangeResult(StateChangeResult.NO_ERROR);
- var stage = AcmStageUtils.getFirstStage(acFromDb, acDefinition.getServiceTemplate());
+ var stage = DeployState.MIGRATION_REVERTING.equals(deployState)
+ ? AcmStageUtils.getLastStage(acFromDb, acDefinition.getServiceTemplate())
+ : AcmStageUtils.getFirstStage(acFromDb, acDefinition.getServiceTemplate());
acFromDb.setPhase(stage);
}
executor.execute(() -> {
encryptionUtils.decryptInstanceProperties(automationComposition);
acCompositionMigrationPublisher.send(automationComposition, automationComposition.getPhase(),
- revisionIdComposition, revisionIdCompositionTarget);
+ revisionIdComposition, revisionIdCompositionTarget, true);
});
}
public void migratePrecheck(AutomationComposition automationComposition, UUID revisionIdComposition,
UUID revisionIdCompositionTarget) {
executor.execute(() -> acCompositionMigrationPublisher.send(automationComposition, 0,
- revisionIdComposition, revisionIdCompositionTarget));
+ revisionIdComposition, revisionIdCompositionTarget, true));
}
}
value = "publisher.automation_composition_migration",
description = "AUTOMATION_COMPOSITION_MIGRATION messages published")
public void send(AutomationComposition automationComposition, int stage, UUID revisionIdComposition,
- UUID revisionIdCompositionTarget) {
+ UUID revisionIdCompositionTarget, boolean fisrtStage) {
var acMigration = new AutomationCompositionMigration();
var rollback = DeployState.MIGRATION_REVERTING.equals(automationComposition.getDeployState());
acMigration.setRollback(rollback);
+ acMigration.setFirstStage(fisrtStage);
acMigration.setPrecheck(Boolean.TRUE.equals(automationComposition.getPrecheck()));
acMigration.setCompositionId(automationComposition.getCompositionId());
acMigration.setAutomationCompositionId(automationComposition.getInstanceId());
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.StateChangeResult;
import org.onap.policy.clamp.models.acm.concepts.SubState;
*/
public void scanStage(final AutomationComposition automationComposition,
AutomationCompositionDefinition acDefinition, UpdateSync updateSync, UUID revisionIdComposition) {
+ var rollback = DeployState.MIGRATION_REVERTING.equals(automationComposition.getDeployState());
+ var highStage = AcmStageUtils.getLastStage(automationComposition, acDefinition.getServiceTemplate());
+ var stageNotCompleted = rollback ? -1 : 1000; // min stage not completed
var completed = true;
- var minStageNotCompleted = 1000; // min stage not completed
List<UUID> elementsDeleted = new ArrayList<>();
for (var element : automationComposition.getElements().values()) {
if (AcmStateUtils.isInTransitionalState(element.getDeployState(), element.getLockState(),
element.getSubState())) {
- var firstStage = AcmStageUtils.getFirstStage(element, acDefinition.getServiceTemplate());
- int stage = element.getStage() != null ? element.getStage() : firstStage;
- minStageNotCompleted = Math.min(minStageNotCompleted, stage);
+ stageNotCompleted = rollback
+ ? Math.max(stageNotCompleted, getRollbackCurrentStage(element, acDefinition, highStage))
+ : Math.min(stageNotCompleted, getCurrentStage(element, acDefinition));
completed = false;
} else if (element.getDeployState().equals(DeployState.DELETED)
&& automationComposition.getStateChangeResult().equals(StateChangeResult.NO_ERROR)) {
if (completed) {
complete(automationComposition, updateSync);
} else {
- processNextStage(automationComposition, updateSync, minStageNotCompleted, revisionIdComposition,
+ processNextStage(automationComposition, updateSync, stageNotCompleted, revisionIdComposition,
acDefinition);
}
}
+ private int getCurrentStage(AutomationCompositionElement element, AutomationCompositionDefinition acDefinition) {
+ return element.getStage() != null
+ ? element.getStage() : AcmStageUtils.getFirstStage(element, acDefinition.getServiceTemplate());
+ }
+
+ private int getRollbackCurrentStage(
+ AutomationCompositionElement element, AutomationCompositionDefinition acDefinition, int defaultValue) {
+ return element.getStage() != null ? element.getStage()
+ : AcmStageUtils.getLastStage(element, acDefinition.getServiceTemplate(), defaultValue);
+ }
+
private void processNextStage(AutomationComposition automationComposition, UpdateSync updateSync,
int minStageNotCompleted, UUID revisionIdComposition,
AutomationCompositionDefinition acDefinition) {
LOGGER.debug("retry migrating message AutomationCompositionMigration");
// acDefinition for migration is the Composition target
acMigrationPublisher.send(automationComposition, minStageNotCompleted, revisionIdComposition,
- acDefinition.getRevisionId());
+ acDefinition.getRevisionId(), false);
} else if (DeployState.MIGRATION_REVERTING.equals(automationComposition.getDeployState())) {
LOGGER.debug("retry rollback message AutomationCompositionMigration");
acMigrationPublisher.send(automationComposition, minStageNotCompleted, acDefinition.getRevisionId(),
- revisionIdComposition);
+ revisionIdComposition, false);
} else if (SubState.PREPARING.equals(automationComposition.getSubState())) {
LOGGER.debug("retry message AutomationCompositionPrepare");
acPreparePublisher.sendPrepare(automationComposition, minStageNotCompleted, acDefinition.getRevisionId());
automationComposition.setPhase(0);
handler.migrate(automationComposition, UUID.randomUUID(), UUID.randomUUID());
verify(acCompositionMigrationPublisher, timeout(1000))
- .send(any(AutomationComposition.class), anyInt(), any(UUID.class), any(UUID.class));
+ .send(any(AutomationComposition.class), anyInt(), any(UUID.class), any(UUID.class), anyBoolean());
}
@Test
InstantiationUtils.getAutomationCompositionFromResource(AC_INSTANTIATION_CREATE_JSON, "Migrate");
handler.migratePrecheck(automationComposition, UUID.randomUUID(), UUID.randomUUID());
verify(acCompositionMigrationPublisher, timeout(1000))
- .send(any(AutomationComposition.class), anyInt(), any(UUID.class), any(UUID.class));
+ .send(any(AutomationComposition.class), anyInt(), any(UUID.class), any(UUID.class), anyBoolean());
}
@Test
publisher.active(topicSink);
var automationComposition =
InstantiationUtils.getAutomationCompositionFromResource(AC_INSTANTIATION_UPDATE_JSON, "Crud");
- publisher.send(automationComposition, 0, UUID.randomUUID(), UUID.randomUUID());
+ publisher.send(automationComposition, 0, UUID.randomUUID(), UUID.randomUUID(), true);
verify(topicSink).send(anyString());
}