From: aravind.est Date: Tue, 3 Oct 2023 14:32:17 +0000 (+0100) Subject: Add status check between ACM depriming and deletion X-Git-Tag: 0.0.1~28 X-Git-Url: https://gerrit.o-ran-sc.org/r/gitweb?a=commitdiff_plain;h=dc1302f9cc01b098fc1a44bcdd115350c032ee45;p=nonrtric%2Fplt%2Frappmanager.git Add status check between ACM depriming and deletion Openapi maven version upgraded. ACM model library version upgraded. Status check added between depriming and deletion. Code coverage improvement in ACM. Issue-ID: NONRTRIC-929 Signed-off-by: aravind.est Change-Id: I44a47145141f50089a92a34276c754ab3e6ab5e6 --- diff --git a/pom.xml b/pom.xml index 0a8f1d1..6981e08 100755 --- a/pom.xml +++ b/pom.xml @@ -48,10 +48,10 @@ 17 - 6.6.0 + 7.0.1 0.30.0 0.8.10 - 6.4.2 + 6.4.3 0.2.6 3.2.1 6.0.8 diff --git a/rapp-manager-acm/pom.xml b/rapp-manager-acm/pom.xml index 0e01bb4..9cbf7ab 100755 --- a/rapp-manager-acm/pom.xml +++ b/rapp-manager-acm/pom.xml @@ -80,6 +80,11 @@ spring-boot-starter-test test + + org.springframework.boot + spring-boot-starter-web + test + @@ -110,7 +115,7 @@ ToscaNodeTemplate=org.onap.policy.models.tosca.authorative.concepts.ToscaNodeTemplate, AutomationCompositions=org.onap.policy.clamp.models.acm.concepts.AutomationCompositions, AutomationComposition=org.onap.policy.clamp.models.acm.concepts.AutomationComposition, - + AutomationCompositionDefinition=org.onap.policy.clamp.models.acm.concepts.AutomationCompositionDefinition, SimpleResponse=org.onap.policy.clamp.models.acm.messages.rest.SimpleResponse, AcTypeStateUpdate=org.onap.policy.clamp.models.acm.messages.rest.commissioning.AcTypeStateUpdate, AcInstanceStateUpdate=org.onap.policy.clamp.models.acm.messages.rest.instantiation.AcInstanceStateUpdate, diff --git a/rapp-manager-acm/src/main/java/com/oransc/rappmanager/acm/configuration/JacksonMessageConverterConfiguration.java b/rapp-manager-acm/src/main/java/com/oransc/rappmanager/acm/configuration/JacksonMessageConverterConfiguration.java new file mode 100755 index 0000000..970c70b --- /dev/null +++ b/rapp-manager-acm/src/main/java/com/oransc/rappmanager/acm/configuration/JacksonMessageConverterConfiguration.java @@ -0,0 +1,37 @@ +/*- + * ============LICENSE_START====================================================================== + * Copyright (C) 2023 Nordix Foundation. 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END======================================================================== + */ + +package com.oransc.rappmanager.acm.configuration; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.deser.std.StdKeyDeserializer; +import com.fasterxml.jackson.databind.module.SimpleModule; +import org.onap.policy.models.tosca.authorative.concepts.ToscaEntityKey; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; + +@Configuration +public class JacksonMessageConverterConfiguration extends MappingJackson2HttpMessageConverter { + + public JacksonMessageConverterConfiguration(ObjectMapper objectMapper) { + super(objectMapper); + //This is to fix the AutomationCompositionDefinition deserialization issue via Openapi generator + objectMapper.registerModule( + new SimpleModule().addKeyDeserializer(ToscaEntityKey.class, StdKeyDeserializer.forType(String.class))); + } +} diff --git a/rapp-manager-acm/src/main/java/com/oransc/rappmanager/acm/service/AcmDeployer.java b/rapp-manager-acm/src/main/java/com/oransc/rappmanager/acm/service/AcmDeployer.java index 0cfeb9b..26d4418 100755 --- a/rapp-manager-acm/src/main/java/com/oransc/rappmanager/acm/service/AcmDeployer.java +++ b/rapp-manager-acm/src/main/java/com/oransc/rappmanager/acm/service/AcmDeployer.java @@ -31,6 +31,7 @@ import com.oransc.rappmanager.models.statemachine.RappInstanceStateMachine; import java.util.UUID; import java.util.concurrent.TimeUnit; import lombok.RequiredArgsConstructor; +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.DeployState; import org.onap.policy.clamp.models.acm.concepts.LockState; @@ -190,9 +191,12 @@ public class AcmDeployer implements RappDeployer { public boolean deprimeRapp(Rapp rapp) { try { primeACMComposition(rapp.getCompositionId(), PrimeOrder.DEPRIME); - CommissioningResponse commissioningResponse = deleteComposition(rapp.getCompositionId()); - if (commissioningResponse != null) { - return true; + if (waitForCompositionTargetState(rapp.getCompositionId(), AcTypeState.COMMISSIONED)) { + CommissioningResponse commissioningResponse = deleteComposition(rapp.getCompositionId()); + if (commissioningResponse != null) { + rapp.setCompositionId(null); + return true; + } } } catch (Exception e) { logger.warn("Failed deprime automation composition", e); @@ -201,6 +205,31 @@ public class AcmDeployer implements RappDeployer { return false; } + boolean waitForCompositionTargetState(UUID compositionId, AcTypeState acTypeState) { + boolean targetCompositionStateTransition = false; + try { + for (int i = 0; i < acmConfiguration.getMaxRetries(); i++) { + logger.debug("Composition state check {}", i + 1); + if (isCompositionStateEquals(compositionId, acTypeState)) { + logger.debug("Composition {} state is {}", compositionId, acTypeState); + targetCompositionStateTransition = true; + break; + } else { + TimeUnit.SECONDS.sleep(acmConfiguration.getRetryInterval()); + } + } + } catch (Exception e) { + logger.warn("Unable to get composition state for composition {}", compositionId, e); + Thread.currentThread().interrupt(); + } + return targetCompositionStateTransition; + } + + boolean isCompositionStateEquals(UUID compositionId, AcTypeState acTypeState) { + return automationCompositionDefinitionApiClient.getCompositionDefinition(compositionId, UUID.randomUUID()) + .getState().equals(acTypeState); + } + public void syncRappInstanceStatus(UUID compositionId, RappInstance rappInstance) { if (rappInstance.getAcm().getAcmInstanceId() != null) { try { diff --git a/rapp-manager-acm/src/test/java/com/oransc/rappmanager/acm/service/AcmDeployerTest.java b/rapp-manager-acm/src/test/java/com/oransc/rappmanager/acm/service/AcmDeployerTest.java index b866b5a..5268640 100755 --- a/rapp-manager-acm/src/test/java/com/oransc/rappmanager/acm/service/AcmDeployerTest.java +++ b/rapp-manager-acm/src/test/java/com/oransc/rappmanager/acm/service/AcmDeployerTest.java @@ -28,6 +28,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.springframework.test.web.client.match.MockRestRequestMatchers.method; import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; +import static org.springframework.test.web.client.response.MockRestResponseCreators.withServerError; import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus; import com.fasterxml.jackson.core.JsonProcessingException; @@ -45,16 +46,23 @@ import com.oransc.rappmanager.models.statemachine.RappInstanceStateMachine; import com.oransc.rappmanager.models.statemachine.RappInstanceStateMachineConfig; import java.io.IOException; import java.util.UUID; +import java.util.stream.Stream; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +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.messages.rest.commissioning.CommissioningResponse; import org.onap.policy.clamp.models.acm.messages.rest.commissioning.PrimeOrder; import org.onap.policy.clamp.models.acm.messages.rest.instantiation.InstantiationResponse; +import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; @@ -84,11 +92,12 @@ class AcmDeployerTest { RappInstanceStateMachine rappInstanceStateMachine; @Autowired RappCsarConfigurationHandler rappCsarConfigurationHandler; + @Autowired + ObjectMapper objectMapper; RappResourceBuilder rappResourceBuilder = new RappResourceBuilder(); private final String validRappFile = "valid-rapp-package.csar"; String validCsarFileLocation = "src/test/resources/"; - ObjectMapper objectMapper = new ObjectMapper(); String URI_ACM_COMPOSITIONS, URI_ACM_COMPOSITION, URI_ACM_INSTANCES, URI_ACM_INSTANCE; @BeforeAll @@ -258,17 +267,47 @@ class AcmDeployerTest { } @Test - void testSyncRappInstanceStatus() throws JsonProcessingException { + void testUndeployRappInstanceACMErrorFailure() throws JsonProcessingException { UUID compositionId = UUID.randomUUID(); + UUID rappId = UUID.randomUUID(); UUID instanceId = UUID.randomUUID(); - expectAcmGetInstanceToReturnState(compositionId, instanceId, DeployState.UNDEPLOYING, LockState.UNLOCKING, + Rapp rapp = Rapp.builder().name(rappId.toString()).packageName(validRappFile).compositionId(compositionId) + .state(RappState.PRIMED).build(); + expectAcmGetInstanceToReturnState(compositionId, instanceId, DeployState.DEPLOYED, LockState.LOCKED, ExpectedCount.once()); + mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_INSTANCE, compositionId, instanceId))) + .andExpect(method(HttpMethod.PUT)).andRespond(withStatus(HttpStatus.ACCEPTED)); + mockServer.expect(ExpectedCount.manyTimes(), + requestTo(String.format(URI_ACM_INSTANCE, compositionId, instanceId))).andExpect(method(HttpMethod.GET)) + .andRespond(withServerError()); + RappInstance rappInstance = rappResourceBuilder.getRappInstance(); + rappInstance.getAcm().setAcmInstanceId(instanceId); + rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId()); + boolean rappUndeployStateActual = acmDeployer.undeployRappInstance(rapp, rappInstance); + mockServer.verify(); + assertFalse(rappUndeployStateActual); + } + + @ParameterizedTest + @MethodSource("getAcmStatusEventMap") + void testSyncRappInstanceStatus(DeployState deployState, LockState lockState, RappEvent rappEvent) + throws JsonProcessingException { + UUID compositionId = UUID.randomUUID(); + UUID instanceId = UUID.randomUUID(); + expectAcmGetInstanceToReturnState(compositionId, instanceId, deployState, lockState, ExpectedCount.once()); RappInstance rappInstance = rappResourceBuilder.getRappInstance(); rappInstance.getAcm().setAcmInstanceId(instanceId); rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId()); acmDeployer.syncRappInstanceStatus(compositionId, rappInstance); mockServer.verify(); - verify(rappInstanceStateMachine, times(1)).sendRappInstanceEvent(rappInstance, RappEvent.UNDEPLOYING); + verify(rappInstanceStateMachine, times(1)).sendRappInstanceEvent(rappInstance, rappEvent); + } + + private static Stream getAcmStatusEventMap() { + return Stream.of(Arguments.of(DeployState.UNDEPLOYING, LockState.UNLOCKING, RappEvent.UNDEPLOYING), + Arguments.of(DeployState.DEPLOYED, LockState.LOCKED, RappEvent.ACMDEPLOYED), + Arguments.of(DeployState.DEPLOYING, LockState.LOCKING, RappEvent.DEPLOYING), + Arguments.of(DeployState.UNDEPLOYED, LockState.UNLOCKED, RappEvent.ACMUNDEPLOYED)); } @Test @@ -357,6 +396,45 @@ class AcmDeployerTest { commissioningResponseExpected.setCompositionId(compositionId); mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId))) .andExpect(method(HttpMethod.PUT)).andRespond(withStatus(HttpStatus.OK)); + AutomationCompositionDefinition automationCompositionDefinition = + getAutomationCompositionDefinition(compositionId, AcTypeState.COMMISSIONED); + mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId))) + .andExpect(method(HttpMethod.GET)).andRespond( + withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON) + .body(objectMapper.writeValueAsString(automationCompositionDefinition))); + mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId))) + .andExpect(method(HttpMethod.DELETE)).andRespond( + withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON) + .body(objectMapper.writeValueAsString(commissioningResponseExpected))); + + boolean deprimeRapp = acmDeployer.deprimeRapp(rapp); + mockServer.verify(); + assertTrue(deprimeRapp); + } + + @Test + void testDeprimeRappClientRetry() throws JsonProcessingException { + UUID compositionId = UUID.randomUUID(); + RappResources rappResources = rappResourceBuilder.getResources(); + Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile) + .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED) + .compositionId(compositionId).rappResources(rappResources).build(); + + CommissioningResponse commissioningResponseExpected = new CommissioningResponse(); + commissioningResponseExpected.setCompositionId(compositionId); + mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId))) + .andExpect(method(HttpMethod.PUT)).andRespond(withStatus(HttpStatus.OK)); + AutomationCompositionDefinition automationCompositionDefinition = + getAutomationCompositionDefinition(compositionId, AcTypeState.DEPRIMING); + mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId))) + .andExpect(method(HttpMethod.GET)).andRespond( + withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON) + .body(objectMapper.writeValueAsString(automationCompositionDefinition))); + automationCompositionDefinition = getAutomationCompositionDefinition(compositionId, AcTypeState.COMMISSIONED); + mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId))) + .andExpect(method(HttpMethod.GET)).andRespond( + withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON) + .body(objectMapper.writeValueAsString(automationCompositionDefinition))); mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId))) .andExpect(method(HttpMethod.DELETE)).andRespond( withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON) @@ -368,7 +446,7 @@ class AcmDeployerTest { } @Test - void testDeprimeFailureRapp() { + void testDeprimeFailureRapp() throws JsonProcessingException { UUID compositionId = UUID.randomUUID(); RappResources rappResources = rappResourceBuilder.getResources(); Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile) @@ -377,6 +455,12 @@ class AcmDeployerTest { mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId))) .andExpect(method(HttpMethod.PUT)).andRespond(withStatus(HttpStatus.OK)); + AutomationCompositionDefinition automationCompositionDefinition = + getAutomationCompositionDefinition(compositionId, AcTypeState.COMMISSIONED); + mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId))) + .andExpect(method(HttpMethod.GET)).andRespond( + withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON) + .body(objectMapper.writeValueAsString(automationCompositionDefinition))); mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId))) .andExpect(method(HttpMethod.DELETE)).andRespond(withStatus(HttpStatus.INTERNAL_SERVER_ERROR)); @@ -385,6 +469,45 @@ class AcmDeployerTest { assertFalse(deprimeRapp); } + @Test + void testDeprimeACMStatusFailureRapp() throws JsonProcessingException { + UUID compositionId = UUID.randomUUID(); + RappResources rappResources = rappResourceBuilder.getResources(); + Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile) + .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED) + .compositionId(compositionId).rappResources(rappResources).build(); + + mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId))) + .andExpect(method(HttpMethod.PUT)).andRespond(withStatus(HttpStatus.OK)); + AutomationCompositionDefinition automationCompositionDefinition = + getAutomationCompositionDefinition(compositionId, AcTypeState.DEPRIMING); + mockServer.expect(ExpectedCount.manyTimes(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId))) + .andExpect(method(HttpMethod.GET)).andRespond( + withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON) + .body(objectMapper.writeValueAsString(automationCompositionDefinition))); + + boolean deprimeRapp = acmDeployer.deprimeRapp(rapp); + mockServer.verify(); + assertFalse(deprimeRapp); + } + + @Test + void testDeprimeACMStatusErrorRapp() { + UUID compositionId = UUID.randomUUID(); + RappResources rappResources = rappResourceBuilder.getResources(); + Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile) + .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED) + .compositionId(compositionId).rappResources(rappResources).build(); + + mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId))) + .andExpect(method(HttpMethod.PUT)).andRespond(withStatus(HttpStatus.OK)); + mockServer.expect(ExpectedCount.manyTimes(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId))) + .andExpect(method(HttpMethod.GET)).andRespond(withServerError()); + boolean deprimeRapp = acmDeployer.deprimeRapp(rapp); + mockServer.verify(); + assertFalse(deprimeRapp); + } + @Test void testDeprimeExceptionRapp() { Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile) @@ -432,4 +555,12 @@ class AcmDeployerTest { withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON) .body(objectMapper.writeValueAsString(automationCompositionDeployed))); } + + AutomationCompositionDefinition getAutomationCompositionDefinition(UUID compositionId, AcTypeState acTypeState) { + AutomationCompositionDefinition automationCompositionDefinition = new AutomationCompositionDefinition(); + automationCompositionDefinition.setCompositionId(compositionId); + automationCompositionDefinition.setState(acTypeState); + automationCompositionDefinition.setServiceTemplate(new ToscaServiceTemplate()); + return automationCompositionDefinition; + } } diff --git a/rapp-manager-acm/src/test/java/com/oransc/rappmanager/acm/service/BeanTestConfiguration.java b/rapp-manager-acm/src/test/java/com/oransc/rappmanager/acm/service/BeanTestConfiguration.java index f59f8fb..a2c2f67 100755 --- a/rapp-manager-acm/src/test/java/com/oransc/rappmanager/acm/service/BeanTestConfiguration.java +++ b/rapp-manager-acm/src/test/java/com/oransc/rappmanager/acm/service/BeanTestConfiguration.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.oransc.rappmanager.acm.ApiClient; import com.oransc.rappmanager.acm.configuration.ACMConfiguration; +import com.oransc.rappmanager.acm.configuration.JacksonMessageConverterConfiguration; import com.oransc.rappmanager.acm.rest.AutomationCompositionDefinitionApiClient; import com.oransc.rappmanager.acm.rest.AutomationCompositionInstanceApiClient; import com.oransc.rappmanager.acm.rest.ParticipantMonitoringApiClient; @@ -40,8 +41,10 @@ public class BeanTestConfiguration { } @Bean - public RestTemplate restTemplate(RestTemplateBuilder builder) { - return builder.build(); + public RestTemplate restTemplate(RestTemplateBuilder builder, ObjectMapper objectMapper) { + RestTemplate restTemplate = builder.build(); + restTemplate.getMessageConverters().add(new JacksonMessageConverterConfiguration(objectMapper)); + return restTemplate; } @Bean("acmApiClient")