aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrameshiyer27 <ramesh.murugan.iyer@est.tech>2024-07-23 18:08:35 +0100
committerrameshiyer27 <ramesh.murugan.iyer@est.tech>2024-07-26 12:43:25 +0100
commitbcc65d6f4268ff437b162324de13ee53bde47ddd (patch)
tree3b4fbbb23639d6a006da828d7d9400b1d75d3e56
parent37195c69c99b5987f037a018859b4421cbcbadf2 (diff)
Support add/remove elements in Migration
Issue-ID: POLICY-4917 Change-Id: I0014b4858dd7e6ac76bfa1184d0b90b52e8649f5 Signed-off-by: zrrmmua <ramesh.murugan.iyer@est.tech>
-rw-r--r--models/src/main/java/org/onap/policy/clamp/models/acm/persistence/provider/AutomationCompositionProvider.java9
-rw-r--r--models/src/test/java/org/onap/policy/clamp/models/acm/persistence/provider/AutomationCompositionProviderTest.java13
-rw-r--r--participant/participant-impl/participant-impl-simulator/src/main/java/org/onap/policy/clamp/acm/participant/sim/main/handler/AutomationCompositionElementHandlerV2.java11
-rw-r--r--participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/api/CompositionElementDto.java9
-rw-r--r--participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/api/InstanceElementDto.java8
-rw-r--r--participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AutomationCompositionHandler.java85
-rw-r--r--participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AutomationCompositionHandlerTest.java40
-rw-r--r--runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/instantiation/AutomationCompositionInstantiationProvider.java78
-rw-r--r--runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/instantiation/AutomationCompositionInstantiationProviderTest.java132
-rw-r--r--runtime-acm/src/test/resources/rest/acm/AutomationCompositionMigrate.json30
10 files changed, 364 insertions, 51 deletions
diff --git a/models/src/main/java/org/onap/policy/clamp/models/acm/persistence/provider/AutomationCompositionProvider.java b/models/src/main/java/org/onap/policy/clamp/models/acm/persistence/provider/AutomationCompositionProvider.java
index 9b736483d..e26a7748f 100644
--- a/models/src/main/java/org/onap/policy/clamp/models/acm/persistence/provider/AutomationCompositionProvider.java
+++ b/models/src/main/java/org/onap/policy/clamp/models/acm/persistence/provider/AutomationCompositionProvider.java
@@ -269,6 +269,15 @@ public class AutomationCompositionProvider {
}
/**
+ * Delete AutomationCompositionElement.
+ *
+ * @param elementId the AutomationCompositionElement Id
+ */
+ public void deleteAutomationCompositionElement(@NonNull final UUID elementId) {
+ acElementRepository.deleteById(elementId.toString());
+ }
+
+ /**
* Validate ElementIds.
*
* @param automationComposition the AutomationComposition
diff --git a/models/src/test/java/org/onap/policy/clamp/models/acm/persistence/provider/AutomationCompositionProviderTest.java b/models/src/test/java/org/onap/policy/clamp/models/acm/persistence/provider/AutomationCompositionProviderTest.java
index 515dfaa83..c2368fe10 100644
--- a/models/src/test/java/org/onap/policy/clamp/models/acm/persistence/provider/AutomationCompositionProviderTest.java
+++ b/models/src/test/java/org/onap/policy/clamp/models/acm/persistence/provider/AutomationCompositionProviderTest.java
@@ -56,6 +56,7 @@ class AutomationCompositionProviderTest {
private static final String AC_IS_NULL = "automationComposition is marked non-null but is null";
private static final String ACELEMENT_IS_NULL = "element is marked non-null but is null";
+ private static final String ACELEMENT_ID_IS_NULL = "elementId is marked non-null but is null";
private static final Coder CODER = new StandardCoder();
private static final String AUTOMATION_COMPOSITION_JSON =
@@ -241,6 +242,18 @@ class AutomationCompositionProviderTest {
}
@Test
+ void testDeleteElementById() {
+ var acElementRepository = mock(AutomationCompositionElementRepository.class);
+ var automationCompositionProvider = new AutomationCompositionProvider(
+ mock(AutomationCompositionRepository.class), acElementRepository);
+ assertThatThrownBy(() -> automationCompositionProvider.deleteAutomationCompositionElement(null))
+ .hasMessageMatching(ACELEMENT_ID_IS_NULL);
+ var elementId = UUID.randomUUID();
+ automationCompositionProvider.deleteAutomationCompositionElement(elementId);
+ verify(acElementRepository).deleteById(elementId.toString());
+ }
+
+ @Test
void testValidateElementIds() {
var acElementRepository = mock(AutomationCompositionElementRepository.class);
var automationCompositionProvider = new AutomationCompositionProvider(
diff --git a/participant/participant-impl/participant-impl-simulator/src/main/java/org/onap/policy/clamp/acm/participant/sim/main/handler/AutomationCompositionElementHandlerV2.java b/participant/participant-impl/participant-impl-simulator/src/main/java/org/onap/policy/clamp/acm/participant/sim/main/handler/AutomationCompositionElementHandlerV2.java
index 5f866eb0c..7eff849a1 100644
--- a/participant/participant-impl/participant-impl-simulator/src/main/java/org/onap/policy/clamp/acm/participant/sim/main/handler/AutomationCompositionElementHandlerV2.java
+++ b/participant/participant-impl/participant-impl-simulator/src/main/java/org/onap/policy/clamp/acm/participant/sim/main/handler/AutomationCompositionElementHandlerV2.java
@@ -117,7 +117,16 @@ public class AutomationCompositionElementHandlerV2 extends AcElementListenerV2 {
LOGGER.debug("migrate call compositionElement: {}, compositionElementTarget: {}, instanceElement: {},"
+ " instanceElementMigrate: {}",
compositionElement, compositionElementTarget, instanceElement, instanceElementMigrate);
- simulatorService.migrate(instanceElement.instanceId(), instanceElement.elementId());
+ if (instanceElement.newElement()) {
+ simulatorService.migrate(instanceElementMigrate.instanceId(), instanceElementMigrate.elementId());
+
+ } else if (instanceElementMigrate.removedElement()) {
+ simulatorService.undeploy(instanceElement.instanceId(), instanceElement.elementId());
+ simulatorService.delete(instanceElement.instanceId(), instanceElement.elementId());
+ } else {
+ simulatorService.migrate(instanceElement.instanceId(), instanceElement.elementId());
+ }
+
}
@Override
diff --git a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/api/CompositionElementDto.java b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/api/CompositionElementDto.java
index d203f90cb..51800cef1 100644
--- a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/api/CompositionElementDto.java
+++ b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/api/CompositionElementDto.java
@@ -25,5 +25,12 @@ import java.util.UUID;
import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
public record CompositionElementDto(UUID compositionId, ToscaConceptIdentifier elementDefinitionId,
- Map<String, Object> inProperties, Map<String, Object> outProperties) {
+ Map<String, Object> inProperties, Map<String, Object> outProperties,
+ boolean newElement, boolean removedElement) {
+
+ public CompositionElementDto(UUID compositionId, ToscaConceptIdentifier elementDefinitionId,
+ Map<String, Object> inProperties, Map<String, Object> outProperties) {
+ this(compositionId, elementDefinitionId, inProperties, outProperties, false, false);
+
+ }
}
diff --git a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/api/InstanceElementDto.java b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/api/InstanceElementDto.java
index 297af6c43..65a83e222 100644
--- a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/api/InstanceElementDto.java
+++ b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/api/InstanceElementDto.java
@@ -25,5 +25,11 @@ import java.util.UUID;
import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
public record InstanceElementDto(UUID instanceId, UUID elementId, ToscaServiceTemplate toscaServiceTemplateFragment,
- Map<String, Object> inProperties, Map<String, Object> outProperties) {
+ Map<String, Object> inProperties, Map<String, Object> outProperties,
+ boolean newElement, boolean removedElement) {
+
+ public InstanceElementDto(UUID instanceId, UUID elementId, ToscaServiceTemplate toscaServiceTemplateFragment,
+ Map<String, Object> inProperties, Map<String, Object> outProperties) {
+ this(instanceId, elementId, toscaServiceTemplateFragment, inProperties, outProperties, false, false);
+ }
}
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 0f230c0a4..6c560e7fa 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,14 +21,20 @@
package org.onap.policy.clamp.acm.participant.intermediary.handler;
+
+import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
+import org.onap.policy.clamp.acm.participant.intermediary.api.CompositionElementDto;
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.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.ParticipantDeploy;
import org.onap.policy.clamp.models.acm.concepts.ParticipantUtils;
import org.onap.policy.clamp.models.acm.concepts.StateChangeResult;
@@ -41,6 +47,7 @@ import org.onap.policy.clamp.models.acm.messages.kafka.participant.ParticipantMe
import org.onap.policy.clamp.models.acm.messages.kafka.participant.PropertiesUpdate;
import org.onap.policy.clamp.models.acm.messages.rest.instantiation.DeployOrder;
import org.onap.policy.clamp.models.acm.utils.AcmUtils;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@@ -186,11 +193,51 @@ public class AutomationCompositionHandler {
var acElementList = cacheProvider.getAutomationComposition(instanceId).getElements();
for (var element : participantDeploy.getAcElementList()) {
var acElement = acElementList.get(element.getId());
- AcmUtils.recursiveMerge(acElement.getProperties(), element.getProperties());
- acElement.setDeployState(deployState);
- acElement.setSubState(SubState.NONE);
- acElement.setDefinition(element.getDefinition());
+ if (acElement == null && deployState.equals(DeployState.MIGRATING)) {
+ var newElement = new AutomationCompositionElement();
+ newElement.setId(element.getId());
+ newElement.setParticipantId(participantDeploy.getParticipantId());
+ newElement.setDefinition(element.getDefinition());
+ newElement.setDeployState(deployState);
+ newElement.setSubState(SubState.NONE);
+ newElement.setLockState(LockState.LOCKED);
+ newElement.setProperties(element.getProperties());
+
+ acElementList.put(element.getId(), newElement);
+ LOGGER.info("New Ac Element with id {} is added in Migration", element.getId());
+ } else if (acElement != null) {
+ AcmUtils.recursiveMerge(acElement.getProperties(), element.getProperties());
+ acElement.setDeployState(deployState);
+ acElement.setSubState(SubState.NONE);
+ acElement.setDefinition(element.getDefinition());
+ }
+ }
+ if (deployState.equals(DeployState.MIGRATING)) {
+ // Check for missing elements and remove them from cache
+ List<UUID> elementsToRemove = findElementsToRemove(participantDeploy.getAcElementList(), acElementList);
+ for (UUID key : elementsToRemove) {
+ acElementList.remove(key);
+ LOGGER.info("Element with id {} is removed in Migration", key);
+ }
+ }
+ }
+
+ private List<UUID> findElementsToRemove(List<AcElementDeploy> acElementDeployList, Map<UUID,
+ AutomationCompositionElement> acElementList) {
+ List<UUID> elementsToRemove = new ArrayList<>();
+ for (var elementInCache : acElementList.entrySet()) {
+ boolean found = false;
+ for (var element : acElementDeployList) {
+ if (element.getId().equals(elementInCache.getValue().getId())) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ elementsToRemove.add(elementInCache.getKey());
+ }
}
+ return elementsToRemove;
}
/**
@@ -282,10 +329,34 @@ public class AutomationCompositionHandler {
compositionTargetId);
var instanceElementMigrateMap = cacheProvider.getInstanceElementDtoMap(automationComposition);
+ // Call migrate for newly added and updated elements
for (var acElement : acElements) {
- listener.migrate(messageId, compositionElementMap.get(acElement.getId()),
- compositionElementTargetMap.get(acElement.getId()),
- instanceElementMap.get(acElement.getId()), instanceElementMigrateMap.get(acElement.getId()));
+ if (instanceElementMap.get(acElement.getId()) == null) {
+ var compositionDto = new CompositionElementDto(acElement.getId(), acElement.getDefinition(),
+ Map.of(), Map.of(), true, false);
+ var instanceDto = new InstanceElementDto(acCopy.getInstanceId(), acElement.getId(),
+ new ToscaServiceTemplate(), Map.of(), Map.of(), true, false);
+
+ listener.migrate(messageId, compositionDto,
+ compositionElementTargetMap.get(acElement.getId()),
+ instanceDto, instanceElementMigrateMap.get(acElement.getId()));
+ } else {
+ listener.migrate(messageId, compositionElementMap.get(acElement.getId()),
+ compositionElementTargetMap.get(acElement.getId()),
+ instanceElementMap.get(acElement.getId()), instanceElementMigrateMap.get(acElement.getId()));
+ }
+ }
+ // Call migrate for removed elements
+ List<UUID> removedElements = findElementsToRemove(acElements, acCopy.getElements());
+ for (var elementId : removedElements) {
+ var compositionDtoTarget = new CompositionElementDto(elementId, acCopy.getElements().get(elementId)
+ .getDefinition(), Map.of(), Map.of(), false, true);
+ var instanceDtoTarget = new InstanceElementDto(acCopy.getInstanceId(), elementId,
+ new ToscaServiceTemplate(), Map.of(), Map.of(), false, true);
+
+ listener.migrate(messageId, compositionElementMap.get(elementId),
+ compositionDtoTarget,
+ instanceElementMap.get(elementId), instanceDtoTarget);
}
}
}
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 40e3b1eec..5973508a8 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
@@ -35,6 +35,7 @@ import org.junit.jupiter.api.Test;
import org.onap.policy.clamp.acm.participant.intermediary.comm.ParticipantMessagePublisher;
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.AutomationCompositionElementDefinition;
import org.onap.policy.clamp.models.acm.concepts.DeployState;
import org.onap.policy.clamp.models.acm.concepts.ParticipantDeploy;
@@ -203,16 +204,34 @@ class AutomationCompositionHandlerTest {
var migrationMsg = new AutomationCompositionMigration();
assertDoesNotThrow(() -> ach.handleAutomationCompositionMigration(migrationMsg));
var automationComposition = CommonTestData.getTestAutomationCompositionMap().values().iterator().next();
- migrationMsg.setCompositionTargetId(UUID.randomUUID());
- migrationMsg.setAutomationCompositionId(automationComposition.getInstanceId());
+
assertDoesNotThrow(() -> ach.handleAutomationCompositionMigration(migrationMsg));
when(cacheProvider.getAutomationComposition(automationComposition.getInstanceId()))
.thenReturn(automationComposition);
+ when(cacheProvider.getParticipantId()).thenReturn(CommonTestData.getParticipantId());
+
+ Map<ToscaConceptIdentifier, AutomationCompositionElementDefinition> map = new HashMap<>();
var participantDeploy = new ParticipantDeploy();
+ populateMigrationMsg(automationComposition, migrationMsg, map, participantDeploy);
+ when(cacheProvider.getAcElementsDefinitions())
+ .thenReturn(Map.of(automationComposition.getCompositionId(), map,
+ migrationMsg.getCompositionTargetId(), map));
+
+ ach.handleAutomationCompositionMigration(migrationMsg);
+ verify(listener, times(automationComposition.getElements().size() + 1))
+ .migrate(any(), any(), any(), any(), any());
+ }
+
+ private void populateMigrationMsg(AutomationComposition automationComposition,
+ AutomationCompositionMigration migrationMsg,
+ Map<ToscaConceptIdentifier,
+ AutomationCompositionElementDefinition> map,
+ ParticipantDeploy participantDeploy) {
+
participantDeploy.setParticipantId(CommonTestData.getParticipantId());
- when(cacheProvider.getParticipantId()).thenReturn(CommonTestData.getParticipantId());
+ migrationMsg.setCompositionTargetId(UUID.randomUUID());
+ migrationMsg.setAutomationCompositionId(automationComposition.getInstanceId());
migrationMsg.getParticipantUpdatesList().add(participantDeploy);
- Map<ToscaConceptIdentifier, AutomationCompositionElementDefinition> map = new HashMap<>();
for (var element : automationComposition.getElements().values()) {
var acElementDeploy = new AcElementDeploy();
acElementDeploy.setProperties(Map.of());
@@ -221,11 +240,14 @@ class AutomationCompositionHandlerTest {
participantDeploy.getAcElementList().add(acElementDeploy);
map.put(element.getDefinition(), new AutomationCompositionElementDefinition());
}
- when(cacheProvider.getAcElementsDefinitions())
- .thenReturn(Map.of(automationComposition.getCompositionId(), map,
- migrationMsg.getCompositionTargetId(), map));
+ // remove an element
+ participantDeploy.getAcElementList().remove(0);
+ // Add a new element
+ var acElementDeploy = new AcElementDeploy();
+ acElementDeploy.setProperties(Map.of());
+ acElementDeploy.setId(UUID.randomUUID());
+ acElementDeploy.setDefinition(new ToscaConceptIdentifier("1.2.3", "policy.clamp.new.element"));
+ participantDeploy.getAcElementList().add(acElementDeploy);
- ach.handleAutomationCompositionMigration(migrationMsg);
- verify(listener, times(automationComposition.getElements().size())).migrate(any(), any(), any(), any(), any());
}
}
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 c732654f2..653d97338 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
@@ -23,6 +23,8 @@ package org.onap.policy.clamp.acm.runtime.instantiation;
import jakarta.validation.Valid;
import jakarta.ws.rs.core.Response.Status;
+import java.util.ArrayList;
+import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import lombok.NonNull;
@@ -50,6 +52,7 @@ import org.onap.policy.clamp.models.acm.utils.AcmUtils;
import org.onap.policy.common.parameters.BeanValidationResult;
import org.onap.policy.common.parameters.ObjectValidationResult;
import org.onap.policy.common.parameters.ValidationStatus;
+import org.onap.policy.models.base.PfConceptKey;
import org.onap.policy.models.base.PfKey;
import org.onap.policy.models.base.PfModelRuntimeException;
import org.slf4j.Logger;
@@ -215,23 +218,29 @@ public class AutomationCompositionInstantiationProvider {
for (var element : automationComposition.getElements().entrySet()) {
var elementId = element.getKey();
var dbAcElement = acToBeUpdated.getElements().get(elementId);
+ // Add additional elements if present for migration
if (dbAcElement == null) {
- throw new PfModelRuntimeException(Status.BAD_REQUEST, "Element id not present " + elementId);
- }
- AcmUtils.recursiveMerge(dbAcElement.getProperties(), element.getValue().getProperties());
- var newDefinition = element.getValue().getDefinition();
- var compatibility =
- newDefinition.asConceptKey().getCompatibility(dbAcElement.getDefinition().asConceptKey());
- if (PfKey.Compatibility.DIFFERENT.equals(compatibility)) {
- throw new PfModelRuntimeException(Status.BAD_REQUEST,
- dbAcElement.getDefinition() + " is not compatible with " + newDefinition);
+ LOGGER.info("New Ac element {} added in Migration", elementId);
+ acToBeUpdated.getElements().put(elementId, automationComposition.getElements().get(elementId));
+ } else {
+ AcmUtils.recursiveMerge(dbAcElement.getProperties(), element.getValue().getProperties());
+ var newDefinition = element.getValue().getDefinition().asConceptKey();
+ var dbElementDefinition = dbAcElement.getDefinition().asConceptKey();
+ checkCompatibility(newDefinition, dbElementDefinition, automationComposition.getInstanceId());
+ dbAcElement.setDefinition(element.getValue().getDefinition());
}
- if (PfKey.Compatibility.MAJOR.equals(compatibility) || PfKey.Compatibility.MINOR.equals(compatibility)) {
- LOGGER.warn("Migrate {}: Version {} has {} compatibility with {} ",
- automationComposition.getInstanceId(), newDefinition, compatibility, dbAcElement.getDefinition());
+ }
+ // Remove element which is not present in the new Ac instance
+ List<UUID> elementsRemoved = new ArrayList<>();
+ for (var dbElement : acToBeUpdated.getElements().entrySet()) {
+ var dbElementId = dbElement.getKey();
+ if (automationComposition.getElements().get(dbElementId) == null) {
+ LOGGER.info("Element with id {} is removed in Migration", dbElementId);
+ elementsRemoved.add(dbElementId);
+ automationCompositionProvider.deleteAutomationCompositionElement(dbElementId);
}
- dbAcElement.setDefinition(element.getValue().getDefinition());
}
+ elementsRemoved.forEach(uuid -> acToBeUpdated.getElements().remove(uuid));
var validationResult =
validateAutomationComposition(acToBeUpdated, automationComposition.getCompositionTargetId());
@@ -244,9 +253,24 @@ public class AutomationCompositionInstantiationProvider {
supervisionAcHandler.migrate(acToBeUpdated);
automationComposition = automationCompositionProvider.updateAutomationComposition(acToBeUpdated);
+ elementsRemoved.forEach(automationCompositionProvider::deleteAutomationCompositionElement);
return createInstantiationResponse(automationComposition);
}
+ void checkCompatibility(PfConceptKey newDefinition, PfConceptKey dbElementDefinition,
+ UUID instanceId) {
+ var compatibility = newDefinition.getCompatibility(dbElementDefinition);
+ if (PfKey.Compatibility.DIFFERENT.equals(compatibility)) {
+ throw new PfModelRuntimeException(Status.BAD_REQUEST,
+ dbElementDefinition + " is not compatible with " + newDefinition);
+ }
+ if (PfKey.Compatibility.MAJOR.equals(compatibility) || PfKey.Compatibility.MINOR
+ .equals(compatibility)) {
+ LOGGER.warn("Migrate {}: Version {} has {} compatibility with {} ", instanceId, newDefinition,
+ compatibility, dbElementDefinition);
+ }
+ }
+
private InstantiationResponse migratePrecheckAc(
AutomationComposition automationComposition, AutomationComposition acToBeUpdated) {
@@ -256,20 +280,30 @@ public class AutomationCompositionInstantiationProvider {
for (var element : automationComposition.getElements().entrySet()) {
var elementId = element.getKey();
var copyElement = copyAc.getElements().get(elementId);
+ // Add additional elements if present for migration
if (copyElement == null) {
- throw new PfModelRuntimeException(Status.BAD_REQUEST, "Element id not present " + elementId);
+ LOGGER.info("New Ac element {} added in Migration", elementId);
+ copyAc.getElements().put(elementId, automationComposition.getElements().get(elementId));
+ } else {
+ AcmUtils.recursiveMerge(copyElement.getProperties(), element.getValue().getProperties());
+ var newDefinition = element.getValue().getDefinition().asConceptKey();
+ var copyElementDefinition = copyElement.getDefinition().asConceptKey();
+ checkCompatibility(newDefinition, copyElementDefinition, automationComposition.getInstanceId());
+ copyElement.setDefinition(element.getValue().getDefinition());
}
- AcmUtils.recursiveMerge(copyElement.getProperties(), element.getValue().getProperties());
- var newDefinition = element.getValue().getDefinition();
- var compatibility =
- newDefinition.asConceptKey().getCompatibility(copyElement.getDefinition().asConceptKey());
- if (PfKey.Compatibility.DIFFERENT.equals(compatibility)) {
- throw new PfModelRuntimeException(Status.BAD_REQUEST,
- copyElement.getDefinition() + " is not compatible with " + newDefinition);
+ }
+ // Remove element which is not present in the new Ac instance
+ List<UUID> elementsRemoved = new ArrayList<>();
+ for (var dbElement : copyAc.getElements().entrySet()) {
+ var dbElementId = dbElement.getKey();
+ if (automationComposition.getElements().get(dbElementId) == null) {
+ LOGGER.info("Element with id {} is removed in Migration", dbElementId);
+ elementsRemoved.add(dbElementId);
}
- copyElement.setDefinition(element.getValue().getDefinition());
}
+ elementsRemoved.forEach(uuid -> copyAc.getElements().remove(uuid));
+
var validationResult =
validateAutomationComposition(copyAc, automationComposition.getCompositionTargetId());
if (!validationResult.isValid()) {
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 a7ae99feb..23709728d 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
@@ -23,6 +23,7 @@ package org.onap.policy.clamp.acm.runtime.instantiation;
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.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
@@ -32,6 +33,7 @@ import static org.onap.policy.clamp.acm.runtime.util.CommonTestData.TOSCA_SERVIC
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import org.junit.jupiter.api.BeforeAll;
@@ -42,9 +44,11 @@ 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.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.StateChangeResult;
+import org.onap.policy.clamp.models.acm.concepts.SubState;
import org.onap.policy.clamp.models.acm.messages.rest.instantiation.AcInstanceStateUpdate;
import org.onap.policy.clamp.models.acm.messages.rest.instantiation.DeployOrder;
import org.onap.policy.clamp.models.acm.messages.rest.instantiation.LockOrder;
@@ -54,6 +58,9 @@ import org.onap.policy.clamp.models.acm.persistence.provider.AcInstanceStateReso
import org.onap.policy.clamp.models.acm.persistence.provider.AutomationCompositionProvider;
import org.onap.policy.clamp.models.acm.persistence.provider.ParticipantProvider;
import org.onap.policy.clamp.models.acm.persistence.provider.ProviderUtils;
+import org.onap.policy.clamp.models.acm.utils.AcmUtils;
+import org.onap.policy.models.base.PfConceptKey;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
import org.onap.policy.models.tosca.simple.concepts.JpaToscaServiceTemplate;
@@ -65,6 +72,7 @@ class AutomationCompositionInstantiationProviderTest {
private static final String AC_INSTANTIATION_CREATE_JSON = "src/test/resources/rest/acm/AutomationComposition.json";
private static final String AC_INSTANTIATION_UPDATE_JSON =
"src/test/resources/rest/acm/AutomationCompositionUpdate.json";
+ private static final String AC_MIGRATE_JSON = "src/test/resources/rest/acm/AutomationCompositionMigrate.json";
private static final String AC_INSTANTIATION_DEFINITION_NAME_NOT_FOUND_JSON =
"src/test/resources/rest/acm/AutomationCompositionElementsNotFound.json";
@@ -280,11 +288,110 @@ class AutomationCompositionInstantiationProviderTest {
var instantiationProvider = new AutomationCompositionInstantiationProvider(acProvider, acDefinitionProvider,
new AcInstanceStateResolver(), supervisionAcHandler, participantProvider,
mock(AcRuntimeParameterGroup.class));
+ var message =
+ """
+ "AutomationComposition" INVALID, item has status INVALID
+ item "ServiceTemplate.restarting" value "true" INVALID, There is a restarting process in composition
+ """;
+
assertThatThrownBy(
() -> instantiationProvider.updateAutomationComposition(compositionId, automationCompositionUpdate))
- .hasMessageMatching("\"AutomationComposition\" INVALID, item has status INVALID\n"
- + " item \"ServiceTemplate.restarting\" value \"true\" INVALID,"
- + " There is a restarting process in composition\n");
+ .hasMessageMatching(message);
+ }
+
+ @Test
+ void testMigrationAddRemoveElements() {
+ var acDefinitionProvider = mock(AcDefinitionProvider.class);
+ var acDefinition = CommonTestData.createAcDefinition(serviceTemplate, AcTypeState.PRIMED);
+ var compositionId = acDefinition.getCompositionId();
+ when(acDefinitionProvider.findAcDefinition(compositionId)).thenReturn(Optional.of(acDefinition));
+ var instanceId = UUID.randomUUID();
+
+ var automationComposition =
+ InstantiationUtils.getAutomationCompositionFromResource(AC_MIGRATE_JSON, "Crud");
+ automationComposition.setCompositionId(compositionId);
+ automationComposition.setInstanceId(instanceId);
+ automationComposition.setDeployState(DeployState.DEPLOYED);
+ automationComposition.setLockState(LockState.LOCKED);
+ var acProvider = mock(AutomationCompositionProvider.class);
+ when(acProvider.getAutomationComposition(automationComposition.getInstanceId()))
+ .thenReturn(automationComposition);
+
+ var automationCompositionTarget = new AutomationComposition(automationComposition);
+ automationCompositionTarget.setInstanceId(instanceId);
+ automationCompositionTarget.setCompositionId(compositionId);
+ // Add a new element
+ var uuid = UUID.randomUUID();
+ var newElement = new AutomationCompositionElement();
+ newElement.setId(uuid);
+ newElement.setDefinition(new ToscaConceptIdentifier(
+ "org.onap.domain.pmsh.PMSH_OperationalPolicyAutomationCompositionElement", "1.2.3"));
+ newElement.setProperties(Map.of("testVar", "1", "testVar2", "2"));
+ automationCompositionTarget.getElements().put(uuid, newElement);
+
+ //Remove an existing element
+ var elementIdToRemove = UUID.randomUUID();
+ for (var element : automationCompositionTarget.getElements().values()) {
+ if (element.getDefinition().getName()
+ .equals("org.onap.domain.database.Http_PMSHMicroserviceAutomationCompositionElement")) {
+ elementIdToRemove = element.getId();
+ }
+ }
+ automationCompositionTarget.getElements().remove(elementIdToRemove);
+
+ var acDefinitionTarget = CommonTestData.createAcDefinition(serviceTemplate, AcTypeState.PRIMED);
+ var compositionTargetId = acDefinitionTarget.getCompositionId();
+ automationCompositionTarget.setCompositionTargetId(compositionTargetId);
+ when(acDefinitionProvider.findAcDefinition(compositionTargetId)).thenReturn(Optional.of(acDefinitionTarget));
+ when(acProvider.updateAutomationComposition(any())).thenReturn(automationCompositionTarget);
+
+ var supervisionAcHandler = mock(SupervisionAcHandler.class);
+ var participantProvider = mock(ParticipantProvider.class);
+ var instantiationProvider = new AutomationCompositionInstantiationProvider(acProvider, acDefinitionProvider,
+ new AcInstanceStateResolver(), supervisionAcHandler, participantProvider,
+ new AcRuntimeParameterGroup());
+
+ automationCompositionTarget.setPrecheck(true);
+ var preCheckResponse = instantiationProvider.updateAutomationComposition(compositionId,
+ automationCompositionTarget);
+ verify(supervisionAcHandler).migratePrecheck(any());
+ InstantiationUtils.assertInstantiationResponse(preCheckResponse, automationCompositionTarget);
+
+ automationCompositionTarget.setPrecheck(false);
+ AcmUtils.setCascadedState(automationComposition, DeployState.DEPLOYED, LockState.LOCKED,
+ SubState.NONE);
+ var instantiationResponse = instantiationProvider.updateAutomationComposition(compositionId,
+ automationCompositionTarget);
+
+ verify(supervisionAcHandler).migrate(any());
+ InstantiationUtils.assertInstantiationResponse(instantiationResponse, automationCompositionTarget);
+
+ }
+
+ @Test
+ void testVersionCompatibility() {
+ var acProvider = mock(AutomationCompositionProvider.class);
+ var acDefinitionProvider = mock(AcDefinitionProvider.class);
+ var supervisionAcHandler = mock(SupervisionAcHandler.class);
+ var participantProvider = mock(ParticipantProvider.class);
+ var newDefinition = new PfConceptKey("policy.clamp.element", "1.2.3");
+ var oldDefinition = new PfConceptKey("policy.clamp.element", "2.2.3");
+
+ var instantiationProvider = new AutomationCompositionInstantiationProvider(acProvider, acDefinitionProvider,
+ new AcInstanceStateResolver(), supervisionAcHandler, participantProvider,
+ new AcRuntimeParameterGroup());
+ var instanceId = UUID.randomUUID();
+ assertDoesNotThrow(() -> {
+ instantiationProvider.checkCompatibility(newDefinition, oldDefinition, instanceId);
+ }, "No exception for major version update");
+
+ // Not compatible
+ newDefinition.setName("policy.clamp.newElement");
+ newDefinition.setVersion("2.2.4");
+
+ assertThatThrownBy(() -> instantiationProvider
+ .checkCompatibility(newDefinition, oldDefinition, instanceId))
+ .hasMessageContaining("is not compatible");
}
@Test
@@ -396,7 +503,7 @@ class AutomationCompositionInstantiationProviderTest {
var acMigrate = new AutomationComposition(automationComposition);
acMigrate.setCompositionTargetId(compositionTargetId);
- automationComposition.getElements().clear();
+ automationComposition.setDeployState(DeployState.DEPLOYING);
var supervisionAcHandler = mock(SupervisionAcHandler.class);
var participantProvider = mock(ParticipantProvider.class);
@@ -406,7 +513,7 @@ class AutomationCompositionInstantiationProviderTest {
assertThatThrownBy(() -> instantiationProvider
.updateAutomationComposition(automationComposition.getCompositionId(), acMigrate))
- .hasMessageStartingWith("Element id not present");
+ .hasMessageStartingWith("Not allowed to MIGRATE in the state DEPLOYING");
}
@Test
@@ -433,7 +540,8 @@ class AutomationCompositionInstantiationProviderTest {
var acMigrate = new AutomationComposition(automationComposition);
acMigrate.setCompositionTargetId(compositionTargetId);
- automationComposition.getElements().clear();
+ automationComposition.setDeployState(DeployState.DEPLOYING);
+ automationComposition.setPrecheck(true);
var supervisionAcHandler = mock(SupervisionAcHandler.class);
var participantProvider = mock(ParticipantProvider.class);
@@ -443,7 +551,7 @@ class AutomationCompositionInstantiationProviderTest {
assertThatThrownBy(() -> instantiationProvider
.updateAutomationComposition(automationComposition.getCompositionId(), acMigrate))
- .hasMessageStartingWith("Element id not present");
+ .hasMessageStartingWith("Not allowed to NONE in the state DEPLOYING");
}
@Test
@@ -635,10 +743,14 @@ class AutomationCompositionInstantiationProviderTest {
var automationComposition =
InstantiationUtils.getAutomationCompositionFromResource(AC_INSTANTIATION_CREATE_JSON, "Crud");
automationComposition.setCompositionId(compositionId);
+ var message = """
+ "AutomationComposition" INVALID, item has status INVALID
+ item "ServiceTemplate.state" value "COMMISSIONED" INVALID, Commissioned automation composition \
+ definition not primed
+ """;
+
assertThatThrownBy(() -> provider.createAutomationComposition(compositionId, automationComposition))
- .hasMessageMatching("\"AutomationComposition\" INVALID, item has status INVALID\n"
- + " item \"ServiceTemplate.state\" value \"COMMISSIONED\" INVALID,"
- + " Commissioned automation composition definition not primed\n");
+ .hasMessageMatching(message);
}
@Test
diff --git a/runtime-acm/src/test/resources/rest/acm/AutomationCompositionMigrate.json b/runtime-acm/src/test/resources/rest/acm/AutomationCompositionMigrate.json
new file mode 100644
index 000000000..d74970d4f
--- /dev/null
+++ b/runtime-acm/src/test/resources/rest/acm/AutomationCompositionMigrate.json
@@ -0,0 +1,30 @@
+{
+ "name": "PMSHInstance1",
+ "version": "1.0.1",
+ "compositionId": "709c62b3-8918-41b9-a747-d21eb79c6c40",
+ "deployState": "DEPLOYED",
+ "lockState": "LOCKED",
+ "description": "PMSH automation composition instance 0",
+ "elements": {
+ "709c62b3-8918-41b9-a747-d21eb79c6c21": {
+ "id": "709c62b3-8918-41b9-a747-d21eb79c6c21",
+ "definition": {
+ "name": "org.onap.domain.database.PMSH_K8SMicroserviceAutomationCompositionElement",
+ "version": "1.2.3"
+ },
+ "deployState": "DEPLOYED",
+ "lockState": "LOCKED",
+ "description": "Automation composition element for the K8S microservice for PMSH"
+ },
+ "709c62b3-8918-41b9-a747-d21eb79c6c22": {
+ "id": "709c62b3-8918-41b9-a747-d21eb79c6c22",
+ "definition": {
+ "name": "org.onap.domain.database.Http_PMSHMicroserviceAutomationCompositionElement",
+ "version": "1.2.3"
+ },
+ "deployState": "DEPLOYED",
+ "lockState": "LOCKED",
+ "description": "Automation composition element for the operational policy for Performance Management Subscription Handling"
+ }
+ }
+} \ No newline at end of file