diff options
Diffstat (limited to 'runtime-acm')
3 files changed, 208 insertions, 32 deletions
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 |