diff options
7 files changed, 404 insertions, 5 deletions
diff --git a/adapters/mso-catalog-db-adapter/src/test/java/org/onap/so/db/catalog/client/CatalogDbClientTest.java b/adapters/mso-catalog-db-adapter/src/test/java/org/onap/so/db/catalog/client/CatalogDbClientTest.java index 707a2a45af..739b4b62de 100644 --- a/adapters/mso-catalog-db-adapter/src/test/java/org/onap/so/db/catalog/client/CatalogDbClientTest.java +++ b/adapters/mso-catalog-db-adapter/src/test/java/org/onap/so/db/catalog/client/CatalogDbClientTest.java @@ -22,8 +22,8 @@ package org.onap.so.db.catalog.client; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -428,7 +428,7 @@ public class CatalogDbClientTest extends CatalogDbAdapterBaseTest { } @Test - public void testPostCloudSite() { + public void testCloudSiteClient() { CatalogDbClientPortChanger localClient = new CatalogDbClientPortChanger( "http://localhost:" + client.wiremockPort, msoAdaptersAuth, client.wiremockPort); CloudSite cloudSite = new CloudSite(); @@ -455,6 +455,19 @@ public class CatalogDbClientTest extends CatalogDbAdapterBaseTest { assertEquals("TESTCLLI", getCloudSite.getClli()); assertEquals("regionId", getCloudSite.getRegionId()); assertEquals("RANDOMID", getCloudSite.getIdentityServiceId()); + + getCloudSite.setClli("clli2"); + getCloudSite.setRegionId("region2"); + + CloudSite updatedCloudSite = this.client.updateCloudSite(getCloudSite); + assertNotNull(updatedCloudSite); + assertNotNull(updatedCloudSite.getIdentityService()); + assertEquals("clli2", updatedCloudSite.getClli()); + assertEquals("region2", updatedCloudSite.getRegionId()); + + this.client.deleteCloudSite(getCloudSite.getId()); + getCloudSite = this.client.getCloudSite("MTN6"); + assertNull(getCloudSite); } @Test diff --git a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/cloudregion/CloudException.java b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/cloudregion/CloudException.java new file mode 100644 index 0000000000..9cdf7e3b2a --- /dev/null +++ b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/cloudregion/CloudException.java @@ -0,0 +1,14 @@ +package org.onap.so.adapters.cloudregion; + +public class CloudException extends RuntimeException { + + /** + * + */ + private static final long serialVersionUID = -2631715095942372451L; + + public CloudException(String error, Exception e) { + super(error, e); + } + +} diff --git a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/cloudregion/CloudRegionRestV1.java b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/cloudregion/CloudRegionRestV1.java new file mode 100644 index 0000000000..780480507b --- /dev/null +++ b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/cloudregion/CloudRegionRestV1.java @@ -0,0 +1,102 @@ +/*- + * ============LICENSE_START======================================================= + * OPENECOMP - SO + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Modifications Copyright (C) 2018 IBM. + * Modifications Copyright (c) 2019 Samsung + * ================================================================================ + * 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 org.onap.so.adapters.cloudregion; + + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import org.apache.http.HttpStatus; +import org.onap.so.db.catalog.beans.CloudSite; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; + + +@Path("/v1/cloud-region") +@Api(value = "/v1/cloud-region", description = "root of cloud region adapter") +@Component +public class CloudRegionRestV1 { + private static Logger logger = LoggerFactory.getLogger(CloudRegionRestV1.class); + + @Autowired + private CloudRestImpl cloudRestImpl; + + @POST + @Path("{cloud-region-id}/{cloud-owner}") + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + @ApiOperation(value = "CreateCloudRegion", response = Response.class, + notes = "Create a cloud site in MSO and Region In AAI") + @ApiResponses({@ApiResponse(code = 201, message = "Cloud Region has been created"), + @ApiResponse(code = 500, message = "Create Cloud Region has failed")}) + public Response createCloudRegion( + @ApiParam(value = "cloud-region-id", required = true) @PathParam("cloud-region-id") String cloudRegionId, + @ApiParam(value = "cloud-owner", required = true) @PathParam("cloud-owner") String cloudOwner, + @ApiParam(value = "CloudSite", required = true) final CloudSite cloudSite) { + cloudRestImpl.createCloudRegion(cloudSite, cloudOwner); + return Response.status(HttpStatus.SC_CREATED).build(); + } + + @DELETE + @Path("{cloud-region-id}/{cloud-owner}") + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + @ApiOperation(value = "CreateCloudRegion", response = Response.class, notes = "Delete an cloud Region in SO") + @ApiResponses({@ApiResponse(code = 204, message = "cloud Region has been deleted"), + @ApiResponse(code = 500, message = "Cloud Region delete has failed")}) + public Response deleteCloudRegion( + @ApiParam(value = "cloud-region-id", required = true) @PathParam("cloud-region-id") String cloudRegionId, + @ApiParam(value = "cloud-owner", required = true) @PathParam("cloud-owner") String cloudOwner) { + cloudRestImpl.deleteCloudRegion(cloudRegionId); + return Response.status(HttpStatus.SC_NO_CONTENT).build(); + } + + @PUT + @Path("{cloud-region-id}/{cloud-owner}") + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + @ApiOperation(value = "CreateCloudRegion", response = Response.class, notes = "Update an existing Cloud Region") + @ApiResponses({@ApiResponse(code = 200, message = "Cloud Region has been updated"), + @ApiResponse(code = 500, message = "Update Cloud Region has failed examine entity object for details")}) + public Response updateCloudRegion( + @ApiParam(value = "cloud-region-id", required = true) @PathParam("cloud-region-id") String cloudRegionId, + @ApiParam(value = "cloud-owner", required = true) @PathParam("cloud-owner") String cloudOwner, + @ApiParam(value = "CloudSite", required = true) final CloudSite cloudSite) { + cloudRestImpl.updateCloudRegion(cloudSite, cloudOwner); + return Response.status(HttpStatus.SC_OK).build(); + } +} diff --git a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/cloudregion/CloudRestImpl.java b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/cloudregion/CloudRestImpl.java new file mode 100644 index 0000000000..4cde8655ae --- /dev/null +++ b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/cloudregion/CloudRestImpl.java @@ -0,0 +1,97 @@ +package org.onap.so.adapters.cloudregion; + +import java.util.Optional; +import org.onap.aai.domain.yang.CloudRegion; +import org.onap.so.client.aai.AAIObjectType; +import org.onap.so.client.aai.AAIResourcesClient; +import org.onap.so.client.aai.entities.uri.AAIResourceUri; +import org.onap.so.client.aai.entities.uri.AAIUriFactory; +import org.onap.so.db.catalog.beans.CloudSite; +import org.onap.so.db.catalog.client.CatalogDbClient; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + + +@Component +public class CloudRestImpl { + private static final Logger logger = LoggerFactory.getLogger(CloudRestImpl.class); + + private AAIResourcesClient aaiClient; + + @Autowired + private CatalogDbClient catalogDBClient; + + public void createCloudRegion(CloudSite cloudSite, String cloudOwner) throws CloudException { + createRegionInCatalogDb(cloudSite); + createCloudRegionInAAI(cloudSite, cloudOwner); + } + + public void updateCloudRegion(CloudSite cloudSite, String cloudOwner) throws CloudException { + updateRegionInCatalogDb(cloudSite); + } + + protected void updateRegionInCatalogDb(CloudSite cloudSite) { + try { + catalogDBClient.updateCloudSite(cloudSite); + } catch (Exception e) { + logger.error("Error updating cloud region in catalogdb", e); + throw new CloudException("Error updating cloud region in Catalog: " + e.getMessage(), e); + } + } + + public void deleteCloudRegion(String cloudRegionId) throws CloudException { + try { + catalogDBClient.deleteCloudSite(cloudRegionId); + } catch (Exception e) { + logger.error("Error deleting cloud region in catalogdb", e); + throw new CloudException("Error deleting cloud region in Catalog: " + e.getMessage(), e); + } + } + + protected void createCloudRegionInAAI(CloudSite cloudSite, String cloudOwner) { + try { + CloudRegion cloudRegion = mapCloudRegion(cloudSite, cloudOwner); + AAIResourceUri uri = AAIUriFactory.createResourceUri(AAIObjectType.CLOUD_REGION, + cloudRegion.getCloudOwner(), cloudRegion.getCloudRegionId()); + getAaiClient().createIfNotExists(uri, Optional.of(cloudRegion)); + } catch (Exception e) { + logger.error("Error creating cloud region in AAI", e); + throw new CloudException("Error creating cloud region in AAI: " + e.getMessage(), e); + } + } + + protected void createRegionInCatalogDb(CloudSite cloudSite) throws CloudException { + try { + CloudSite existingCloudSite = catalogDBClient.getCloudSite(cloudSite.getRegionId()); + if (existingCloudSite == null) { + catalogDBClient.postCloudSite(cloudSite); + } + } catch (Exception e) { + logger.error("Error creating cloud site in Catalog Adapter: " + e.getMessage(), e); + throw new CloudException("Error creating cloud site in Catalog Adapter", e); + } + } + + protected CloudRegion mapCloudRegion(CloudSite cloudSite, String cloudOwner) { + CloudRegion region = new CloudRegion(); + region.setCloudOwner(cloudOwner); + region.setCloudRegionId(cloudSite.getRegionId()); + region.setCloudRegionVersion(cloudSite.getCloudVersion()); + region.setOwnerDefinedType("cLCP"); + region.setOrchestrationDisabled(false); + region.setComplexName("NA"); + region.setInMaint(false); + region.setCloudType("openstack"); + return region; + } + + protected AAIResourcesClient getAaiClient() { + if (aaiClient == null) + return new AAIResourcesClient(); + else + return aaiClient; + } + +} diff --git a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/openstack/CXFConfiguration.java b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/openstack/CXFConfiguration.java index 9fc5c979d8..9badd795eb 100644 --- a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/openstack/CXFConfiguration.java +++ b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/openstack/CXFConfiguration.java @@ -32,6 +32,7 @@ import org.apache.cxf.jaxrs.JAXRSServerFactoryBean; import org.apache.cxf.jaxrs.swagger.Swagger2Feature; import org.apache.cxf.jaxws.EndpointImpl; import org.apache.cxf.transport.servlet.CXFServlet; +import org.onap.so.adapters.cloudregion.CloudRegionRestV1; import org.onap.so.adapters.network.MsoNetworkAdapterAsyncImpl; import org.onap.so.adapters.network.MsoNetworkAdapterImpl; import org.onap.so.adapters.network.NetworkAdapterRest; @@ -47,10 +48,12 @@ import org.onap.so.adapters.vnf.VolumeAdapterRestV2; import org.onap.so.client.policy.JettisonStyleMapperProvider; import org.onap.so.logging.cxf.interceptor.SOAPLoggingInInterceptor; import org.onap.so.logging.cxf.interceptor.SOAPLoggingOutInterceptor; +import org.onap.so.logging.jaxrs.filter.SOAuditLogContainerFilter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; @@ -81,7 +84,14 @@ public class CXFConfiguration { @Autowired private MsoVnfCloudifyAdapterImpl vnfCloudifyAdapterImpl; @Autowired + private CloudRegionRestV1 cloudRegionRestV1; + @Autowired private JettisonStyleMapperProvider jettisonStyleObjectMapper; + @Autowired + private ObjectMapper mapper; + @Autowired + private SOAuditLogContainerFilter soAuditLogContainerFilter; + @Bean(name = Bus.DEFAULT_BUS_ID) public SpringBus springBus() { @@ -170,6 +180,7 @@ public class CXFConfiguration { return endpoint; } + // Uses Jettson Style marshalling semantics @Bean public Server rsServer() { JAXRSServerFactoryBean endpoint = new JAXRSServerFactoryBean(); @@ -178,7 +189,20 @@ public class CXFConfiguration { vnfAdapterRestV2, volumeAdapterRest, volumeAdapterRestV2)); endpoint.setAddress("/rest"); endpoint.setFeatures(Arrays.asList(createSwaggerFeature(), new LoggingFeature())); - endpoint.setProvider(new JacksonJsonProvider(jettisonStyleObjectMapper.getMapper())); + endpoint.setProviders(Arrays.asList(new JacksonJsonProvider(jettisonStyleObjectMapper.getMapper()), + soAuditLogContainerFilter)); + return endpoint.create(); + } + + // Uses normal Jackson marshalling semantics + @Bean + public Server rsServerApi() { + JAXRSServerFactoryBean endpoint = new JAXRSServerFactoryBean(); + endpoint.setBus(springBus()); + endpoint.setServiceBeans(Arrays.<Object>asList(cloudRegionRestV1)); + endpoint.setAddress("/api"); + endpoint.setFeatures(Arrays.asList(new LoggingFeature())); + endpoint.setProviders(Arrays.asList(new JacksonJsonProvider(mapper), soAuditLogContainerFilter)); return endpoint.create(); } diff --git a/adapters/mso-openstack-adapters/src/test/java/org/onap/so/adapters/cloudregion/CloudRegionRestImplTest.java b/adapters/mso-openstack-adapters/src/test/java/org/onap/so/adapters/cloudregion/CloudRegionRestImplTest.java new file mode 100644 index 0000000000..9c62c286ac --- /dev/null +++ b/adapters/mso-openstack-adapters/src/test/java/org/onap/so/adapters/cloudregion/CloudRegionRestImplTest.java @@ -0,0 +1,96 @@ +package org.onap.so.adapters.cloudregion; + +import static com.shazam.shazamcrest.MatcherAssert.assertThat; +import static com.shazam.shazamcrest.matcher.Matchers.sameBeanAs; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import java.util.Optional; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; +import org.onap.aai.domain.yang.CloudRegion; +import org.onap.so.client.aai.AAIObjectType; +import org.onap.so.client.aai.AAIResourcesClient; +import org.onap.so.client.aai.entities.uri.AAIResourceUri; +import org.onap.so.client.aai.entities.uri.AAIUriFactory; +import org.onap.so.db.catalog.beans.CloudSite; +import org.onap.so.db.catalog.client.CatalogDbClient; + + +@RunWith(MockitoJUnitRunner.class) +public class CloudRegionRestImplTest { + + @Spy + @InjectMocks + private CloudRestImpl cloudRestImpl; + + @Mock + private CatalogDbClient catalogDbClientMock; + + @Mock + private AAIResourcesClient aaiResClientMock; + + private CloudSite cloudSite = new CloudSite(); + + private CloudRegion testCloudRegion = new CloudRegion(); + + @Before + public void setup() { + cloudSite.setCloudVersion("1.0"); + cloudSite.setRegionId("region1"); + Mockito.doReturn(aaiResClientMock).when(cloudRestImpl).getAaiClient(); + testCloudRegion.setCloudOwner("bob"); + testCloudRegion.setCloudRegionId("region1"); + testCloudRegion.setCloudRegionVersion("1.0"); + testCloudRegion.setInMaint(false); + testCloudRegion.setOrchestrationDisabled(false); + testCloudRegion.setComplexName("NA"); + testCloudRegion.setCloudRegionVersion("1.0"); + testCloudRegion.setOwnerDefinedType("cLCP"); + testCloudRegion.setCloudType("openstack"); + } + + @Test + public void mapCloudRegionTest() { + CloudRegion mappedRegion = cloudRestImpl.mapCloudRegion(cloudSite, "bob"); + assertThat(mappedRegion, sameBeanAs(testCloudRegion)); + } + + @Test + public void createCloudRegionTest() { + when(catalogDbClientMock.getCloudSite("region1")).thenReturn(null); + when(catalogDbClientMock.postCloudSite(cloudSite)).thenReturn(cloudSite); + AAIResourceUri uri = AAIUriFactory.createResourceUri(AAIObjectType.CLOUD_REGION, "bob", "region1"); + cloudRestImpl.createCloudRegion(cloudSite, "bob"); + ArgumentCaptor<AAIResourceUri> actualURI = ArgumentCaptor.forClass(AAIResourceUri.class); + ArgumentCaptor<Optional<Object>> actualCloudRegion = ArgumentCaptor.forClass(Optional.class); + verify(catalogDbClientMock, times(1)).getCloudSite("region1"); + verify(catalogDbClientMock, times(1)).postCloudSite(cloudSite); + verify(aaiResClientMock, times(1)).createIfNotExists(Mockito.eq(uri), Mockito.any()); + verify(aaiResClientMock, times(1)).createIfNotExists(actualURI.capture(), actualCloudRegion.capture()); + assertThat((CloudRegion) actualCloudRegion.getValue().get(), sameBeanAs(testCloudRegion)); + } + + @Test + public void updateCloudRegionTest() { + when(catalogDbClientMock.updateCloudSite(cloudSite)).thenReturn(cloudSite); + cloudRestImpl.updateCloudRegion(cloudSite, "bob"); + verify(catalogDbClientMock, times(1)).updateCloudSite(cloudSite); + } + + @Test + public void deleteCloudRegionTest() { + doNothing().when(catalogDbClientMock).deleteCloudSite("region1"); + cloudRestImpl.deleteCloudRegion(cloudSite.getRegionId()); + verify(catalogDbClientMock, times(1)).deleteCloudSite("region1"); + } + +} diff --git a/mso-catalog-db/src/main/java/org/onap/so/db/catalog/client/CatalogDbClient.java b/mso-catalog-db/src/main/java/org/onap/so/db/catalog/client/CatalogDbClient.java index 7f5907e9bf..161ca2a2fb 100644 --- a/mso-catalog-db/src/main/java/org/onap/so/db/catalog/client/CatalogDbClient.java +++ b/mso-catalog-db/src/main/java/org/onap/so/db/catalog/client/CatalogDbClient.java @@ -764,8 +764,61 @@ public class CatalogDbClient { return this.getSingleResource(cloudSiteClient, getUri(uri + id)); } - public void postCloudSite(CloudSite cloudSite) { - this.postSingleResource(cloudSiteClient, cloudSite); + public CloudSite postCloudSite(CloudSite cloudSite) { + if (cloudSite == null) { + throw new EntityNotFoundException("CloudSite passed as null"); + } + try { + HttpHeaders headers = getHttpHeaders(); + HttpEntity<CloudSite> entity = new HttpEntity<>(cloudSite, headers); + CloudSite updatedCloudSite = restTemplate + .exchange(UriComponentsBuilder.fromUriString(endpoint + "/cloudSite").build().encode().toString(), + HttpMethod.POST, entity, CloudSite.class) + .getBody(); + return updatedCloudSite; + } catch (HttpClientErrorException e) { + if (HttpStatus.SC_NOT_FOUND == e.getStatusCode().value()) { + throw new EntityNotFoundException("Unable to find CloudSite with Cloud Site Id: " + cloudSite.getId()); + } + throw e; + } + } + + public CloudSite updateCloudSite(CloudSite cloudSite) { + if (cloudSite == null) { + throw new EntityNotFoundException("CloudSite passed as null"); + } + try { + HttpHeaders headers = getHttpHeaders(); + HttpEntity<CloudSite> entity = new HttpEntity<>(cloudSite, headers); + CloudSite updatedCloudSite = restTemplate + .exchange(UriComponentsBuilder.fromUriString(endpoint + "/cloudSite/" + cloudSite.getId()).build() + .encode().toString(), HttpMethod.PUT, entity, CloudSite.class) + .getBody(); + return updatedCloudSite; + } catch (HttpClientErrorException e) { + if (HttpStatus.SC_NOT_FOUND == e.getStatusCode().value()) { + throw new EntityNotFoundException("Unable to find CloudSite with Cloud Site Id: " + cloudSite.getId()); + } + throw e; + } + } + + public void deleteCloudSite(String cloudSiteId) { + if (cloudSiteId == null) { + throw new EntityNotFoundException("CloudSiteId passed as null"); + } + try { + HttpHeaders headers = getHttpHeaders(); + HttpEntity<String> entity = new HttpEntity<>(null, headers); + restTemplate.exchange(UriComponentsBuilder.fromUriString(endpoint + "/cloudSite/" + cloudSiteId).build() + .encode().toString(), HttpMethod.DELETE, entity, CloudSite.class).getBody(); + } catch (HttpClientErrorException e) { + if (HttpStatus.SC_NOT_FOUND == e.getStatusCode().value()) { + throw new EntityNotFoundException("Unable to find CloudSite with Cloud Site Id: " + cloudSiteId); + } + throw e; + } } public List<CloudSite> getCloudSites() { |