From: aravind.est Date: Wed, 20 Sep 2023 11:22:18 +0000 (+0100) Subject: Add DME integration X-Git-Tag: 0.0.1~39 X-Git-Url: https://gerrit.o-ran-sc.org/r/gitweb?a=commitdiff_plain;h=refs%2Fchanges%2F03%2F11803%2F1;hp=cc9f90ef6d725e90cf477ef6b83ec5a2948127b3;p=nonrtric%2Fplt%2Frappmanager.git Add DME integration Integrated DME to create info type, producer and consumer as per the configuration from rApp package. Issue-ID: NONRTRIC-913 Signed-off-by: aravind.est Change-Id: Ib7a41f8a4200503f3a0b0ffee8e80a0cda3c0871 --- diff --git a/pom.xml b/pom.xml index a0f222a..9491a02 100755 --- a/pom.xml +++ b/pom.xml @@ -36,6 +36,7 @@ rapp-manager-models rapp-manager-acm rapp-manager-sme + rapp-manager-dme rapp-manager-application 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 41f4bae..eb93ed3 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 @@ -25,7 +25,6 @@ import com.oransc.rappmanager.models.RappDeployer; import com.oransc.rappmanager.models.csar.RappCsarConfigurationHandler; import com.oransc.rappmanager.models.rapp.Rapp; import com.oransc.rappmanager.models.rapp.RappEvent; -import com.oransc.rappmanager.models.rapp.RappState; import com.oransc.rappmanager.models.rappinstance.RappACMInstance; import com.oransc.rappmanager.models.rappinstance.RappInstance; import com.oransc.rappmanager.models.statemachine.RappInstanceStateMachine; @@ -172,7 +171,6 @@ public class AcmDeployer implements RappDeployer { rapp.setCompositionId(commissioningResponse.getCompositionId()); logger.info("Priming automation Composition"); primeACMComposition(commissioningResponse.getCompositionId(), PrimeOrder.PRIME); - rapp.setState(RappState.PRIMED); return true; } else { logger.error("Failed to create automation composition"); @@ -189,7 +187,6 @@ public class AcmDeployer implements RappDeployer { primeACMComposition(rapp.getCompositionId(), PrimeOrder.DEPRIME); CommissioningResponse commissioningResponse = deleteComposition(rapp.getCompositionId()); if (commissioningResponse != null) { - rapp.setState(RappState.COMMISSIONED); return true; } } catch (Exception e) { 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 59e69a6..f3016b0 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 @@ -287,8 +287,6 @@ class AcmDeployerTest { boolean primeRapp = acmDeployer.primeRapp(rapp); mockServer.verify(); assertTrue(primeRapp); - assertEquals(RappState.PRIMED, rapp.getState()); - } @Test diff --git a/rapp-manager-acm/src/test/resources/valid-rapp-package.csar b/rapp-manager-acm/src/test/resources/valid-rapp-package.csar index b8b36e3..398a516 100755 Binary files a/rapp-manager-acm/src/test/resources/valid-rapp-package.csar and b/rapp-manager-acm/src/test/resources/valid-rapp-package.csar differ diff --git a/rapp-manager-application/pom.xml b/rapp-manager-application/pom.xml index ee830cf..5bd7dd1 100755 --- a/rapp-manager-application/pom.xml +++ b/rapp-manager-application/pom.xml @@ -53,6 +53,11 @@ rapp-manager-sme ${project.version} + + org.o-ran-sc.nonrtric.plt.rappmanager + rapp-manager-dme + ${project.version} + org.onap.policy.clamp policy-clamp-models diff --git a/rapp-manager-application/src/main/java/com/oransc/rappmanager/BeanConfiguration.java b/rapp-manager-application/src/main/java/com/oransc/rappmanager/BeanConfiguration.java index 0d3bd3d..4e7f7fd 100755 --- a/rapp-manager-application/src/main/java/com/oransc/rappmanager/BeanConfiguration.java +++ b/rapp-manager-application/src/main/java/com/oransc/rappmanager/BeanConfiguration.java @@ -23,6 +23,9 @@ import com.oransc.rappmanager.acm.configuration.ACMConfiguration; import com.oransc.rappmanager.acm.rest.AutomationCompositionDefinitionApiClient; import com.oransc.rappmanager.acm.rest.AutomationCompositionInstanceApiClient; import com.oransc.rappmanager.acm.rest.ParticipantMonitoringApiClient; +import com.oransc.rappmanager.dme.configuration.DmeConfiguration; +import com.oransc.rappmanager.dme.rest.DataConsumerApiClient; +import com.oransc.rappmanager.dme.rest.DataProducerRegistrationApiClient; import com.oransc.rappmanager.sme.configuration.SmeConfiguration; import com.oransc.rappmanager.sme.provider.rest.DefaultApiClient; import lombok.RequiredArgsConstructor; @@ -40,6 +43,7 @@ public class BeanConfiguration { private final ACMConfiguration acmConfiguration; private final SmeConfiguration smeConfiguration; + private final DmeConfiguration dmeConfiguration; @Bean public RestTemplate restTemplate(RestTemplateBuilder builder) { @@ -108,6 +112,23 @@ public class BeanConfiguration { return new com.oransc.rappmanager.sme.invoker.rest.DefaultApiClient(apiClient); } + @Bean + public com.oransc.rappmanager.dme.ApiClient dmeApiClient(RestTemplate restTemplate) { + com.oransc.rappmanager.dme.ApiClient apiClient = new com.oransc.rappmanager.dme.ApiClient(restTemplate); + return apiClient.setBasePath(dmeConfiguration.getBaseUrl()); + } + + @Bean + public DataProducerRegistrationApiClient dataProducerRegistrationApiClient( + com.oransc.rappmanager.dme.ApiClient apiClient) { + return new DataProducerRegistrationApiClient(apiClient); + } + + @Bean + public DataConsumerApiClient dataConsumerApiClient(com.oransc.rappmanager.dme.ApiClient apiClient) { + return new DataConsumerApiClient(apiClient); + } + @Bean public CacheManager cacheManager() { return new ConcurrentMapCacheManager(); diff --git a/rapp-manager-application/src/main/java/com/oransc/rappmanager/service/RappService.java b/rapp-manager-application/src/main/java/com/oransc/rappmanager/service/RappService.java index 6411b2b..1b9fb68 100755 --- a/rapp-manager-application/src/main/java/com/oransc/rappmanager/service/RappService.java +++ b/rapp-manager-application/src/main/java/com/oransc/rappmanager/service/RappService.java @@ -19,6 +19,7 @@ package com.oransc.rappmanager.service; import com.oransc.rappmanager.acm.service.AcmDeployer; +import com.oransc.rappmanager.dme.service.DmeDeployer; import com.oransc.rappmanager.models.rapp.Rapp; import com.oransc.rappmanager.models.rapp.RappEvent; import com.oransc.rappmanager.models.rapp.RappState; @@ -37,13 +38,16 @@ public class RappService { private final AcmDeployer acmDeployer; private final SmeDeployer smeDeployer; + private final DmeDeployer dmeDeployer; private final RappInstanceStateMachine rappInstanceStateMachine; private static final String STATE_TRANSITION_NOT_PERMITTED = "State transition from %s to %s is not permitted."; public ResponseEntity primeRapp(Rapp rapp) { if (rapp.getState().equals(RappState.COMMISSIONED)) { rapp.setState(RappState.PRIMING); - if (!acmDeployer.primeRapp(rapp)) { + if (acmDeployer.primeRapp(rapp) && dmeDeployer.primeRapp(rapp)) { + rapp.setState(RappState.PRIMED); + } else { rapp.setState(RappState.COMMISSIONED); } return ResponseEntity.ok().build(); @@ -57,7 +61,9 @@ public class RappService { public ResponseEntity deprimeRapp(Rapp rapp) { if (rapp.getState().equals(RappState.PRIMED) && rapp.getRappInstances().isEmpty()) { rapp.setState(RappState.DEPRIMING); - if (!acmDeployer.deprimeRapp(rapp)) { + if (acmDeployer.deprimeRapp(rapp) && dmeDeployer.deprimeRapp(rapp)) { + rapp.setState(RappState.COMMISSIONED); + } else { rapp.setState(RappState.PRIMED); } return ResponseEntity.ok().build(); @@ -75,8 +81,8 @@ public class RappService { public ResponseEntity deployRappInstance(Rapp rapp, RappInstance rappInstance) { if (rappInstance.getState().equals(RappInstanceState.UNDEPLOYED)) { rappInstanceStateMachine.sendRappInstanceEvent(rappInstance, RappEvent.DEPLOYING); - if (acmDeployer.deployRappInstance(rapp, rappInstance) && smeDeployer.deployRappInstance(rapp, - rappInstance)) { + if (acmDeployer.deployRappInstance(rapp, rappInstance) && smeDeployer.deployRappInstance(rapp, rappInstance) + && dmeDeployer.deployRappInstance(rapp, rappInstance)) { return ResponseEntity.accepted().build(); } return ResponseEntity.status(HttpStatus.BAD_GATEWAY).build(); @@ -91,7 +97,7 @@ public class RappService { if (rappInstance.getState().equals(RappInstanceState.DEPLOYED)) { rappInstanceStateMachine.sendRappInstanceEvent(rappInstance, RappEvent.UNDEPLOYING); if (acmDeployer.undeployRappInstance(rapp, rappInstance) && smeDeployer.undeployRappInstance(rapp, - rappInstance)) { + rappInstance) && dmeDeployer.undeployRappInstance(rapp, rappInstance)) { return ResponseEntity.accepted().build(); } return ResponseEntity.status(HttpStatus.BAD_GATEWAY).build(); diff --git a/rapp-manager-application/src/main/resources/application.yaml b/rapp-manager-application/src/main/resources/application.yaml index 0672fd2..8a30abb 100755 --- a/rapp-manager-application/src/main/resources/application.yaml +++ b/rapp-manager-application/src/main/resources/application.yaml @@ -1,23 +1,25 @@ rappmanager: csarlocation: src/test/resources/csar acm: - baseurl: http://10.101.3.22:30442/onap/policy/clamp/acm/v2/ + baseurl: http://10.101.2.41:30442/onap/policy/clamp/acm/v2/ username: runtimeUser password: zb!XztG34 - maxRetries: 3 + maxRetries: 10 retryInterval: 2 #seconds sme: - baseurl: http://localhost:60821 #http://10.101.3.22:61761 + baseurl: http://localhost:56571 #http://10.101.3.22:61761 providerBasePath: /api-provider-management/v1/ invokerBasePath: /api-invoker-management/v1/ publishApiBasePath: /published-apis/v1/ maxRetries: 3 retryInterval: 2 #seconds + dme: + baseurl: http://localhost:63475 #http://10.101.3.22:61761 logging: level: root: INFO - com.oransc.rapps: DEBUG + com.oransc: DEBUG org.apache.http: DEBUG httpclient.wire: DEBUG org.springframework.web.client.RestTemplate: TRACE diff --git a/rapp-manager-application/src/main/resources/resource-csar/Files/Acm/definition/compositions.json b/rapp-manager-application/src/main/resources/resource-csar/Files/Acm/definition/compositions.json index bc568ad..e64d882 100755 --- a/rapp-manager-application/src/main/resources/resource-csar/Files/Acm/definition/compositions.json +++ b/rapp-manager-application/src/main/resources/resource-csar/Files/Acm/definition/compositions.json @@ -263,7 +263,7 @@ "name": "org.onap.policy.clamp.acm.KserveParticipant", "version": "2.3.4" }, - "uninitializedToPassiveTimeout": 60, + "uninitializedToPassiveTimeout": 300, "statusCheckInterval": 30 } }, diff --git a/rapp-manager-application/src/main/resources/resource-csar/Files/Acm/instances/k8s-instance.json b/rapp-manager-application/src/main/resources/resource-csar/Files/Acm/instances/k8s-instance.json index 286a943..562b5a2 100755 --- a/rapp-manager-application/src/main/resources/resource-csar/Files/Acm/instances/k8s-instance.json +++ b/rapp-manager-application/src/main/resources/resource-csar/Files/Acm/instances/k8s-instance.json @@ -22,7 +22,7 @@ "podName": "ransliceassurance", "repository": { "repoName": "local", - "address": "http://10.101.3.22:8879/charts" + "address": "http://10.101.2.41:8879/charts" } } } diff --git a/rapp-manager-application/src/main/resources/resource-csar/Files/Dme/infoconsumers/json-file-consumer.json b/rapp-manager-application/src/main/resources/resource-csar/Files/Dme/infoconsumers/json-file-consumer.json new file mode 100755 index 0000000..b18a643 --- /dev/null +++ b/rapp-manager-application/src/main/resources/resource-csar/Files/Dme/infoconsumers/json-file-consumer.json @@ -0,0 +1,13 @@ +{ + "info_type_id": "json-file-data-from-filestore", + "job_owner": "console", + "status_notification_uri": "http://callback.nonrtric:80/post", + "job_definition": { + "db-url": "http://influxdb2.nonrtric:8086", + "db-org": "est", + "db-bucket": "pm-bucket", + "db-token": "token", + "filterType": "pmdata", + "filter": {} + } +} \ No newline at end of file diff --git a/rapp-manager-application/src/main/resources/resource-csar/Files/Dme/infoconsumers/xml-file-consumer.json b/rapp-manager-application/src/main/resources/resource-csar/Files/Dme/infoconsumers/xml-file-consumer.json new file mode 100755 index 0000000..b33482a --- /dev/null +++ b/rapp-manager-application/src/main/resources/resource-csar/Files/Dme/infoconsumers/xml-file-consumer.json @@ -0,0 +1,13 @@ +{ + "info_type_id": "xml-file-data-from-filestore", + "job_owner": "console", + "status_notification_uri": "http://callback.nonrtric:80/post", + "job_definition": { + "db-url": "http://influxdb2.nonrtric:8086", + "db-org": "est", + "db-bucket": "pm-bucket", + "db-token": "token", + "filterType": "pmdata", + "filter": {} + } +} \ No newline at end of file diff --git a/rapp-manager-application/src/main/resources/resource-csar/Files/Dme/infoproducers/json-file-data-producer.json b/rapp-manager-application/src/main/resources/resource-csar/Files/Dme/infoproducers/json-file-data-producer.json new file mode 100755 index 0000000..ed74384 --- /dev/null +++ b/rapp-manager-application/src/main/resources/resource-csar/Files/Dme/infoproducers/json-file-data-producer.json @@ -0,0 +1,7 @@ +{ + "info_job_callback_url": "http://localhost/jsonproducerjobcallback", + "info_producer_supervision_callback_url": "http://localhost/jsonproducersupervisioncallback", + "supported_info_types": [ + "json-file-data-from-filestore" + ] +} \ No newline at end of file diff --git a/rapp-manager-application/src/main/resources/resource-csar/Files/Dme/infoproducers/xml-file-data-producer.json b/rapp-manager-application/src/main/resources/resource-csar/Files/Dme/infoproducers/xml-file-data-producer.json new file mode 100755 index 0000000..33075de --- /dev/null +++ b/rapp-manager-application/src/main/resources/resource-csar/Files/Dme/infoproducers/xml-file-data-producer.json @@ -0,0 +1,7 @@ +{ + "info_job_callback_url": "http://localhost/xmlproducerjobcallback", + "info_producer_supervision_callback_url": "http://localhost/xmlproducersupervisioncallback", + "supported_info_types": [ + "xml-file-data-from-filestore" + ] +} \ No newline at end of file diff --git a/rapp-manager-application/src/main/resources/resource-csar/Files/Dme/infotypes/json-file-data-from-filestore.json b/rapp-manager-application/src/main/resources/resource-csar/Files/Dme/infotypes/json-file-data-from-filestore.json new file mode 100755 index 0000000..a5f9d02 --- /dev/null +++ b/rapp-manager-application/src/main/resources/resource-csar/Files/Dme/infotypes/json-file-data-from-filestore.json @@ -0,0 +1,8 @@ +{ + "info_job_data_schema": { + "schema": "http://json-schema.org/draft-07/schema#", + "title": "json-file-data-from-filestore", + "description": "json-file-data-from-filestore", + "type": "object" + } +} \ No newline at end of file diff --git a/rapp-manager-application/src/main/resources/resource-csar/Files/Dme/infotypes/xml-file-data-from-filestore.json b/rapp-manager-application/src/main/resources/resource-csar/Files/Dme/infotypes/xml-file-data-from-filestore.json new file mode 100755 index 0000000..3585603 --- /dev/null +++ b/rapp-manager-application/src/main/resources/resource-csar/Files/Dme/infotypes/xml-file-data-from-filestore.json @@ -0,0 +1,8 @@ +{ + "info_job_data_schema": { + "schema": "http://json-schema.org/draft-07/schema#", + "title": "xml-file-data-to-filestore", + "description": "xml-file-data-to-filestore", + "type": "object" + } +} \ No newline at end of file diff --git a/rapp-manager-application/src/main/resources/resource-csar/asd.mf b/rapp-manager-application/src/main/resources/resource-csar/asd.mf index 39218a5..09818fc 100755 --- a/rapp-manager-application/src/main/resources/resource-csar/asd.mf +++ b/rapp-manager-application/src/main/resources/resource-csar/asd.mf @@ -17,7 +17,24 @@ Source: TOSCA-Metadata/TOSCA.meta Source: Artifacts/Deployment/HELM/free5gc-1.1.3.tgz Source: Artifacts/Deployment/HELM/ueransim-2.0.14.tgz Source: Files/rapp1/rapp.zip -Source: Files/Acm/instantiation.yaml +Source: Files/Acm/definition/compositions.json +Source: Files/Acm/instances/a1pms-instance.json +Source: Files/Acm/instances/k8s-instance.json +Source: Files/Acm/instances/kserve-instance.json +Source: Files/Dme/infoconsumers/json-file-consumer.json +Source: Files/Dme/infoconsumers/xml-file-consumer.json +Source: Files/Dme/infoproducers/json-file-data-producer.json +Source: Files/Dme/infoproducers/xml-file-data-producer.json +Source: Files/Dme/infotypes/json-file-data-from-filestore.json +Source: Files/Dme/infotypes/xml-file-data-from-filestore.json +Source: Files/Sme/invokers/invoker-app1.json +Source: Files/Sme/invokers/invoker-app2.json +Source: Files/Sme/provider/aef-provider-function.json +Source: Files/Sme/provider/amf-provider-function.json +Source: Files/Sme/provider/apf-provider-function.json +Source: Files/Sme/provider/gateway-provider-function.json +Source: Files/Sme/serviceapis/api-set-1.json +Source: Files/Sme/serviceapis/api-set-2.json non_mano_artifact_sets: onap_ves_events: diff --git a/rapp-manager-application/src/test/java/com/oransc/rappmanager/rest/RappInstanceControllerTest.java b/rapp-manager-application/src/test/java/com/oransc/rappmanager/rest/RappInstanceControllerTest.java index d6ba4ce..9eb959a 100755 --- a/rapp-manager-application/src/test/java/com/oransc/rappmanager/rest/RappInstanceControllerTest.java +++ b/rapp-manager-application/src/test/java/com/oransc/rappmanager/rest/RappInstanceControllerTest.java @@ -8,6 +8,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import com.fasterxml.jackson.databind.ObjectMapper; import com.oransc.rappmanager.acm.service.AcmDeployer; +import com.oransc.rappmanager.dme.service.DmeDeployer; import com.oransc.rappmanager.models.rappinstance.DeployOrder; import com.oransc.rappmanager.models.rapp.Rapp; import com.oransc.rappmanager.models.rappinstance.RappInstance; @@ -49,6 +50,9 @@ class RappInstanceControllerTest { @MockBean SmeDeployer smeDeployer; + @MockBean + DmeDeployer dmeDeployer; + @MockBean SmeLifecycleManager smeLifecycleManager; @@ -146,6 +150,7 @@ class RappInstanceControllerTest { rappInstanceDeployOrder.setDeployOrder(DeployOrder.DEPLOY); when(acmDeployer.deployRappInstance(any(), any())).thenReturn(true); when(smeDeployer.deployRappInstance(any(), any())).thenReturn(true); + when(dmeDeployer.deployRappInstance(any(), any())).thenReturn(true); mockMvc.perform(MockMvcRequestBuilders.put("/rapps/{rapp_id}/instance/{instance_id}", rappId, rappInstanceId) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(rappInstanceDeployOrder))) @@ -164,6 +169,7 @@ class RappInstanceControllerTest { rappInstanceDeployOrder.setDeployOrder(DeployOrder.DEPLOY); when(acmDeployer.deployRappInstance(any(), any())).thenReturn(true); when(smeDeployer.deployRappInstance(any(), any())).thenReturn(true); + when(dmeDeployer.deployRappInstance(any(), any())).thenReturn(true); mockMvc.perform(MockMvcRequestBuilders.put("/rapps/{rapp_id}/instance/{instance_id}", rappId, rappInstanceId) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(rappInstanceDeployOrder))) @@ -176,6 +182,7 @@ class RappInstanceControllerTest { rappInstanceDeployOrder.setDeployOrder(DeployOrder.DEPLOY); when(acmDeployer.deployRappInstance(any(), any())).thenReturn(true); when(smeDeployer.deployRappInstance(any(), any())).thenReturn(true); + when(dmeDeployer.deployRappInstance(any(), any())).thenReturn(true); mockMvc.perform(MockMvcRequestBuilders.put("/rapps/{rapp_id}/instance/{instance_id}", UUID.randomUUID(), UUID.randomUUID()).contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(rappInstanceDeployOrder))) @@ -194,6 +201,7 @@ class RappInstanceControllerTest { rappInstanceDeployOrder.setDeployOrder(DeployOrder.UNDEPLOY); when(acmDeployer.undeployRappInstance(any(), any())).thenReturn(true); when(smeDeployer.undeployRappInstance(any(), any())).thenReturn(true); + when(dmeDeployer.undeployRappInstance(any(), any())).thenReturn(true); mockMvc.perform(MockMvcRequestBuilders.put("/rapps/{rapp_id}/instance/{instance_id}", rappId, rappInstanceId) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(rappInstanceDeployOrder))) @@ -212,6 +220,7 @@ class RappInstanceControllerTest { rappInstanceDeployOrder.setDeployOrder(DeployOrder.UNDEPLOY); when(acmDeployer.undeployRappInstance(any(), any())).thenReturn(true); when(smeDeployer.undeployRappInstance(any(), any())).thenReturn(true); + when(dmeDeployer.undeployRappInstance(any(), any())).thenReturn(true); mockMvc.perform(MockMvcRequestBuilders.put("/rapps/{rapp_id}/instance/{instance_id}", rappId, rappInstanceId) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(rappInstanceDeployOrder))) @@ -226,6 +235,7 @@ class RappInstanceControllerTest { rappInstanceDeployOrder.setDeployOrder(DeployOrder.UNDEPLOY); when(acmDeployer.undeployRappInstance(any(), any())).thenReturn(true); when(smeDeployer.undeployRappInstance(any(), any())).thenReturn(true); + when(dmeDeployer.undeployRappInstance(any(), any())).thenReturn(true); mockMvc.perform(MockMvcRequestBuilders.put("/rapps/{rapp_id}/instance/{instance_id}", rappId, rappInstanceId) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(rappInstanceDeployOrder))) diff --git a/rapp-manager-application/src/test/java/com/oransc/rappmanager/service/RappServiceTest.java b/rapp-manager-application/src/test/java/com/oransc/rappmanager/service/RappServiceTest.java index dbb3da7..c50f0b4 100755 --- a/rapp-manager-application/src/test/java/com/oransc/rappmanager/service/RappServiceTest.java +++ b/rapp-manager-application/src/test/java/com/oransc/rappmanager/service/RappServiceTest.java @@ -5,6 +5,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; import com.oransc.rappmanager.acm.service.AcmDeployer; +import com.oransc.rappmanager.dme.service.DmeDeployer; import com.oransc.rappmanager.models.rapp.Rapp; import com.oransc.rappmanager.models.rapp.RappState; import com.oransc.rappmanager.models.rappinstance.RappInstance; @@ -34,6 +35,9 @@ class RappServiceTest { @MockBean SmeDeployer smeDeployer; + @MockBean + DmeDeployer dmeDeployer; + @MockBean SmeLifecycleManager smeLifecycleManager; @@ -44,15 +48,15 @@ class RappServiceTest { private final String validRappFile = "valid-rapp-package.csar"; - private final String invalidRappFile = "invalid-rapp-package.csar"; - @Test void testPrimeRapp() { Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile) .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED).build(); when(acmDeployer.primeRapp(any())).thenReturn(true); + when(dmeDeployer.primeRapp(any())).thenReturn(true); assertEquals(HttpStatus.OK, rappService.primeRapp(rapp).getStatusCode()); + assertEquals(RappState.PRIMED, rapp.getState()); } @Test @@ -67,23 +71,49 @@ class RappServiceTest { Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile) .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED).build(); when(acmDeployer.primeRapp(any())).thenReturn(false); + when(dmeDeployer.primeRapp(any())).thenReturn(true); + assertEquals(HttpStatus.OK, rappService.primeRapp(rapp).getStatusCode()); + assertEquals(RappState.COMMISSIONED, rapp.getState()); + } + @Test + void testPrimeRappDmeFailure() { + Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile) + .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED).build(); + when(acmDeployer.primeRapp(any())).thenReturn(true); + when(dmeDeployer.primeRapp(any())).thenReturn(false); assertEquals(HttpStatus.OK, rappService.primeRapp(rapp).getStatusCode()); + assertEquals(RappState.COMMISSIONED, rapp.getState()); } + @Test void testDeprimeRapp() { Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile) .packageLocation(validCsarFileLocation).state(RappState.PRIMED).build(); when(acmDeployer.deprimeRapp(any())).thenReturn(true); + when(dmeDeployer.deprimeRapp(any())).thenReturn(true); assertEquals(HttpStatus.OK, rappService.deprimeRapp(rapp).getStatusCode()); + assertEquals(RappState.COMMISSIONED, rapp.getState()); } @Test - void testDeprimeRappFailure() { + void testDeprimeRappAcmFailure() { Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile) .packageLocation(validCsarFileLocation).state(RappState.PRIMED).build(); when(acmDeployer.deprimeRapp(any())).thenReturn(false); + when(dmeDeployer.deprimeRapp(any())).thenReturn(true); assertEquals(HttpStatus.OK, rappService.deprimeRapp(rapp).getStatusCode()); + assertEquals(RappState.PRIMED, rapp.getState()); + } + + @Test + void testDeprimeRappDmeFailure() { + Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile) + .packageLocation(validCsarFileLocation).state(RappState.PRIMED).build(); + when(acmDeployer.deprimeRapp(any())).thenReturn(true); + when(dmeDeployer.deprimeRapp(any())).thenReturn(false); + assertEquals(HttpStatus.OK, rappService.deprimeRapp(rapp).getStatusCode()); + assertEquals(RappState.PRIMED, rapp.getState()); } @Test @@ -91,14 +121,16 @@ class RappServiceTest { Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile) .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED).build(); assertEquals(HttpStatus.BAD_REQUEST, rappService.deprimeRapp(rapp).getStatusCode()); + assertEquals(RappState.COMMISSIONED, rapp.getState()); } @Test void testDeprimeRappActiveInstances() { Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile) - .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED) + .packageLocation(validCsarFileLocation).state(RappState.PRIMED) .rappInstances(Map.of(UUID.randomUUID(), new RappInstance())).build(); assertEquals(HttpStatus.BAD_REQUEST, rappService.deprimeRapp(rapp).getStatusCode()); + assertEquals(RappState.PRIMED, rapp.getState()); } @Test @@ -109,6 +141,7 @@ class RappServiceTest { rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId()); when(acmDeployer.deployRappInstance(any(), any())).thenReturn(true); when(smeDeployer.deployRappInstance(any(), any())).thenReturn(true); + when(dmeDeployer.deployRappInstance(any(), any())).thenReturn(true); assertEquals(HttpStatus.ACCEPTED, rappService.deployRappInstance(rapp, rappInstance).getStatusCode()); } @@ -120,6 +153,19 @@ class RappServiceTest { rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId()); when(acmDeployer.deployRappInstance(any(), any())).thenReturn(true); when(smeDeployer.deployRappInstance(any(), any())).thenReturn(false); + when(dmeDeployer.deployRappInstance(any(), any())).thenReturn(true); + assertEquals(HttpStatus.BAD_GATEWAY, rappService.deployRappInstance(rapp, rappInstance).getStatusCode()); + } + + @Test + void testDeployRappInstanceDmeFailure() { + Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile) + .packageLocation(validCsarFileLocation).state(RappState.PRIMED).build(); + RappInstance rappInstance = new RappInstance(); + rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId()); + when(acmDeployer.deployRappInstance(any(), any())).thenReturn(true); + when(smeDeployer.deployRappInstance(any(), any())).thenReturn(true); + when(dmeDeployer.deployRappInstance(any(), any())).thenReturn(false); assertEquals(HttpStatus.BAD_GATEWAY, rappService.deployRappInstance(rapp, rappInstance).getStatusCode()); } @@ -132,6 +178,7 @@ class RappServiceTest { rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId()); when(acmDeployer.undeployRappInstance(any(), any())).thenReturn(true); when(smeDeployer.undeployRappInstance(any(), any())).thenReturn(true); + when(dmeDeployer.undeployRappInstance(any(), any())).thenReturn(true); assertEquals(HttpStatus.ACCEPTED, rappService.undeployRappInstance(rapp, rappInstance).getStatusCode()); } @@ -144,6 +191,20 @@ class RappServiceTest { rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId()); when(acmDeployer.undeployRappInstance(any(), any())).thenReturn(true); when(smeDeployer.undeployRappInstance(any(), any())).thenReturn(false); + when(dmeDeployer.undeployRappInstance(any(), any())).thenReturn(true); + assertEquals(HttpStatus.BAD_GATEWAY, rappService.undeployRappInstance(rapp, rappInstance).getStatusCode()); + } + + @Test + void testUndeployRappInstanceDmeFailure() { + Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile) + .packageLocation(validCsarFileLocation).state(RappState.PRIMED).build(); + RappInstance rappInstance = new RappInstance(); + rappInstance.setState(RappInstanceState.DEPLOYED); + rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId()); + when(acmDeployer.undeployRappInstance(any(), any())).thenReturn(true); + when(smeDeployer.undeployRappInstance(any(), any())).thenReturn(true); + when(dmeDeployer.undeployRappInstance(any(), any())).thenReturn(false); assertEquals(HttpStatus.BAD_GATEWAY, rappService.undeployRappInstance(rapp, rappInstance).getStatusCode()); } @@ -156,6 +217,7 @@ class RappServiceTest { rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId()); when(acmDeployer.undeployRappInstance(any(), any())).thenReturn(true); when(smeDeployer.undeployRappInstance(any(), any())).thenReturn(false); + when(dmeDeployer.undeployRappInstance(any(), any())).thenReturn(true); assertEquals(HttpStatus.BAD_REQUEST, rappService.undeployRappInstance(rapp, rappInstance).getStatusCode()); } } diff --git a/rapp-manager-dme/pom.xml b/rapp-manager-dme/pom.xml new file mode 100755 index 0000000..6b8e83f --- /dev/null +++ b/rapp-manager-dme/pom.xml @@ -0,0 +1,106 @@ + + + + 4.0.0 + + org.o-ran-sc.nonrtric.plt + rappmanager + 0.0.1-SNAPSHOT + + + org.o-ran-sc.nonrtric.plt.rappmanager + rapp-manager-dme + + + 17 + 17 + UTF-8 + + + + + org.o-ran-sc.nonrtric.plt.rappmanager + rapp-manager-models + ${project.version} + + + org.springframework.boot + spring-boot-starter-web + + + org.openapitools + jackson-databind-nullable + ${openapi.jackson.databind.nullable.version} + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.openapitools + openapi-generator-maven-plugin + ${openapi.maven.version} + + + dme-spec-generator + + generate + + + ${project.basedir}/src/main/resources/openapi/ics-api.yaml + java + resttemplate + false + false + false + false + true + + apiNameSuffix=ApiClient + + + src/main/java + true + com.oransc.rappmanager.dme + com.oransc.rappmanager.dme.rest + com.oransc.rappmanager.dme.data + false + + + + + + + + + \ No newline at end of file diff --git a/rapp-manager-dme/src/main/java/com/oransc/rappmanager/dme/configuration/DmeConfiguration.java b/rapp-manager-dme/src/main/java/com/oransc/rappmanager/dme/configuration/DmeConfiguration.java new file mode 100755 index 0000000..e7cc151 --- /dev/null +++ b/rapp-manager-dme/src/main/java/com/oransc/rappmanager/dme/configuration/DmeConfiguration.java @@ -0,0 +1,30 @@ +/*- + * ============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.dme.configuration; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ConfigurationProperties(prefix = "rappmanager.dme") +@Data +public class DmeConfiguration { + String baseUrl; +} diff --git a/rapp-manager-dme/src/main/java/com/oransc/rappmanager/dme/service/DmeDeployer.java b/rapp-manager-dme/src/main/java/com/oransc/rappmanager/dme/service/DmeDeployer.java new file mode 100755 index 0000000..388a565 --- /dev/null +++ b/rapp-manager-dme/src/main/java/com/oransc/rappmanager/dme/service/DmeDeployer.java @@ -0,0 +1,230 @@ +/*- + * ============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.dme.service; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.oransc.rappmanager.dme.data.ConsumerJob; +import com.oransc.rappmanager.dme.data.ProducerInfoTypeInfo; +import com.oransc.rappmanager.dme.data.ProducerRegistrationInfo; +import com.oransc.rappmanager.dme.rest.DataConsumerApiClient; +import com.oransc.rappmanager.dme.rest.DataProducerRegistrationApiClient; +import com.oransc.rappmanager.models.RappDeployer; +import com.oransc.rappmanager.models.csar.RappCsarConfigurationHandler; +import com.oransc.rappmanager.models.rapp.Rapp; +import com.oransc.rappmanager.models.rapp.RappEvent; +import com.oransc.rappmanager.models.rappinstance.RappInstance; +import com.oransc.rappmanager.models.statemachine.RappInstanceStateMachine; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import lombok.RequiredArgsConstructor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class DmeDeployer implements RappDeployer { + + Logger logger = LoggerFactory.getLogger(DmeDeployer.class); + + private final DataProducerRegistrationApiClient dataProducerRegistrationApiClient; + + private final DataConsumerApiClient dataConsumerApiClient; + + private final RappCsarConfigurationHandler rappCsarConfigurationHandler; + + private final ObjectMapper objectMapper; + + private final RappInstanceStateMachine rappInstanceStateMachine; + + @Override + public boolean deployRappInstance(Rapp rapp, RappInstance rappInstance) { + logger.debug("Deploying DME functions for RappInstance {}", rappInstance.getRappInstanceId()); + boolean deployState = true; + if (rappInstance.getDme().getInfoTypesProducer() != null + || rappInstance.getDme().getInfoTypeConsumer() != null) { + Set infoTypes = new HashSet<>(); + if (rappInstance.getDme().getInfoTypesProducer() != null) { + infoTypes.addAll(rappInstance.getDme().getInfoTypesProducer()); + } + if (rappInstance.getDme().getInfoTypeConsumer() != null) { + infoTypes.add(rappInstance.getDme().getInfoTypeConsumer()); + } + deployState = createInfoTypes(rapp, infoTypes); + } + if (rappInstance.getDme().getInfoProducer() != null) { + deployState = deployState && createInfoProducer(rapp, rappInstance.getDme().getInfoProducer()); + } + if (rappInstance.getDme().getInfoConsumer() != null) { + deployState = deployState && createInfoConsumer(rapp, rappInstance.getDme().getInfoConsumer()); + } + if (deployState) { + rappInstanceStateMachine.sendRappInstanceEvent(rappInstance, RappEvent.DMEDEPLOYED); + } else { + rappInstanceStateMachine.sendRappInstanceEvent(rappInstance, RappEvent.DMEDEPLOYFAILED); + } + return deployState; + } + + @Override + public boolean undeployRappInstance(Rapp rapp, RappInstance rappInstance) { + logger.debug("Undeploying DME functions for RappInstance {}", rappInstance.getRappInstanceId()); + boolean undeployState = true; + if (rappInstance.getDme().getInfoConsumer() != null) { + undeployState = deleteInfoConsumer(rapp, rappInstance.getDme().getInfoConsumer()); + } + if (rappInstance.getDme().getInfoProducer() != null) { + undeployState = undeployState && deleteInfoProducer(rapp, rappInstance.getDme().getInfoProducer()); + } + if (undeployState) { + rappInstanceStateMachine.sendRappInstanceEvent(rappInstance, RappEvent.DMEUNDEPLOYED); + } else { + rappInstanceStateMachine.sendRappInstanceEvent(rappInstance, RappEvent.DMEUNDEPLOYFAILED); + } + return undeployState; + } + + @Override + public boolean primeRapp(Rapp rapp) { + logger.debug("Priming DME functions for rApp {}", rapp.getRappId()); + try { + Set requiredInfoTypes = new HashSet<>(); + for (String producerResourceName : rapp.getRappResources().getDme().getInfoProducers()) { + String producerPayload = + rappCsarConfigurationHandler.getDmeInfoProducerPayload(rapp, producerResourceName); + ProducerRegistrationInfo producerRegistrationInfo = + objectMapper.readValue(producerPayload, ProducerRegistrationInfo.class); + requiredInfoTypes.addAll(producerRegistrationInfo.getSupportedInfoTypes()); + } + for (String consumerResourceName : rapp.getRappResources().getDme().getInfoConsumers()) { + String consumerPayload = + rappCsarConfigurationHandler.getDmeInfoConsumerPayload(rapp, consumerResourceName); + ConsumerJob consumerJob = objectMapper.readValue(consumerPayload, ConsumerJob.class); + requiredInfoTypes.add(consumerJob.getInfoTypeId()); + } + Set allInfoTypes = new HashSet<>(rapp.getRappResources().getDme().getInfoTypes()); + requiredInfoTypes.removeAll(allInfoTypes); + if (!requiredInfoTypes.isEmpty()) { + allInfoTypes.addAll(dataProducerRegistrationApiClient.getInfoTypdentifiers()); + requiredInfoTypes.removeAll(allInfoTypes); + if (!requiredInfoTypes.isEmpty()) { + logger.info("Invalid rapp package as the following info types cannot be found {}", + requiredInfoTypes); + rapp.setIsDmeValid(false); + } else { + rapp.setIsDmeValid(true); + } + } else { + rapp.setIsDmeValid(true); + } + return true; + } catch (Exception e) { + logger.warn("Failed to prime DME", e); + rapp.setIsDmeValid(false); + return false; + } + } + + @Override + public boolean deprimeRapp(Rapp rapp) { + logger.debug("Depriming DME functions for rApp {}", rapp.getRappId()); + rapp.setIsDmeValid(null); + return true; + } + + boolean createInfoTypes(Rapp rApp, Set infoTypes) { + logger.debug("Creating DME info types {} for rApp {}", infoTypes, rApp.getRappId()); + try { + Map producerInfoTypeInfoMap = new HashMap<>(); + for (String infoType : infoTypes) { + String infoTypePayload = rappCsarConfigurationHandler.getDmeInfoTypePayload(rApp, infoType); + if (infoTypePayload != null && !infoTypePayload.isEmpty()) { + producerInfoTypeInfoMap.put(infoType, + objectMapper.readValue(infoTypePayload, ProducerInfoTypeInfo.class)); + } + } + return producerInfoTypeInfoMap.entrySet().stream().map(stringProducerInfoTypeInfoEntry -> { + ResponseEntity objectResponseEntity = dataProducerRegistrationApiClient.putInfoTypeWithHttpInfo( + stringProducerInfoTypeInfoEntry.getKey(), stringProducerInfoTypeInfoEntry.getValue()); + return objectResponseEntity.getStatusCode().is2xxSuccessful(); + }).reduce(true, (a, b) -> a && b); + } catch (Exception e) { + logger.warn("Error in creating info types {} for rApp {}", infoTypes, rApp.getRappId(), e); + return false; + } + } + + boolean createInfoProducer(Rapp rApp, String producerResource) { + logger.debug("Creating DME info producer {} for rApp {}", producerResource, rApp.getRappId()); + try { + String infoProducerPayload = rappCsarConfigurationHandler.getDmeInfoProducerPayload(rApp, producerResource); + ProducerRegistrationInfo producerRegistrationInfo = + objectMapper.readValue(infoProducerPayload, ProducerRegistrationInfo.class); + + ResponseEntity objectResponseEntity = + dataProducerRegistrationApiClient.putInfoProducerWithHttpInfo(producerResource, + producerRegistrationInfo); + return objectResponseEntity.getStatusCode().is2xxSuccessful(); + } catch (Exception e) { + logger.warn("Error in creating info producer {} for rApp {}", producerResource, rApp.getRappId(), e); + return false; + } + } + + boolean createInfoConsumer(Rapp rApp, String consumerResource) { + logger.debug("Creating DME info consumer {} for rApp {}", consumerResource, rApp.getRappId()); + try { + String infoJobPayload = rappCsarConfigurationHandler.getDmeInfoConsumerPayload(rApp, consumerResource); + ConsumerJob consumerJob = objectMapper.readValue(infoJobPayload, ConsumerJob.class); + ResponseEntity objectResponseEntity = + dataConsumerApiClient.putIndividualInfoJobWithHttpInfo(consumerResource, consumerJob); + return objectResponseEntity.getStatusCode().is2xxSuccessful(); + } catch (Exception e) { + logger.warn("Error in creating info consumer {} for rApp {}", consumerResource, rApp.getRappId(), e); + return false; + } + } + + boolean deleteInfoProducer(Rapp rApp, String producerResource) { + logger.debug("Deleting DME info producer {} for rApp {}", producerResource, rApp.getRappId()); + try { + ResponseEntity objectResponseEntity = + dataProducerRegistrationApiClient.deleteInfoProducerWithHttpInfo(producerResource); + return objectResponseEntity.getStatusCode().is2xxSuccessful(); + } catch (Exception e) { + logger.warn("Error in deleting info producer {} for rApp {}", producerResource, rApp.getRappId(), e); + return false; + } + } + + boolean deleteInfoConsumer(Rapp rApp, String consumerResource) { + logger.debug("Deleting DME info consumer {} for rApp {}", consumerResource, rApp.getRappId()); + try { + ResponseEntity objectResponseEntity = + dataConsumerApiClient.deleteIndividualInfoJobWithHttpInfo(consumerResource); + return objectResponseEntity.getStatusCode().is2xxSuccessful(); + } catch (Exception e) { + logger.warn("Error in deleting info consumer {} for rApp {}", consumerResource, rApp.getRappId(), e); + return false; + } + } +} diff --git a/rapp-manager-dme/src/main/resources/openapi/ics-api.yaml b/rapp-manager-dme/src/main/resources/openapi/ics-api.yaml new file mode 100755 index 0000000..6762bc5 --- /dev/null +++ b/rapp-manager-dme/src/main/resources/openapi/ics-api.yaml @@ -0,0 +1,1669 @@ +openapi: 3.0.1 +info: + title: Data management and exposure + description:

API documentation

General

The service is mainly + a broker between data producers and data consumers. A data producer has the ability + to produce one or several types of data (Information Type). One type of data can + be produced by zero to many producers.

A data consumer can have several + active data subscriptions (Information Job). One Information Job consists of the + type of data to produce and additional parameters for filtering of the data. These + parameters are different for different data types.

APIs provided by the + service

A1-EI

This API is between Near-RT RIC and the Non-RT + RIC. The Near-RT RIC is a data consumer, which creates Information Jobs to subscribe + for data. In this context, the information is referred to as 'Enrichment Information', + EI.

Data producer API

This API is provided by the Non-RT RIC platform + and is intended to be part of the O-RAN R1 interface. The API is for use by different + kinds of data producers and provides support for:

  • Registry of supported + information types and which parameters needed to setup a subscription.
  • Registry + of existing data producers.
  • Callback API provided by producers to setup + subscriptions.

Data consumer API

This API is provided + by the Non-RT RIC platform and is intended to be part of the O-RAN R1 interface. The + API is for use by different kinds of data consumers and provides support for:

  • Querying + of available types of data to consume.
  • Management of data subscription + jobs
  • Optional callback API provided by consumers to get notification on + added and removed information types.

Service status

This + API provides a means to monitor the health of this service.

+ license: + name: Copyright (C) 2020-2023 Nordix Foundation. Licensed under the Apache License. + url: http://www.apache.org/licenses/LICENSE-2.0 + version: "1.0" +servers: +- url: / +tags: +- name: A1-EI (registration) + description: Data consumer EI job registration +- name: A1-EI (callbacks) + description: Data consumer EI job status callbacks +- name: Data consumer (callbacks) + description: API for data consumers +- name: Data producer (registration) + description: API for data producers +- name: Data producer (callbacks) + description: API implemented by data producers +- name: Data consumer + description: API for data consumers +- name: Service status + description: API for monitoring of the service +- name: Actuator + description: Monitor and interact + externalDocs: + description: Spring Boot Actuator Web API Documentation + url: https://docs.spring.io/spring-boot/docs/current/actuator-api/html/ +- name: Authorization API + description: API used for authorization of information job access (this is provided + by an authorization producer such as OPA) +paths: + /example-authz-check: + post: + tags: + - Authorization API + summary: Request for access authorization. + description: The authorization function decides if access is granted. + operationId: subscriptionAuth + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/subscription_authorization' + required: true + responses: + 200: + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/authorization_result' + /data-producer/v1/info-types: + get: + tags: + - Data producer (registration) + summary: Info Type identifiers + operationId: getInfoTypdentifiers + responses: + 200: + description: Info Type identifiers + content: + application/json: + schema: + type: array + items: + type: string + /actuator/threaddump: + get: + tags: + - Actuator + summary: Actuator web endpoint 'threaddump' + operationId: threaddump + responses: + 200: + description: OK + content: + text/plain;charset=UTF-8: + schema: + type: object + application/vnd.spring-boot.actuator.v3+json: + schema: + type: object + application/json: + schema: + type: object + application/vnd.spring-boot.actuator.v2+json: + schema: + type: object + /A1-EI/v1/eitypes/{eiTypeId}: + get: + tags: + - A1-EI (registration) + summary: Individual EI type + operationId: getEiType + parameters: + - name: eiTypeId + in: path + required: true + style: simple + explode: false + schema: + type: string + responses: + 200: + description: EI type + content: + application/json: + schema: + $ref: '#/components/schemas/EiTypeObject' + 404: + description: Enrichment Information type is not found + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetails' + /data-producer/v1/info-types/{infoTypeId}: + get: + tags: + - Data producer (registration) + summary: Individual Information Type + operationId: getInfoType + parameters: + - name: infoTypeId + in: path + required: true + style: simple + explode: false + schema: + type: string + responses: + 200: + description: Info Type + content: + application/json: + schema: + $ref: '#/components/schemas/producer_info_type_info' + 404: + description: Information type is not found + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetails' + put: + tags: + - Data producer (registration) + summary: Individual Information Type + operationId: putInfoType + parameters: + - name: infoTypeId + in: path + required: true + style: simple + explode: false + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/producer_info_type_info' + required: true + responses: + 200: + description: Type updated + content: + application/json: + schema: + $ref: '#/components/schemas/Void' + 201: + description: Type created + content: + application/json: + schema: + $ref: '#/components/schemas/Void' + 400: + description: Input validation failed + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetails' + delete: + tags: + - Data producer (registration) + summary: Individual Information Type + description: Existing jobs of the type will be automatically deleted. + operationId: deleteInfoType + parameters: + - name: infoTypeId + in: path + required: true + style: simple + explode: false + schema: + type: string + responses: + 200: + description: Not used + content: + application/json: + schema: + $ref: '#/components/schemas/Void' + 204: + description: Producer deleted + content: + application/json: + schema: + $ref: '#/components/schemas/Void' + 404: + description: Information type is not found + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetails' + 409: + description: The Information type has one or several active producers + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetails' + /data-consumer/v1/info-type-subscription/{subscriptionId}: + get: + tags: + - Data consumer + summary: Individual subscription for information types (registration/deregistration) + operationId: getIndividualTypeSubscription + parameters: + - name: subscriptionId + in: path + required: true + style: simple + explode: false + schema: + type: string + responses: + 200: + description: Type subscription + content: + application/json: + schema: + $ref: '#/components/schemas/consumer_type_subscription_info' + 404: + description: Subscription is not found + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetails' + put: + tags: + - Data consumer + summary: Individual subscription for information types (registration/deregistration) + description: This service operation is used to subscribe to notifications for + changes in the availability of data types. + operationId: putIndividualTypeSubscription + parameters: + - name: subscriptionId + in: path + required: true + style: simple + explode: false + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/consumer_type_subscription_info' + required: true + responses: + 200: + description: Subscription updated + content: + application/json: + schema: + $ref: '#/components/schemas/Void' + 201: + description: Subscription created + content: + application/json: + schema: + $ref: '#/components/schemas/Void' + delete: + tags: + - Data consumer + summary: Individual subscription for information types (registration/deregistration) + operationId: deleteIndividualTypeSubscription + parameters: + - name: subscriptionId + in: path + required: true + style: simple + explode: false + schema: + type: string + responses: + 200: + description: Not used + content: + application/json: + schema: + $ref: '#/components/schemas/Void' + 204: + description: Subscription deleted + content: + application/json: + schema: + $ref: '#/components/schemas/Void' + 404: + description: Subscription is not found + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetails' + /example-dataproducer/health-check: + get: + tags: + - Data producer (callbacks) + summary: Producer supervision + description: The endpoint is provided by the Information Producer and is used + for supervision of the producer. + operationId: producerSupervision + responses: + 200: + description: The producer is OK + content: + application/json: + schema: + type: string + /actuator/loggers: + get: + tags: + - Actuator + summary: Actuator web endpoint 'loggers' + operationId: loggers + responses: + 200: + description: OK + content: + application/vnd.spring-boot.actuator.v3+json: + schema: + type: object + application/json: + schema: + type: object + application/vnd.spring-boot.actuator.v2+json: + schema: + type: object + /actuator/health/**: + get: + tags: + - Actuator + summary: Actuator web endpoint 'health-path' + operationId: health-path + responses: + 200: + description: OK + content: + application/vnd.spring-boot.actuator.v3+json: + schema: + type: object + application/json: + schema: + type: object + application/vnd.spring-boot.actuator.v2+json: + schema: + type: object + /data-consumer/v1/info-types: + get: + tags: + - Data consumer + summary: Information type identifiers + operationId: getinfoTypeIdentifiers + responses: + 200: + description: Information type identifiers + content: + application/json: + schema: + type: array + items: + type: string + /example-dataconsumer/info-type-status: + post: + tags: + - Data consumer (callbacks) + summary: Callback for changed Information type registration status + description: The primitive is implemented by the data consumer and is invoked + when a Information type status has been changed.
Subscription are managed + by primitives in 'Data consumer' + operationId: typeStatusCallback + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/consumer_type_registration_info' + required: true + responses: + 200: + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Void' + /actuator/shutdown: + post: + tags: + - Actuator + summary: Actuator web endpoint 'shutdown' + operationId: shutdown + responses: + 200: + description: OK + content: + application/vnd.spring-boot.actuator.v3+json: + schema: + type: object + application/json: + schema: + type: object + application/vnd.spring-boot.actuator.v2+json: + schema: + type: object + /actuator/metrics/{requiredMetricName}: + get: + tags: + - Actuator + summary: Actuator web endpoint 'metrics-requiredMetricName' + operationId: metrics-requiredMetricName + parameters: + - name: requiredMetricName + in: path + required: true + style: simple + explode: false + schema: + type: string + responses: + 200: + description: OK + content: + application/vnd.spring-boot.actuator.v3+json: + schema: + type: object + application/json: + schema: + type: object + application/vnd.spring-boot.actuator.v2+json: + schema: + type: object + /actuator: + get: + tags: + - Actuator + summary: Actuator root web endpoint + operationId: links + responses: + 200: + description: OK + content: + application/vnd.spring-boot.actuator.v3+json: + schema: + type: object + additionalProperties: + type: object + additionalProperties: + $ref: '#/components/schemas/Link' + application/json: + schema: + type: object + additionalProperties: + type: object + additionalProperties: + $ref: '#/components/schemas/Link' + application/vnd.spring-boot.actuator.v2+json: + schema: + type: object + additionalProperties: + type: object + additionalProperties: + $ref: '#/components/schemas/Link' + /data-consumer/v1/info-jobs: + get: + tags: + - Data consumer + summary: Information Job identifiers + description: query for information job identifiers + operationId: getJobIds + parameters: + - name: infoTypeId + in: query + description: selects subscription jobs of matching information type + required: false + style: form + explode: true + schema: + type: string + - name: owner + in: query + description: selects result for one owner + required: false + style: form + explode: true + schema: + type: string + responses: + 200: + description: Information information job identifiers + content: + application/json: + schema: + type: array + items: + type: string + 404: + description: Information type is not found + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetails' + delete: + tags: + - Data consumer + summary: Information Jobs + description: delete all jobs for one owner + operationId: deleteJobsForOwner + parameters: + - name: owner + in: query + description: selects result for one owner + required: true + style: form + explode: true + schema: + type: string + responses: + 204: + description: No Content + content: + application/json: + schema: + type: object + /actuator/loggers/{name}: + get: + tags: + - Actuator + summary: Actuator web endpoint 'loggers-name' + operationId: loggers-name + parameters: + - name: name + in: path + required: true + style: simple + explode: false + schema: + type: string + responses: + 200: + description: OK + content: + application/vnd.spring-boot.actuator.v3+json: + schema: + type: object + application/json: + schema: + type: object + application/vnd.spring-boot.actuator.v2+json: + schema: + type: object + post: + tags: + - Actuator + summary: Actuator web endpoint 'loggers-name' + operationId: loggers-name_2 + parameters: + - name: name + in: path + required: true + style: simple + explode: false + schema: + type: string + requestBody: + content: + application/json: + schema: + type: string + enum: + - TRACE + - DEBUG + - INFO + - WARN + - ERROR + - FATAL + - OFF + responses: + 200: + description: OK + content: + '*/*': + schema: + type: object + /example-dataproducer/info-job: + post: + tags: + - Data producer (callbacks) + summary: Callback for Information Job creation/modification + description: The call is invoked to activate or to modify a data subscription. + The endpoint is provided by the Information Producer. + operationId: jobCreatedCallback + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/producer_info_job_request' + required: true + responses: + 200: + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Void' + /example-dataproducer/info-job/{infoJobId}: + delete: + tags: + - Data producer (callbacks) + summary: Callback for Information Job deletion + description: The call is invoked to terminate a data subscription. The endpoint + is provided by the Information Producer. + operationId: jobDeletedCallback + parameters: + - name: infoJobId + in: path + required: true + style: simple + explode: false + schema: + type: string + responses: + 200: + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Void' + /A1-EI/v1/eijobs/{eiJobId}/status: + get: + tags: + - A1-EI (registration) + summary: EI job status + operationId: getEiJobStatus + parameters: + - name: eiJobId + in: path + required: true + style: simple + explode: false + schema: + type: string + responses: + 200: + description: EI job status + content: + application/json: + schema: + $ref: '#/components/schemas/EiJobStatusObject' + 404: + description: Enrichment Information job is not found + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetails' + /data-producer/v1/info-producers/{infoProducerId}/status: + get: + tags: + - Data producer (registration) + summary: Information producer status + operationId: getInfoProducerStatus + parameters: + - name: infoProducerId + in: path + required: true + style: simple + explode: false + schema: + type: string + responses: + 200: + description: Information producer status + content: + application/json: + schema: + $ref: '#/components/schemas/producer_status' + 404: + description: Information producer is not found + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetails' + /data-consumer/v1/info-jobs/{infoJobId}/status: + get: + tags: + - Data consumer + summary: Job status + operationId: getInfoJobStatus + parameters: + - name: infoJobId + in: path + required: true + style: simple + explode: false + schema: + type: string + responses: + 200: + description: Information subscription job status + content: + application/json: + schema: + $ref: '#/components/schemas/consumer_job_status' + 404: + description: Information subscription job is not found + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetails' + /actuator/metrics: + get: + tags: + - Actuator + summary: Actuator web endpoint 'metrics' + operationId: metrics + responses: + 200: + description: OK + content: + application/vnd.spring-boot.actuator.v3+json: + schema: + type: object + application/json: + schema: + type: object + application/vnd.spring-boot.actuator.v2+json: + schema: + type: object + /example-dataconsumer/info-jobs/{infoJobId}/status: + post: + tags: + - A1-EI (callbacks) + summary: Callback for changed Information Job status + description: The primitive is implemented by the data consumer and is invoked + when a Information Job status has been changed. + operationId: jobStatusCallback + parameters: + - name: infoJobId + in: path + required: true + style: simple + explode: false + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/EiJobStatusObject' + required: true + responses: + 200: + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Void' + /actuator/info: + get: + tags: + - Actuator + summary: Actuator web endpoint 'info' + operationId: info + responses: + 200: + description: OK + content: + application/vnd.spring-boot.actuator.v3+json: + schema: + type: object + application/json: + schema: + type: object + application/vnd.spring-boot.actuator.v2+json: + schema: + type: object + /A1-EI/v1/eitypes: + get: + tags: + - A1-EI (registration) + summary: EI type identifiers + operationId: getEiTypeIdentifiers + responses: + 200: + description: EI type identifiers + content: + application/json: + schema: + type: array + items: + type: string + /data-producer/v1/info-producers/{infoProducerId}: + get: + tags: + - Data producer (registration) + summary: Individual Information Producer + operationId: getInfoProducer + parameters: + - name: infoProducerId + in: path + required: true + style: simple + explode: false + schema: + type: string + responses: + 200: + description: Information producer + content: + application/json: + schema: + $ref: '#/components/schemas/producer_registration_info' + 404: + description: Information producer is not found + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetails' + put: + tags: + - Data producer (registration) + summary: Individual Information Producer + operationId: putInfoProducer + parameters: + - name: infoProducerId + in: path + required: true + style: simple + explode: false + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/producer_registration_info' + required: true + responses: + 200: + description: Producer updated + content: + application/json: + schema: + $ref: '#/components/schemas/Void' + 201: + description: Producer created + content: + application/json: + schema: + $ref: '#/components/schemas/Void' + 400: + description: Input validation failed + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetails' + 404: + description: Producer type not found + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetails' + delete: + tags: + - Data producer (registration) + summary: Individual Information Producer + operationId: deleteInfoProducer + parameters: + - name: infoProducerId + in: path + required: true + style: simple + explode: false + schema: + type: string + responses: + 200: + description: Not used + content: + application/json: + schema: + $ref: '#/components/schemas/Void' + 204: + description: Producer deleted + content: + application/json: + schema: + $ref: '#/components/schemas/Void' + 404: + description: Producer is not found + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetails' + /status: + get: + tags: + - Service status + summary: Returns status and statistics of this service + operationId: getStatus + responses: + 200: + description: Service is living + content: + application/json: + schema: + $ref: '#/components/schemas/service_status_info' + /data-consumer/v1/info-type-subscription: + get: + tags: + - Data consumer + summary: Information type subscription identifiers + description: query for information type subscription identifiers + operationId: getInfoTypeSubscriptions + parameters: + - name: owner + in: query + description: selects result for one owner + required: false + style: form + explode: true + schema: + type: string + responses: + 200: + description: Information type subscription identifiers + content: + application/json: + schema: + type: array + items: + type: string + /A1-EI/v1/eijobs/{eiJobId}: + get: + tags: + - A1-EI (registration) + summary: Individual EI job + operationId: getIndividualEiJob + parameters: + - name: eiJobId + in: path + required: true + style: simple + explode: false + schema: + type: string + responses: + 200: + description: EI job + content: + application/json: + schema: + $ref: '#/components/schemas/EiJobObject' + 404: + description: Enrichment Information job is not found + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetails' + put: + tags: + - A1-EI (registration) + summary: Individual EI job + description: If the requested info_type_id is not found, an attempt to find + a compatible version is made. As an example, 'type_1.9.0' is backwards compatible + with 'type_1.0.0' + operationId: putIndividualEiJob + parameters: + - name: eiJobId + in: path + required: true + style: simple + explode: false + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/EiJobObject' + required: true + responses: + 200: + description: Job updated + content: + application/json: + schema: + $ref: '#/components/schemas/Void' + 201: + description: Job created + content: + application/json: + schema: + $ref: '#/components/schemas/Void' + 400: + description: Input validation failed + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetails' + 404: + description: Enrichment Information type is not found + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetails' + 409: + description: Cannot modify job type + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetails' + delete: + tags: + - A1-EI (registration) + summary: Individual EI job + operationId: deleteIndividualEiJob + parameters: + - name: eiJobId + in: path + required: true + style: simple + explode: false + schema: + type: string + responses: + 200: + description: Not used + content: + application/json: + schema: + $ref: '#/components/schemas/Void' + 204: + description: Job deleted + content: + application/json: + schema: + $ref: '#/components/schemas/Void' + 404: + description: Enrichment Information job is not found + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetails' + /actuator/logfile: + get: + tags: + - Actuator + summary: Actuator web endpoint 'logfile' + operationId: logfile + responses: + 200: + description: OK + content: + text/plain;charset=UTF-8: + schema: + type: object + /data-consumer/v1/info-jobs/{infoJobId}: + get: + tags: + - Data consumer + summary: Individual data subscription job + operationId: getIndividualInfoJob + parameters: + - name: infoJobId + in: path + required: true + style: simple + explode: false + schema: + type: string + responses: + 200: + description: Information subscription job + content: + application/json: + schema: + $ref: '#/components/schemas/consumer_job' + 404: + description: Information subscription job is not found + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetails' + put: + tags: + - Data consumer + summary: Individual data subscription job + description: The job will be enabled when a producer is available. If the requested + info_type_id is not found, an attempt to find a compatible version is made. + As an example, 'type_1.9.0' is backwards compatible with 'type_1.0.0' + operationId: putIndividualInfoJob + parameters: + - name: infoJobId + in: path + required: true + style: simple + explode: false + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/consumer_job' + required: true + responses: + 200: + description: Job updated + content: + application/json: + schema: + $ref: '#/components/schemas/Void' + 201: + description: Job created + content: + application/json: + schema: + $ref: '#/components/schemas/Void' + 400: + description: Input validation failed + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetails' + 404: + description: Information type is not found + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetails' + 409: + description: Cannot modify job type + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetails' + delete: + tags: + - Data consumer + summary: Individual data subscription job + operationId: deleteIndividualInfoJob + parameters: + - name: infoJobId + in: path + required: true + style: simple + explode: false + schema: + type: string + responses: + 200: + description: Not used + content: + application/json: + schema: + $ref: '#/components/schemas/Void' + 204: + description: Job deleted + content: + application/json: + schema: + $ref: '#/components/schemas/Void' + 404: + description: Information subscription job is not found + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetails' + /data-producer/v1/info-producers: + get: + tags: + - Data producer (registration) + summary: Information producer identifiers + operationId: getInfoProducerIdentifiers + parameters: + - name: infoTypeId + in: query + description: If given, only the producers for the Info Type is returned. + required: false + style: form + explode: true + schema: + type: string + responses: + 200: + description: Information producer identifiers + content: + application/json: + schema: + type: array + items: + type: string + /data-consumer/v1/info-types/{infoTypeId}: + get: + tags: + - Data consumer + summary: Individual information type + operationId: getInfoType_1 + parameters: + - name: infoTypeId + in: path + required: true + style: simple + explode: false + schema: + type: string + responses: + 200: + description: Information type + content: + application/json: + schema: + $ref: '#/components/schemas/consumer_information_type' + 404: + description: Information type is not found + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetails' + /actuator/health: + get: + tags: + - Actuator + summary: Actuator web endpoint 'health' + operationId: health + responses: + 200: + description: OK + content: + application/vnd.spring-boot.actuator.v3+json: + schema: + type: object + application/json: + schema: + type: object + application/vnd.spring-boot.actuator.v2+json: + schema: + type: object + /A1-EI/v1/eijobs: + get: + tags: + - A1-EI (registration) + summary: EI job identifiers + description: query for EI job identifiers + operationId: getEiJobIds + parameters: + - name: eiTypeId + in: query + description: selects EI jobs of matching EI type + required: false + style: form + explode: true + schema: + type: string + - name: owner + in: query + description: selects EI jobs for one EI job owner + required: false + style: form + explode: true + schema: + type: string + responses: + 200: + description: EI job identifiers + content: + application/json: + schema: + type: array + items: + type: string + 404: + description: Enrichment Information type is not found + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetails' + /data-producer/v1/info-producers/{infoProducerId}/info-jobs: + get: + tags: + - Data producer (registration) + summary: Information Job definitions + description: Information Job definitions for one Information Producer + operationId: getInfoProducerJobs + parameters: + - name: infoProducerId + in: path + required: true + style: simple + explode: false + schema: + type: string + responses: + 200: + description: Information producer + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/producer_info_job_request' + 404: + description: Information producer is not found + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetails' + /actuator/heapdump: + get: + tags: + - Actuator + summary: Actuator web endpoint 'heapdump' + operationId: heapdump + responses: + 200: + description: OK + content: + application/octet-stream: + schema: + type: object +components: + schemas: + consumer_information_type: + required: + - job_data_schema + - no_of_producers + - type_status + type: object + properties: + no_of_producers: + type: integer + description: The number of registered producers for the type + format: int32 + type_status: + type: string + description: 'Allowed values:
ENABLED: one or several producers for + the information type are available
DISABLED: no producers for the + information type are available' + enum: + - ENABLED + - DISABLED + job_data_schema: + type: object + description: Json schema for the job data + description: Information for an Information type + EiTypeObject: + type: object + description: Information for an EI type + authorization_result: + required: + - result + type: object + properties: + result: + type: boolean + description: If true, the access is granted + description: Result of authorization + service_status_info: + required: + - no_of_jobs + - no_of_producers + - no_of_types + - status + type: object + properties: + no_of_producers: + type: integer + description: Number of Information Producers + format: int32 + no_of_types: + type: integer + description: Number of Information Types + format: int32 + no_of_jobs: + type: integer + description: Number of Information Jobs + format: int32 + status: + type: string + description: status text + producer_registration_info: + required: + - info_job_callback_url + - info_producer_supervision_callback_url + - supported_info_types + type: object + properties: + info_producer_supervision_callback_url: + type: string + description: callback for producer supervision + supported_info_types: + type: array + description: Supported Information Type IDs + items: + type: string + description: Supported Information Type IDs + info_job_callback_url: + type: string + description: callback for Information Job + description: Information for an Information Producer + consumer_type_registration_info: + required: + - info_type_id + - job_data_schema + - status + type: object + properties: + info_type_id: + type: string + description: Information type identifier + job_data_schema: + type: object + description: Json schema for the job data + status: + type: string + description: 'Allowed values:
REGISTERED: the information type has + been registered
DEREGISTERED: the information type has been removed' + enum: + - REGISTERED + - DEREGISTERED + description: Information for an Information type + ProblemDetails: + type: object + properties: + detail: + type: string + description: A human-readable explanation specific to this occurrence of + the problem. + example: Information Job type not found + status: + type: integer + description: The HTTP status code generated by the origin server for this + occurrence of the problem. + format: int32 + example: 404 + description: A problem detail to carry details in a HTTP response according + to RFC 7807 + EiJobStatusObject: + required: + - eiJobStatus + type: object + properties: + eiJobStatus: + type: string + description: 'Allowed values for EI job status:
ENABLED: the A1-EI + producer is able to deliver EI result for the EI job
DISABLED: the + A1-EI producer is unable to deliver EI result for the EI job' + enum: + - ENABLED + - DISABLED + description: Status for an EI job + consumer_job_status: + required: + - info_job_status + - producers + type: object + properties: + info_job_status: + type: string + description: 'Allowed values:
ENABLED: the A1-Information producer + is able to deliver result for the Information Job
DISABLED: the A1-Information + producer is unable to deliver result for the Information Job' + enum: + - ENABLED + - DISABLED + producers: + type: array + description: An array of all registered Information Producer Identifiers. + items: + type: string + description: An array of all registered Information Producer Identifiers. + description: Status for an Information Job + EiJobObject: + required: + - eiTypeId + - jobDefinition + - jobOwner + - jobResultUri + type: object + properties: + eiTypeId: + type: string + description: EI type Idenitifier of the EI job + jobResultUri: + type: string + description: The target URI of the EI data + jobOwner: + type: string + description: Identity of the owner of the job + statusNotificationUri: + type: string + description: The target of EI job status notifications + jobDefinition: + type: object + description: EI type specific job data + description: Information for an Enrichment Information Job + subscription_authorization: + required: + - input + type: object + properties: + input: + $ref: '#/components/schemas/input' + description: Authorization request for subscription requests + producer_info_type_info: + required: + - info_job_data_schema + type: object + properties: + info_type_information: + type: object + description: Type specific information for the information type + info_job_data_schema: + type: object + description: Json schema for the job data + description: Information for an Information Type + producer_info_job_request: + required: + - info_job_identity + type: object + properties: + owner: + type: string + description: The owner of the job. This is a string that indentifies the + job owner, which could be an application, a POD or something else. + last_updated: + type: string + description: The time when the job was last updated or created (ISO-8601) + info_job_identity: + type: string + description: Identity of the Information Job + target_uri: + type: string + description: URI for the target of the produced Information. Note, this + is deprecated and will be removed. The information on how the data is + delivered is type specific and should be defined in the type specific + info_job_data. + info_job_data: + type: object + description: Json for the job data + info_type_identity: + type: string + description: Type identity for the job + description: The body of the Information Producer callbacks for Information + Job creation and deletion + input: + required: + - access_type + - auth_token + - info_type_id + - job_definition + type: object + properties: + access_type: + type: string + description: Access type + enum: + - READ + - WRITE + info_type_id: + type: string + description: Information type identifier + job_definition: + type: object + description: Information type specific job data + auth_token: + type: string + description: Authorization token + description: input + consumer_job: + required: + - info_type_id + - job_definition + - job_owner + - job_result_uri + type: object + properties: + info_type_id: + type: string + description: Information type Idenitifier of the subscription job + job_result_uri: + type: string + description: The target URI of the subscribed information + job_owner: + type: string + description: Identity of the owner of the job + job_definition: + type: object + description: Information type specific job data + status_notification_uri: + type: string + description: The target of Information subscription job status notifications + description: Information for an Information Job + producer_status: + required: + - operational_state + type: object + properties: + operational_state: + type: string + description: Represents the operational states + enum: + - ENABLED + - DISABLED + description: Status for an Info Producer + Void: + type: object + description: 'Void/empty ' + Link: + type: object + properties: + templated: + type: boolean + href: + type: string + consumer_type_subscription_info: + required: + - owner + - status_result_uri + type: object + properties: + owner: + type: string + description: Identity of the owner of the subscription + status_result_uri: + type: string + description: The target URI of the subscribed information + description: Information for an information type subscription diff --git a/rapp-manager-dme/src/test/java/com/oransc/rappmanager/dme/service/BeanTestConfiguration.java b/rapp-manager-dme/src/test/java/com/oransc/rappmanager/dme/service/BeanTestConfiguration.java new file mode 100755 index 0000000..d22e939 --- /dev/null +++ b/rapp-manager-dme/src/test/java/com/oransc/rappmanager/dme/service/BeanTestConfiguration.java @@ -0,0 +1,78 @@ +/*- + * ============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.dme.service; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.oransc.rappmanager.dme.configuration.DmeConfiguration; +import com.oransc.rappmanager.dme.rest.DataConsumerApiClient; +import com.oransc.rappmanager.dme.rest.DataProducerRegistrationApiClient; +import lombok.RequiredArgsConstructor; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.cache.CacheManager; +import org.springframework.cache.concurrent.ConcurrentMapCacheManager; +import org.springframework.context.annotation.Bean; +import org.springframework.web.client.RestTemplate; + +@TestConfiguration +@RequiredArgsConstructor +public class BeanTestConfiguration { + + private final DmeConfiguration dmeConfiguration; + + @Bean + public ObjectMapper objectMapper() { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + return objectMapper; + } + + @Bean + public RestTemplateBuilder restTemplateBuilder() { + return new RestTemplateBuilder(); + } + + @Bean + public CacheManager cacheManager() { + return new ConcurrentMapCacheManager(); // or any other CacheManager implementation you want to use in the test + } + + @Bean + public RestTemplate restTemplate(RestTemplateBuilder builder) { + return builder.build(); + } + + @Bean + public com.oransc.rappmanager.dme.ApiClient dmeApiClient(RestTemplate restTemplate) { + com.oransc.rappmanager.dme.ApiClient apiClient = new com.oransc.rappmanager.dme.ApiClient(restTemplate); + return apiClient.setBasePath(dmeConfiguration.getBaseUrl()); + } + + @Bean + public DataProducerRegistrationApiClient dataProducerRegistrationApiClient( + com.oransc.rappmanager.dme.ApiClient apiClient) { + return new DataProducerRegistrationApiClient(apiClient); + } + + @Bean + public DataConsumerApiClient dataConsumerApiClient(com.oransc.rappmanager.dme.ApiClient apiClient) { + return new DataConsumerApiClient(apiClient); + } +} diff --git a/rapp-manager-dme/src/test/java/com/oransc/rappmanager/dme/service/DmeDeployerTest.java b/rapp-manager-dme/src/test/java/com/oransc/rappmanager/dme/service/DmeDeployerTest.java new file mode 100755 index 0000000..5b97b2d --- /dev/null +++ b/rapp-manager-dme/src/test/java/com/oransc/rappmanager/dme/service/DmeDeployerTest.java @@ -0,0 +1,350 @@ +/*- + * ============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.dme.service; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +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.withStatus; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.oransc.rappmanager.dme.configuration.DmeConfiguration; +import com.oransc.rappmanager.models.cache.RappCacheService; +import com.oransc.rappmanager.models.csar.RappCsarConfigurationHandler; +import com.oransc.rappmanager.models.rapp.Rapp; +import com.oransc.rappmanager.models.rapp.RappDmeResourceBuilder; +import com.oransc.rappmanager.models.rapp.RappResources; +import com.oransc.rappmanager.models.rapp.RappState; +import com.oransc.rappmanager.models.rappinstance.RappInstance; +import com.oransc.rappmanager.models.statemachine.RappInstanceStateMachine; +import com.oransc.rappmanager.models.statemachine.RappInstanceStateMachineConfig; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +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.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.test.web.client.ExpectedCount; +import org.springframework.test.web.client.MockRestServiceServer; +import org.springframework.web.client.RestTemplate; + +@SpringBootTest(classes = {DmeConfiguration.class, DmeDeployer.class, BeanTestConfiguration.class, + RappCsarConfigurationHandler.class, RappCacheService.class, RappInstanceStateMachineConfig.class, + RappInstanceStateMachine.class}) +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@AutoConfigureMockMvc +class DmeDeployerTest { + + MockRestServiceServer mockServer; + @SpyBean + DmeDeployer dmeDeployer; + @Autowired + RestTemplate restTemplate; + @Autowired + DmeConfiguration dmeConfiguration; + @SpyBean + RappInstanceStateMachine rappInstanceStateMachine; + + RappDmeResourceBuilder rappDmeResourceBuilder = new RappDmeResourceBuilder(); + + private static final String validRappFile = "valid-rapp-package.csar"; + private static final String validRappFileNewInfoType = "valid-rapp-package-new-info-type.csar"; + String validCsarFileLocation = "src/test/resources/"; + ObjectMapper objectMapper = new ObjectMapper(); + + String URI_INFO_TYPES, URI_INFO_TYPE, URI_INFO_PRODUCER, URI_INFO_CONSUMER; + + @BeforeAll + void initACMURI() { + URI_INFO_TYPES = dmeConfiguration.getBaseUrl() + "/data-producer/v1/info-types"; + URI_INFO_TYPE = dmeConfiguration.getBaseUrl() + "/data-producer/v1/info-types/%s"; + URI_INFO_PRODUCER = dmeConfiguration.getBaseUrl() + "/data-producer/v1/info-producers/%s"; + URI_INFO_CONSUMER = dmeConfiguration.getBaseUrl() + "/data-consumer/v1/info-jobs/%s"; + } + + @BeforeEach + public void init() { + mockServer = MockRestServiceServer.createServer(restTemplate); + } + + @ParameterizedTest + @MethodSource("getSuccessParamsWithUnavailableInfoTypes") + void testPrimeRappSuccessWithUnavailableInfoType(String rappFile, boolean result) throws JsonProcessingException { + RappResources rappResources = rappDmeResourceBuilder.getResources(); + Rapp rapp = getRapp(Optional.empty()); + rapp.setPackageName(rappFile); + rapp.setRappResources(rappResources); + List infoTypes = List.of(); + mockServer.expect(ExpectedCount.once(), requestTo(URI_INFO_TYPES)).andExpect(method(HttpMethod.GET)).andRespond( + withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON) + .body(objectMapper.writeValueAsString(infoTypes))); + assertTrue(dmeDeployer.primeRapp(rapp)); + if (rappFile.equals(validRappFileNewInfoType)) { + mockServer.verify(); + } + assertEquals(rapp.getIsDmeValid(), result); + } + + private static Stream getSuccessParamsWithUnavailableInfoTypes() { + return Stream.of(Arguments.of(validRappFile, true), Arguments.of(validRappFileNewInfoType, false)); + } + + @ParameterizedTest + @MethodSource("getSuccessParamsWithAvailableInfoTypes") + void testPrimeRappSuccessWithValidInfoType(String rappFile, boolean result) throws JsonProcessingException { + RappResources rappResources = rappDmeResourceBuilder.getResources(); + Rapp rapp = getRapp(Optional.empty()); + rapp.setPackageName(rappFile); + rapp.setRappResources(rappResources); + List infoTypes = List.of("new-info-type-not-available"); + mockServer.expect(ExpectedCount.once(), requestTo(URI_INFO_TYPES)).andExpect(method(HttpMethod.GET)).andRespond( + withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON) + .body(objectMapper.writeValueAsString(infoTypes))); + assertTrue(dmeDeployer.primeRapp(rapp)); + if (rappFile.equals(validRappFileNewInfoType)) { + mockServer.verify(); + } + assertEquals(rapp.getIsDmeValid(), result); + } + + private static Stream getSuccessParamsWithAvailableInfoTypes() { + return Stream.of(Arguments.of(validRappFile, true), Arguments.of(validRappFileNewInfoType, true)); + } + + @Test + void testPrimeRappFailure() { + RappResources rappResources = rappDmeResourceBuilder.getResources(); + RappResources.DMEResources dme = rappResources.getDme(); + Set infoProducers = new HashSet<>(rappResources.getDme().getInfoProducers()); + infoProducers.add("invalid-producer-not-available-in-rapp"); + dme.setInfoProducers(infoProducers); + rappResources.setDme(dme); + Rapp rapp = getRapp(Optional.empty()); + rapp.setRappResources(rappResources); + assertFalse(dmeDeployer.primeRapp(rapp)); + assertFalse(rapp.getIsDmeValid()); + } + + @Test + void testDeprimeRapp() { + Rapp rapp = getRapp(Optional.empty()); + rapp.setIsDmeValid(true); + assertTrue(dmeDeployer.deprimeRapp(rapp)); + assertNull(rapp.getIsDmeValid()); + } + + @Test + void testDeployrAppInstanceSuccess() { + Rapp rapp = getRapp(Optional.empty()); + RappInstance rappInstance = rappDmeResourceBuilder.getRappInstance(); + getMockServerClientCreateInfoType(rappInstance.getDme().getInfoTypesProducer().toArray()[0].toString(), true); + getMockServerClientCreateInfoProducer(rappInstance.getDme().getInfoProducer(), true); + getMockServerClientCreateInfoConsumer(rappInstance.getDme().getInfoConsumer(), true); + rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId()); + assertTrue(dmeDeployer.deployRappInstance(rapp, rappInstance)); + mockServer.verify(); + } + + @Test + void testDeployrAppInstanceSuccessWithoutConsumer() { + Rapp rapp = getRapp(Optional.empty()); + RappInstance rappInstance = rappDmeResourceBuilder.getRappInstance(); + rappInstance.getDme().setInfoTypeConsumer(null); + rappInstance.getDme().setInfoConsumer(null); + getMockServerClientCreateInfoType(rappInstance.getDme().getInfoTypesProducer().toArray()[0].toString(), true); + getMockServerClientCreateInfoProducer(rappInstance.getDme().getInfoProducer(), true); + rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId()); + assertTrue(dmeDeployer.deployRappInstance(rapp, rappInstance)); + mockServer.verify(); + } + + @Test + void testDeployrAppInstanceWithoutProducer() { + Rapp rapp = getRapp(Optional.empty()); + RappInstance rappInstance = rappDmeResourceBuilder.getRappInstance(); + rappInstance.getDme().setInfoTypesProducer(null); + rappInstance.getDme().setInfoProducer(null); + getMockServerClientCreateInfoType(rappInstance.getDme().getInfoTypeConsumer(), true); + getMockServerClientCreateInfoConsumer(rappInstance.getDme().getInfoConsumer(), true); + rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId()); + assertTrue(dmeDeployer.deployRappInstance(rapp, rappInstance)); + mockServer.verify(); + } + + @Test + void testDeployrAppInstanceFailureWithInfoType() { + Rapp rapp = getRapp(Optional.empty()); + RappInstance rappInstance = rappDmeResourceBuilder.getRappInstance(); + getMockServerClientCreateInfoType(rappInstance.getDme().getInfoTypesProducer().toArray()[0].toString(), false); + rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId()); + assertFalse(dmeDeployer.deployRappInstance(rapp, rappInstance)); + mockServer.verify(); + } + + @Test + void testDeployrAppInstanceFailureWithInfoProducer() { + Rapp rapp = getRapp(Optional.empty()); + RappInstance rappInstance = rappDmeResourceBuilder.getRappInstance(); + getMockServerClientCreateInfoType(rappInstance.getDme().getInfoTypesProducer().toArray()[0].toString(), true); + getMockServerClientCreateInfoProducer(rappInstance.getDme().getInfoProducer(), false); + rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId()); + assertFalse(dmeDeployer.deployRappInstance(rapp, rappInstance)); + mockServer.verify(); + } + + @Test + void testDeployrAppInstanceFailureWithInfoConsumer() { + Rapp rapp = getRapp(Optional.empty()); + RappInstance rappInstance = rappDmeResourceBuilder.getRappInstance(); + getMockServerClientCreateInfoType(rappInstance.getDme().getInfoTypesProducer().toArray()[0].toString(), true); + getMockServerClientCreateInfoProducer(rappInstance.getDme().getInfoProducer(), true); + getMockServerClientCreateInfoConsumer(rappInstance.getDme().getInfoConsumer(), false); + rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId()); + assertFalse(dmeDeployer.deployRappInstance(rapp, rappInstance)); + mockServer.verify(); + } + + @Test + void testUndeployrAppInstanceSuccess() { + Rapp rapp = getRapp(Optional.empty()); + rapp.setState(RappState.PRIMED); + RappInstance rappInstance = rappDmeResourceBuilder.getRappInstance(); + getMockServerClientDeleteInfoConsumer(rappInstance.getDme().getInfoConsumer(), true); + getMockServerClientDeleteInfoProducer(rappInstance.getDme().getInfoProducer(), true); + rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId()); + assertTrue(dmeDeployer.undeployRappInstance(rapp, rappInstance)); + mockServer.verify(); + } + + + @Test + void testUndeployrAppInstanceFailureWithInfoProducer() { + Rapp rapp = getRapp(Optional.empty()); + rapp.setState(RappState.PRIMED); + RappInstance rappInstance = rappDmeResourceBuilder.getRappInstance(); + getMockServerClientDeleteInfoConsumer(rappInstance.getDme().getInfoConsumer(), true); + getMockServerClientDeleteInfoProducer(rappInstance.getDme().getInfoProducer(), false); + rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId()); + assertFalse(dmeDeployer.undeployRappInstance(rapp, rappInstance)); + mockServer.verify(); + } + + @Test + void testUndeployrAppInstanceFailureWithInfoConsumer() { + Rapp rapp = getRapp(Optional.empty()); + rapp.setState(RappState.PRIMED); + RappInstance rappInstance = rappDmeResourceBuilder.getRappInstance(); + getMockServerClientDeleteInfoConsumer(rappInstance.getDme().getInfoConsumer(), false); + rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId()); + assertFalse(dmeDeployer.undeployRappInstance(rapp, rappInstance)); + mockServer.verify(); + } + + @Test + void testCreateInfoTypeFailureInvalidInfoType() { + Rapp rapp = getRapp(Optional.empty()); + assertFalse(dmeDeployer.createInfoTypes(rapp, null)); + } + + @Test + void testCreateInfoTypeFailureInvalidInfoProducer() { + Rapp rapp = getRapp(Optional.empty()); + assertFalse(dmeDeployer.createInfoProducer(rapp, "")); + } + + @Test + void testCreateInfoTypeFailureInvalidInfoConsumer() { + Rapp rapp = getRapp(Optional.empty()); + assertFalse(dmeDeployer.createInfoConsumer(rapp, "")); + } + + Rapp getRapp(Optional rappOptional) { + return Rapp.builder().rappId(rappOptional.orElse(UUID.randomUUID())).name("").packageName(validRappFile) + .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED).build(); + } + + void getMockServerClientCreateInfoType(String infoType, boolean isSuccess) { + if (isSuccess) { + mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_INFO_TYPE, infoType))) + .andExpect(method(HttpMethod.PUT)).andRespond(withStatus(HttpStatus.CREATED)); + } else { + mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_INFO_TYPE, infoType))) + .andExpect(method(HttpMethod.PUT)).andRespond(withStatus(HttpStatus.BAD_GATEWAY)); + } + } + + void getMockServerClientCreateInfoProducer(String infoProducer, boolean isSuccess) { + if (isSuccess) { + mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_INFO_PRODUCER, infoProducer))) + .andExpect(method(HttpMethod.PUT)).andRespond(withStatus(HttpStatus.CREATED)); + } else { + mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_INFO_PRODUCER, infoProducer))) + .andExpect(method(HttpMethod.PUT)).andRespond(withStatus(HttpStatus.BAD_GATEWAY)); + } + } + + void getMockServerClientCreateInfoConsumer(String infoConsumer, boolean isSuccess) { + if (isSuccess) { + mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_INFO_CONSUMER, infoConsumer))) + .andExpect(method(HttpMethod.PUT)).andRespond(withStatus(HttpStatus.CREATED)); + } else { + mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_INFO_CONSUMER, infoConsumer))) + .andExpect(method(HttpMethod.PUT)).andRespond(withStatus(HttpStatus.BAD_GATEWAY)); + } + } + + void getMockServerClientDeleteInfoProducer(String infoProducer, boolean isSuccess) { + if (isSuccess) { + mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_INFO_PRODUCER, infoProducer))) + .andExpect(method(HttpMethod.DELETE)).andRespond(withStatus(HttpStatus.NO_CONTENT)); + } else { + mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_INFO_PRODUCER, infoProducer))) + .andExpect(method(HttpMethod.DELETE)).andRespond(withStatus(HttpStatus.BAD_GATEWAY)); + } + } + + void getMockServerClientDeleteInfoConsumer(String infoConsumer, boolean isSuccess) { + if (isSuccess) { + mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_INFO_CONSUMER, infoConsumer))) + .andExpect(method(HttpMethod.DELETE)).andRespond(withStatus(HttpStatus.NO_CONTENT)); + } else { + mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_INFO_CONSUMER, infoConsumer))) + .andExpect(method(HttpMethod.DELETE)).andRespond(withStatus(HttpStatus.BAD_GATEWAY)); + } + } + +} diff --git a/rapp-manager-dme/src/test/java/com/oransc/rappmanager/models/rapp/RappDmeResourceBuilder.java b/rapp-manager-dme/src/test/java/com/oransc/rappmanager/models/rapp/RappDmeResourceBuilder.java new file mode 100755 index 0000000..c12d981 --- /dev/null +++ b/rapp-manager-dme/src/test/java/com/oransc/rappmanager/models/rapp/RappDmeResourceBuilder.java @@ -0,0 +1,47 @@ +/*- + * ============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.models.rapp; + +import com.oransc.rappmanager.models.rappinstance.RappDMEInstance; +import com.oransc.rappmanager.models.rappinstance.RappInstance; +import java.util.Set; + +public class RappDmeResourceBuilder { + + public RappResources getResources() { + RappResources rappResources = new RappResources(); + RappResources.DMEResources dmeResources = + new RappResources.DMEResources(Set.of("json-file-data-from-filestore", "xml-file-data-from-filestore"), + Set.of("json-file-data-producer", "xml-file-data-producer"), + Set.of("json-file-consumer", "xml-file-consumer")); + rappResources.setDme(dmeResources); + return rappResources; + } + + public RappInstance getRappInstance() { + RappInstance rappInstance = new RappInstance(); + RappDMEInstance rappDMEInstance = new RappDMEInstance(); + rappDMEInstance.setInfoTypesProducer(Set.of("json-file-data-from-filestore")); + rappDMEInstance.setInfoProducer("json-file-data-producer"); + rappDMEInstance.setInfoTypeConsumer("json-file-data-from-filestore"); + rappDMEInstance.setInfoConsumer("json-file-consumer"); + rappInstance.setDme(rappDMEInstance); + return rappInstance; + } +} diff --git a/rapp-manager-dme/src/test/resources/application.yaml b/rapp-manager-dme/src/test/resources/application.yaml new file mode 100755 index 0000000..3c53841 --- /dev/null +++ b/rapp-manager-dme/src/test/resources/application.yaml @@ -0,0 +1,3 @@ +rappmanager: + dme: + baseurl: http://localhost:39390 diff --git a/rapp-manager-dme/src/test/resources/valid-rapp-package-new-info-type.csar b/rapp-manager-dme/src/test/resources/valid-rapp-package-new-info-type.csar new file mode 100755 index 0000000..67ae27c Binary files /dev/null and b/rapp-manager-dme/src/test/resources/valid-rapp-package-new-info-type.csar differ diff --git a/rapp-manager-dme/src/test/resources/valid-rapp-package.csar b/rapp-manager-dme/src/test/resources/valid-rapp-package.csar new file mode 100755 index 0000000..b3d75b2 Binary files /dev/null and b/rapp-manager-dme/src/test/resources/valid-rapp-package.csar differ diff --git a/rapp-manager-models/src/main/java/com/oransc/rappmanager/models/csar/RappCsarConfigurationHandler.java b/rapp-manager-models/src/main/java/com/oransc/rappmanager/models/csar/RappCsarConfigurationHandler.java index 73c644e..05bf056 100755 --- a/rapp-manager-models/src/main/java/com/oransc/rappmanager/models/csar/RappCsarConfigurationHandler.java +++ b/rapp-manager-models/src/main/java/com/oransc/rappmanager/models/csar/RappCsarConfigurationHandler.java @@ -47,11 +47,12 @@ public class RappCsarConfigurationHandler { private static final String ACM_COMPOSITION_JSON_LOCATION = "Files/Acm/definition/compositions.json"; private static final String ACM_DEFINITION_LOCATION = "Files/Acm/definition"; private static final String ACM_INSTANCES_LOCATION = "Files/Acm/instances"; - private static final String SME_PROVIDER_FUNCS_LOCATION = "Files/Sme/providers"; private static final String SME_SERVICE_APIS_LOCATION = "Files/Sme/serviceapis"; - private static final String SME_INVOKERS_LOCATION = "Files/Sme/invokers"; + private static final String DME_INFO_TYPES_LOCATION = "Files/Dme/infotypes"; + private static final String DME_INFO_PRODUCERS_LOCATION = "Files/Dme/infoproducers"; + private static final String DME_INFO_CONSUMERS_LOCATION = "Files/Dme/infoconsumers"; public boolean isValidRappPackage(MultipartFile multipartFile) { @@ -88,7 +89,7 @@ public class RappCsarConfigurationHandler { } String getPayload(Rapp rapp, String location) { - logger.info("Getting payload for {} from {}", rapp.getRappId(), location); + logger.debug("Getting payload for {} from {}", rapp.getRappId(), location); File csarFile = getCsarFile(rapp); return getFileFromCsar(csarFile, location).toString(); } @@ -136,6 +137,18 @@ public class RappCsarConfigurationHandler { getResourceUri(ACM_DEFINITION_LOCATION, rapp.getRappResources().getAcm().getCompositionDefinitions())); } + public String getDmeInfoProducerPayload(Rapp rapp, String producerIdentifier) { + return getPayload(rapp, getResourceUri(DME_INFO_PRODUCERS_LOCATION, producerIdentifier)); + } + + public String getDmeInfoTypePayload(Rapp rapp, String infoTypeIdentifier) { + return getPayload(rapp, getResourceUri(DME_INFO_TYPES_LOCATION, infoTypeIdentifier)); + } + + public String getDmeInfoConsumerPayload(Rapp rapp, String infoConsumerIdentifier) { + return getPayload(rapp, getResourceUri(DME_INFO_CONSUMERS_LOCATION, infoConsumerIdentifier)); + } + String getResourceUri(String resourceLocation, String resource) { return resourceLocation + "/" + resource + ".json"; } @@ -152,6 +165,10 @@ public class RappCsarConfigurationHandler { getFileListFromCsar(csarFile, SME_PROVIDER_FUNCS_LOCATION)) .serviceApis(getFileListFromCsar(csarFile, SME_SERVICE_APIS_LOCATION)) .invokers(getFileListFromCsar(csarFile, SME_INVOKERS_LOCATION)).build()); + rappResources.setDme(RappResources.DMEResources.builder() + .infoTypes(getFileListFromCsar(csarFile, DME_INFO_TYPES_LOCATION)) + .infoProducers(getFileListFromCsar(csarFile, DME_INFO_PRODUCERS_LOCATION)) + .infoConsumers(getFileListFromCsar(csarFile, DME_INFO_CONSUMERS_LOCATION)).build()); } } catch (Exception e) { logger.warn("Error in getting the rapp resources", e); diff --git a/rapp-manager-models/src/main/java/com/oransc/rappmanager/models/rapp/Rapp.java b/rapp-manager-models/src/main/java/com/oransc/rappmanager/models/rapp/Rapp.java index 0aac806..a845fac 100755 --- a/rapp-manager-models/src/main/java/com/oransc/rappmanager/models/rapp/Rapp.java +++ b/rapp-manager-models/src/main/java/com/oransc/rappmanager/models/rapp/Rapp.java @@ -41,4 +41,5 @@ public class Rapp { Map rappInstances = new HashMap<>(); UUID compositionId; + Boolean isDmeValid; } diff --git a/rapp-manager-models/src/main/java/com/oransc/rappmanager/models/rapp/RappEvent.java b/rapp-manager-models/src/main/java/com/oransc/rappmanager/models/rapp/RappEvent.java index 1bdc67d..7cb00be 100755 --- a/rapp-manager-models/src/main/java/com/oransc/rappmanager/models/rapp/RappEvent.java +++ b/rapp-manager-models/src/main/java/com/oransc/rappmanager/models/rapp/RappEvent.java @@ -28,5 +28,9 @@ public enum RappEvent { ACMUNDEPLOYED, SMEUNDEPLOYED, ACMUNDEPLOYFAILED, - SMEUNDEPLOYFAILED + SMEUNDEPLOYFAILED, + DMEDEPLOYED, + DMEDEPLOYFAILED, + DMEUNDEPLOYED, + DMEUNDEPLOYFAILED } diff --git a/rapp-manager-models/src/main/java/com/oransc/rappmanager/models/rapp/RappResources.java b/rapp-manager-models/src/main/java/com/oransc/rappmanager/models/rapp/RappResources.java index a3033e2..d9344e4 100755 --- a/rapp-manager-models/src/main/java/com/oransc/rappmanager/models/rapp/RappResources.java +++ b/rapp-manager-models/src/main/java/com/oransc/rappmanager/models/rapp/RappResources.java @@ -27,6 +27,7 @@ public class RappResources { ACMResources acm; SMEResources sme; + DMEResources dme; @Data @Builder @@ -44,4 +45,13 @@ public class RappResources { Set serviceApis; Set invokers; } + + @Data + @Builder + public static class DMEResources { + + Set infoTypes; + Set infoProducers; + Set infoConsumers; + } } diff --git a/rapp-manager-models/src/main/java/com/oransc/rappmanager/models/rappinstance/RappDMEInstance.java b/rapp-manager-models/src/main/java/com/oransc/rappmanager/models/rappinstance/RappDMEInstance.java new file mode 100755 index 0000000..bd1d19d --- /dev/null +++ b/rapp-manager-models/src/main/java/com/oransc/rappmanager/models/rappinstance/RappDMEInstance.java @@ -0,0 +1,31 @@ +/*- + * ============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.models.rappinstance; + +import java.util.Set; +import lombok.Data; + +@Data +public class RappDMEInstance { + + Set infoTypesProducer; + String infoProducer; + String infoTypeConsumer; + String infoConsumer; +} diff --git a/rapp-manager-models/src/main/java/com/oransc/rappmanager/models/rappinstance/RappInstance.java b/rapp-manager-models/src/main/java/com/oransc/rappmanager/models/rappinstance/RappInstance.java index b5e54a7..39d2709 100755 --- a/rapp-manager-models/src/main/java/com/oransc/rappmanager/models/rappinstance/RappInstance.java +++ b/rapp-manager-models/src/main/java/com/oransc/rappmanager/models/rappinstance/RappInstance.java @@ -28,4 +28,5 @@ public class RappInstance { RappInstanceState state = RappInstanceState.UNDEPLOYED; RappACMInstance acm; RappSMEInstance sme; + RappDMEInstance dme; } diff --git a/rapp-manager-models/src/main/java/com/oransc/rappmanager/models/statemachine/RappInstanceStateMachineConfig.java b/rapp-manager-models/src/main/java/com/oransc/rappmanager/models/statemachine/RappInstanceStateMachineConfig.java index e1bc784..34c9a92 100755 --- a/rapp-manager-models/src/main/java/com/oransc/rappmanager/models/statemachine/RappInstanceStateMachineConfig.java +++ b/rapp-manager-models/src/main/java/com/oransc/rappmanager/models/statemachine/RappInstanceStateMachineConfig.java @@ -57,12 +57,18 @@ public class RappInstanceStateMachineConfig extends EnumStateMachineConfigurerAd .withExternal() .source(RappInstanceState.DEPLOYING).target(RappInstanceState.UNDEPLOYED).event(RappEvent.SMEDEPLOYFAILED) .and() + .withExternal() + .source(RappInstanceState.DEPLOYING).target(RappInstanceState.UNDEPLOYED).event(RappEvent.DMEDEPLOYFAILED) + .and() .withExternal() .source(RappInstanceState.UNDEPLOYING).target(RappInstanceState.DEPLOYED).event(RappEvent.ACMUNDEPLOYFAILED) .and() .withExternal() .source(RappInstanceState.UNDEPLOYING).target(RappInstanceState.DEPLOYED).event(RappEvent.SMEUNDEPLOYFAILED) .and() + .withExternal() + .source(RappInstanceState.UNDEPLOYING).target(RappInstanceState.DEPLOYED).event(RappEvent.DMEUNDEPLOYFAILED) + .and() .withExternal() .source(RappInstanceState.DEPLOYED).target(RappInstanceState.UNDEPLOYING).event(RappEvent.UNDEPLOYING) .and() @@ -74,12 +80,20 @@ public class RappInstanceStateMachineConfig extends EnumStateMachineConfigurerAd .source(RappInstanceState.DEPLOYING).target(RappInstanceState.DEPLOYED).event(RappEvent.SMEDEPLOYED) .guard(deployedGuard()) .and() + .withExternal() + .source(RappInstanceState.DEPLOYING).target(RappInstanceState.DEPLOYED).event(RappEvent.DMEDEPLOYED) + .guard(deployedGuard()) + .and() .withExternal() .source(RappInstanceState.UNDEPLOYING).target(RappInstanceState.UNDEPLOYED).event(RappEvent.ACMUNDEPLOYED) .guard(undeployedGuard()) .and() .withExternal() .source(RappInstanceState.UNDEPLOYING).target(RappInstanceState.UNDEPLOYED).event(RappEvent.SMEUNDEPLOYED) + .guard(undeployedGuard()) + .and() + .withExternal() + .source(RappInstanceState.UNDEPLOYING).target(RappInstanceState.UNDEPLOYED).event(RappEvent.DMEUNDEPLOYED) .guard(undeployedGuard()); } @@ -90,7 +104,8 @@ public class RappInstanceStateMachineConfig extends EnumStateMachineConfigurerAd return stateContext -> { stateContext.getExtendedState().getVariables().put(stateContext.getEvent(), true); return stateContext.getExtendedState().getVariables().get(RappEvent.ACMDEPLOYED) != null - && stateContext.getExtendedState().getVariables().get(RappEvent.SMEDEPLOYED) != null; + && stateContext.getExtendedState().getVariables().get(RappEvent.SMEDEPLOYED) != null + && stateContext.getExtendedState().getVariables().get(RappEvent.DMEDEPLOYED) != null; }; } @@ -99,7 +114,8 @@ public class RappInstanceStateMachineConfig extends EnumStateMachineConfigurerAd return stateContext -> { stateContext.getExtendedState().getVariables().put(stateContext.getEvent(), true); return stateContext.getExtendedState().getVariables().get(RappEvent.ACMUNDEPLOYED) != null - && stateContext.getExtendedState().getVariables().get(RappEvent.SMEUNDEPLOYED) != null; + && stateContext.getExtendedState().getVariables().get(RappEvent.SMEUNDEPLOYED) != null + && stateContext.getExtendedState().getVariables().get(RappEvent.DMEUNDEPLOYED) != null; }; } } diff --git a/rapp-manager-models/src/test/java/com/oransc/rappmanager/models/csar/RappCsarConfigurationHandlerTest.java b/rapp-manager-models/src/test/java/com/oransc/rappmanager/models/csar/RappCsarConfigurationHandlerTest.java index 0a18ec4..d5e8d42 100755 --- a/rapp-manager-models/src/test/java/com/oransc/rappmanager/models/csar/RappCsarConfigurationHandlerTest.java +++ b/rapp-manager-models/src/test/java/com/oransc/rappmanager/models/csar/RappCsarConfigurationHandlerTest.java @@ -22,11 +22,14 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.mockito.Mockito.mock; import com.oransc.rappmanager.models.rapp.Rapp; import com.oransc.rappmanager.models.rapp.RappResources; import com.oransc.rappmanager.models.rappinstance.RappACMInstance; +import com.oransc.rappmanager.models.rappinstance.RappDMEInstance; import com.oransc.rappmanager.models.rappinstance.RappSMEInstance; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -74,6 +77,18 @@ class RappCsarConfigurationHandlerTest { assertEquals(Boolean.FALSE, rappCsarConfigurationHandler.isValidRappPackage(multipartFile)); } + @Test + void testCsarPackageValidationFailureWithoutOrginalName() throws IOException { + MultipartFile multipartFile = mock(MultipartFile.class); + assertEquals(Boolean.FALSE, rappCsarConfigurationHandler.isValidRappPackage(multipartFile)); + } + + @Test + void testInvalidCsarFileExist() { + MultipartFile multipartFile = mock(MultipartFile.class); + assertEquals(Boolean.FALSE, rappCsarConfigurationHandler.isFileExistsInCsar(multipartFile, "INVALID_LOCATION")); + } + @Test void testCsarInstantiationPayload() throws JSONException { Rapp rapp = Rapp.builder().name("").packageName(validRappFile).packageLocation(validCsarFileLocation).build(); @@ -99,6 +114,13 @@ class RappCsarConfigurationHandlerTest { assertThat(fileListFromCsar).isEmpty(); } + @Test + void testInvalidFileListingFromCsar() { + File file = new File("InvalidFile"); + ByteArrayOutputStream fileByteArray = rappCsarConfigurationHandler.getFileFromCsar(file, null); + assertThat(fileByteArray.size()).isZero(); + } + @Test void testListResources() { UUID rappId = UUID.randomUUID(); @@ -108,10 +130,13 @@ class RappCsarConfigurationHandlerTest { RappResources rappResources = rappCsarConfigurationHandler.getRappResource(rapp); assertThat(rappResources).isNotNull(); assertNotNull(rappResources.getAcm().getCompositionDefinitions()); - assertThat(rappResources.getAcm().getCompositionInstances()).hasSize(3); + assertThat(rappResources.getAcm().getCompositionInstances()).hasSize(4); assertThat(rappResources.getSme().getProviderFunctions()).hasSize(4); assertThat(rappResources.getSme().getServiceApis()).hasSize(2); assertThat(rappResources.getSme().getInvokers()).hasSize(2); + assertThat(rappResources.getDme().getInfoTypes()).hasSize(2); + assertThat(rappResources.getDme().getInfoProducers()).hasSize(2); + assertThat(rappResources.getDme().getInfoConsumers()).hasSize(2); } @Test @@ -122,6 +147,7 @@ class RappCsarConfigurationHandlerTest { assertThat(rappResources).isNotNull(); assertNull(rappResources.getAcm()); assertNull(rappResources.getSme()); + assertNull(rappResources.getDme()); } @Test @@ -187,4 +213,43 @@ class RappCsarConfigurationHandlerTest { assertNotNull(smeProviderDomainPayload); } + @Test + void testGetDmeInfoTypePayload() { + UUID rappId = UUID.randomUUID(); + RappDMEInstance rappDMEInstance = new RappDMEInstance(); + rappDMEInstance.setInfoTypesProducer(Set.of("json-file-data-from-filestore")); + Rapp rapp = + Rapp.builder().rappId(rappId).name("").packageName(validRappFile).packageLocation(validCsarFileLocation) + .build(); + String dmeInfoTypePayload = rappCsarConfigurationHandler.getDmeInfoTypePayload(rapp, + rappDMEInstance.getInfoTypesProducer().iterator().next()); + assertNotNull(dmeInfoTypePayload); + } + + @Test + void testGetDmeInfoProducerPayload() { + UUID rappId = UUID.randomUUID(); + RappDMEInstance rappDMEInstance = new RappDMEInstance(); + rappDMEInstance.setInfoProducer("json-file-data-producer"); + Rapp rapp = + Rapp.builder().rappId(rappId).name("").packageName(validRappFile).packageLocation(validCsarFileLocation) + .build(); + String dmeInfoProducerPayload = + rappCsarConfigurationHandler.getDmeInfoProducerPayload(rapp, rappDMEInstance.getInfoProducer()); + assertNotNull(dmeInfoProducerPayload); + } + + @Test + void testGetDmeInfoConsumerPayload() { + UUID rappId = UUID.randomUUID(); + RappDMEInstance rappDMEInstance = new RappDMEInstance(); + rappDMEInstance.setInfoConsumer("json-file-consumer"); + Rapp rapp = + Rapp.builder().rappId(rappId).name("").packageName(validRappFile).packageLocation(validCsarFileLocation) + .build(); + String dmeInfoConsumerPayload = + rappCsarConfigurationHandler.getDmeInfoConsumerPayload(rapp, rappDMEInstance.getInfoConsumer()); + assertNotNull(dmeInfoConsumerPayload); + } + } diff --git a/rapp-manager-models/src/test/java/com/oransc/rappmanager/models/statemachine/RappInstanceStateMachineConfigTest.java b/rapp-manager-models/src/test/java/com/oransc/rappmanager/models/statemachine/RappInstanceStateMachineConfigTest.java index 59d5c94..4657c76 100755 --- a/rapp-manager-models/src/test/java/com/oransc/rappmanager/models/statemachine/RappInstanceStateMachineConfigTest.java +++ b/rapp-manager-models/src/test/java/com/oransc/rappmanager/models/statemachine/RappInstanceStateMachineConfigTest.java @@ -75,7 +75,7 @@ class RappInstanceStateMachineConfigTest { } @ParameterizedTest - @EnumSource(value = RappEvent.class, names = {"ACMDEPLOYED", "SMEDEPLOYED"}) + @EnumSource(value = RappEvent.class, names = {"ACMDEPLOYED", "SMEDEPLOYED", "DMEDEPLOYED" }) void testIndividualDeployedState(RappEvent rappEvent) throws Exception { StateMachineTestPlan plan = StateMachineTestPlanBuilder.builder().stateMachine(stateMachine).step() @@ -92,23 +92,51 @@ class RappInstanceStateMachineConfigTest { .expectState(RappInstanceState.UNDEPLOYED).and().step().sendEvent(RappEvent.DEPLOYING) .expectState(RappInstanceState.DEPLOYING).expectStateChanged(1).and().step() .sendEvent(RappEvent.ACMDEPLOYED).expectState(RappInstanceState.DEPLOYING).and().step() - .sendEvent(RappEvent.SMEDEPLOYED).expectState(RappInstanceState.DEPLOYED).expectStateChanged(1) + .sendEvent(RappEvent.SMEDEPLOYED).expectState(RappInstanceState.DEPLOYING).and().step() + .sendEvent(RappEvent.DMEDEPLOYED).expectState(RappInstanceState.DEPLOYED).expectStateChanged(1) .and().build(); plan.test(); } @Test - void testDeployFailedState() throws Exception { + void testAcmDeployFailedState() throws Exception { + StateMachineTestPlan plan = + StateMachineTestPlanBuilder.builder().stateMachine(stateMachine).step() + .expectState(RappInstanceState.UNDEPLOYED).and().step().sendEvent(RappEvent.DEPLOYING) + .expectState(RappInstanceState.DEPLOYING).expectStateChanged(1).and().step() + .sendEvent(RappEvent.SMEDEPLOYED).expectState(RappInstanceState.DEPLOYING).and().step() + .sendEvent(RappEvent.DMEDEPLOYED).expectState(RappInstanceState.DEPLOYING).and().step() + .sendEvent(RappEvent.ACMDEPLOYFAILED).expectState(RappInstanceState.UNDEPLOYED) + .expectStateChanged(1).and().build(); + plan.test(); + } + + @Test + void testSmeDeployFailedState() throws Exception { StateMachineTestPlan plan = StateMachineTestPlanBuilder.builder().stateMachine(stateMachine).step() .expectState(RappInstanceState.UNDEPLOYED).and().step().sendEvent(RappEvent.DEPLOYING) .expectState(RappInstanceState.DEPLOYING).expectStateChanged(1).and().step() .sendEvent(RappEvent.ACMDEPLOYED).expectState(RappInstanceState.DEPLOYING).and().step() + .sendEvent(RappEvent.DMEDEPLOYED).expectState(RappInstanceState.DEPLOYING).and().step() .sendEvent(RappEvent.SMEDEPLOYFAILED).expectState(RappInstanceState.UNDEPLOYED) .expectStateChanged(1).and().build(); plan.test(); } + @Test + void testDmeDeployFailedState() throws Exception { + StateMachineTestPlan plan = + StateMachineTestPlanBuilder.builder().stateMachine(stateMachine).step() + .expectState(RappInstanceState.UNDEPLOYED).and().step().sendEvent(RappEvent.DEPLOYING) + .expectState(RappInstanceState.DEPLOYING).expectStateChanged(1).and().step() + .sendEvent(RappEvent.ACMDEPLOYED).expectState(RappInstanceState.DEPLOYING).and().step() + .sendEvent(RappEvent.SMEDEPLOYED).expectState(RappInstanceState.DEPLOYING).and().step() + .sendEvent(RappEvent.DMEDEPLOYFAILED).expectState(RappInstanceState.UNDEPLOYED) + .expectStateChanged(1).and().build(); + plan.test(); + } + @Test void testUndeployingState() throws Exception { StateMachineTestPlan plan = @@ -116,21 +144,23 @@ class RappInstanceStateMachineConfigTest { .expectState(RappInstanceState.UNDEPLOYED).and().step().sendEvent(RappEvent.DEPLOYING) .expectState(RappInstanceState.DEPLOYING).expectStateChanged(1).and().step() .sendEvent(RappEvent.ACMDEPLOYED).expectState(RappInstanceState.DEPLOYING).and().step() - .sendEvent(RappEvent.SMEDEPLOYED).expectState(RappInstanceState.DEPLOYED).expectStateChanged(1) + .sendEvent(RappEvent.SMEDEPLOYED).expectState(RappInstanceState.DEPLOYING).and().step() + .sendEvent(RappEvent.DMEDEPLOYED).expectState(RappInstanceState.DEPLOYED).expectStateChanged(1) .and().step().sendEvent(RappEvent.UNDEPLOYING).expectState(RappInstanceState.UNDEPLOYING) .expectStateChanged(1).and().build(); plan.test(); } @ParameterizedTest - @EnumSource(value = RappEvent.class, names = {"ACMUNDEPLOYED", "SMEUNDEPLOYED"}) + @EnumSource(value = RappEvent.class, names = {"ACMUNDEPLOYED", "SMEUNDEPLOYED", "DMEUNDEPLOYED" }) void testIndividualUndeployedState(RappEvent rappEvent) throws Exception { StateMachineTestPlan plan = StateMachineTestPlanBuilder.builder().stateMachine(stateMachine).step() .expectState(RappInstanceState.UNDEPLOYED).and().step().sendEvent(RappEvent.DEPLOYING) .expectState(RappInstanceState.DEPLOYING).expectStateChanged(1).and().step() .sendEvent(RappEvent.ACMDEPLOYED).expectState(RappInstanceState.DEPLOYING).and().step() - .sendEvent(RappEvent.SMEDEPLOYED).expectState(RappInstanceState.DEPLOYED).expectStateChanged(1) + .sendEvent(RappEvent.SMEDEPLOYED).expectState(RappInstanceState.DEPLOYING).and().step() + .sendEvent(RappEvent.DMEDEPLOYED).expectState(RappInstanceState.DEPLOYED).expectStateChanged(1) .and().step().sendEvent(RappEvent.UNDEPLOYING).expectState(RappInstanceState.UNDEPLOYING) .expectStateChanged(1).and().step().sendEvent(rappEvent) .expectState(RappInstanceState.UNDEPLOYING).and().build(); @@ -144,26 +174,64 @@ class RappInstanceStateMachineConfigTest { .expectState(RappInstanceState.UNDEPLOYED).and().step().sendEvent(RappEvent.DEPLOYING) .expectState(RappInstanceState.DEPLOYING).expectStateChanged(1).and().step() .sendEvent(RappEvent.ACMDEPLOYED).expectState(RappInstanceState.DEPLOYING).and().step() - .sendEvent(RappEvent.SMEDEPLOYED).expectState(RappInstanceState.DEPLOYED).expectStateChanged(1) + .sendEvent(RappEvent.SMEDEPLOYED).expectState(RappInstanceState.DEPLOYING).and().step() + .sendEvent(RappEvent.DMEDEPLOYED).expectState(RappInstanceState.DEPLOYED).expectStateChanged(1) .and().step().sendEvent(RappEvent.UNDEPLOYING).expectState(RappInstanceState.UNDEPLOYING) .expectStateChanged(1).and().step().sendEvent(RappEvent.ACMUNDEPLOYED) .expectState(RappInstanceState.UNDEPLOYING).and().step().sendEvent(RappEvent.SMEUNDEPLOYED) - .expectState(RappInstanceState.UNDEPLOYED).expectStateChanged(1).and().build(); + .expectState(RappInstanceState.UNDEPLOYING).and().step().sendEvent(RappEvent.DMEUNDEPLOYED) + .expectStateChanged(1).and().build(); + plan.test(); + } + + @Test + void testUndeployAcmFailedState() throws Exception { + StateMachineTestPlan plan = + StateMachineTestPlanBuilder.builder().stateMachine(stateMachine).step() + .expectState(RappInstanceState.UNDEPLOYED).and().step().sendEvent(RappEvent.DEPLOYING) + .expectState(RappInstanceState.DEPLOYING).expectStateChanged(1).and().step() + .sendEvent(RappEvent.ACMDEPLOYED).expectState(RappInstanceState.DEPLOYING).and().step() + .sendEvent(RappEvent.SMEDEPLOYED).expectState(RappInstanceState.DEPLOYING).and().step() + .sendEvent(RappEvent.DMEDEPLOYED).expectState(RappInstanceState.DEPLOYED).expectStateChanged(1) + .and().step().sendEvent(RappEvent.UNDEPLOYING).expectState(RappInstanceState.UNDEPLOYING) + .expectStateChanged(1).and().step().sendEvent(RappEvent.SMEUNDEPLOYED) + .expectState(RappInstanceState.UNDEPLOYING).and().step().sendEvent(RappEvent.DMEUNDEPLOYED) + .expectState(RappInstanceState.UNDEPLOYING).and().step().sendEvent(RappEvent.ACMUNDEPLOYFAILED) + .expectState(RappInstanceState.DEPLOYED).expectStateChanged(1).and().build(); plan.test(); } @Test - void testUndeployFailedState() throws Exception { + void testUndeploySmeFailedState() throws Exception { StateMachineTestPlan plan = StateMachineTestPlanBuilder.builder().stateMachine(stateMachine).step() .expectState(RappInstanceState.UNDEPLOYED).and().step().sendEvent(RappEvent.DEPLOYING) .expectState(RappInstanceState.DEPLOYING).expectStateChanged(1).and().step() .sendEvent(RappEvent.ACMDEPLOYED).expectState(RappInstanceState.DEPLOYING).and().step() - .sendEvent(RappEvent.SMEDEPLOYED).expectState(RappInstanceState.DEPLOYED).expectStateChanged(1) + .sendEvent(RappEvent.SMEDEPLOYED).expectState(RappInstanceState.DEPLOYING).and().step() + .sendEvent(RappEvent.DMEDEPLOYED).expectState(RappInstanceState.DEPLOYED).expectStateChanged(1) .and().step().sendEvent(RappEvent.UNDEPLOYING).expectState(RappInstanceState.UNDEPLOYING) .expectStateChanged(1).and().step().sendEvent(RappEvent.ACMUNDEPLOYED) + .expectState(RappInstanceState.UNDEPLOYING).and().step().sendEvent(RappEvent.DMEUNDEPLOYED) .expectState(RappInstanceState.UNDEPLOYING).and().step().sendEvent(RappEvent.SMEUNDEPLOYFAILED) .expectState(RappInstanceState.DEPLOYED).expectStateChanged(1).and().build(); plan.test(); } + + @Test + void testUndeployDmeFailedState() throws Exception { + StateMachineTestPlan plan = + StateMachineTestPlanBuilder.builder().stateMachine(stateMachine).step() + .expectState(RappInstanceState.UNDEPLOYED).and().step().sendEvent(RappEvent.DEPLOYING) + .expectState(RappInstanceState.DEPLOYING).expectStateChanged(1).and().step() + .sendEvent(RappEvent.ACMDEPLOYED).expectState(RappInstanceState.DEPLOYING).and().step() + .sendEvent(RappEvent.SMEDEPLOYED).expectState(RappInstanceState.DEPLOYING).and().step() + .sendEvent(RappEvent.DMEDEPLOYED).expectState(RappInstanceState.DEPLOYED).expectStateChanged(1) + .and().step().sendEvent(RappEvent.UNDEPLOYING).expectState(RappInstanceState.UNDEPLOYING) + .expectStateChanged(1).and().step().sendEvent(RappEvent.ACMUNDEPLOYED) + .expectState(RappInstanceState.UNDEPLOYING).and().step().sendEvent(RappEvent.SMEUNDEPLOYED) + .expectState(RappInstanceState.UNDEPLOYING).and().step().sendEvent(RappEvent.DMEUNDEPLOYFAILED) + .expectState(RappInstanceState.DEPLOYED).expectStateChanged(1).and().build(); + plan.test(); + } } diff --git a/rapp-manager-models/src/test/resources/valid-rapp-package.csar b/rapp-manager-models/src/test/resources/valid-rapp-package.csar index b8b36e3..b3d75b2 100755 Binary files a/rapp-manager-models/src/test/resources/valid-rapp-package.csar and b/rapp-manager-models/src/test/resources/valid-rapp-package.csar differ diff --git a/rapp-manager-sme/src/main/java/com/oransc/rappmanager/sme/service/SmeDeployer.java b/rapp-manager-sme/src/main/java/com/oransc/rappmanager/sme/service/SmeDeployer.java index 1760e9c..fc42f4d 100755 --- a/rapp-manager-sme/src/main/java/com/oransc/rappmanager/sme/service/SmeDeployer.java +++ b/rapp-manager-sme/src/main/java/com/oransc/rappmanager/sme/service/SmeDeployer.java @@ -21,12 +21,11 @@ package com.oransc.rappmanager.sme.service; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -import com.oransc.rappmanager.models.rapp.Rapp; -import com.oransc.rappmanager.models.csar.RappCsarConfigurationHandler; import com.oransc.rappmanager.models.RappDeployer; +import com.oransc.rappmanager.models.csar.RappCsarConfigurationHandler; +import com.oransc.rappmanager.models.rapp.Rapp; import com.oransc.rappmanager.models.rapp.RappEvent; import com.oransc.rappmanager.models.rappinstance.RappInstance; -import com.oransc.rappmanager.models.cache.RappCacheService; import com.oransc.rappmanager.models.statemachine.RappInstanceStateMachine; import com.oransc.rappmanager.sme.invoker.data.APIInvokerEnrolmentDetails; import com.oransc.rappmanager.sme.provider.data.APIProviderEnrolmentDetails; @@ -49,6 +48,7 @@ public class SmeDeployer implements RappDeployer { private final com.oransc.rappmanager.sme.provider.rest.DefaultApiClient providerDefaultApiClient; + private final com.oransc.rappmanager.sme.publishservice.rest.DefaultApiClient publishServiceDefaultApiClient; private final com.oransc.rappmanager.sme.invoker.rest.DefaultApiClient invokerDefaultApiClient; @@ -57,8 +57,6 @@ public class SmeDeployer implements RappDeployer { private final ObjectMapper objectMapper; - private final RappCacheService rappCacheService; - private final RappInstanceStateMachine rappInstanceStateMachine; private String amfRegistrationId; @@ -131,7 +129,7 @@ public class SmeDeployer implements RappDeployer { @Override public boolean deprimeRapp(Rapp rapp) { - //If there is any deprimgng operations + //If there is any depriming operations return true; } @@ -190,7 +188,8 @@ public class SmeDeployer implements RappDeployer { if (providerApiPayload != null) { ServiceAPIDescription serviceAPIDescription = objectMapper.readValue(providerApiPayload, ServiceAPIDescription.class); - serviceAPIDescription.getAefProfiles().forEach(aefProfile -> aefProfile.setAefId(rappInstance.getSme().getAefId())); + serviceAPIDescription.getAefProfiles() + .forEach(aefProfile -> aefProfile.setAefId(rappInstance.getSme().getAefId())); ServiceAPIDescription serviceAPIDescriptionResponse = publishServiceDefaultApiClient.postApfIdServiceApis(rappInstance.getSme().getApfId(), serviceAPIDescription);