From b35670742c728d7d85da2f9c856c51a5889449d6 Mon Sep 17 00:00:00 2001 From: Ofir Sonsino Date: Mon, 4 Sep 2017 14:52:07 +0300 Subject: [PATCH] [VID-55] Upgrade Tosca Parser (merge) Change-Id: I5389f1641ef22df3ed569a154b3c367117c04d04 Signed-off-by: Ofir Sonsino --- .../src/main/resources/portal_template.properties | 2 +- vid-app-common/pom.xml | 16 + .../java/org/openecomp/aai/util/AAIProperties.java | 12 +- .../org/openecomp/aai/util/AAIRestInterface.java | 47 +- .../aai/util/CustomJacksonJaxBJsonProvider.java | 12 +- .../org/openecomp/aai/util/HttpsAuthClient.java | 12 +- .../JettyObfuscationConversionCommandLineUtil.java | 12 +- .../openecomp/vid/asdc/AsdcCatalogException.java | 12 +- .../java/org/openecomp/vid/asdc/AsdcClient.java | 7 +- .../org/openecomp/vid/asdc/beans/Artifact.java | 12 +- .../org/openecomp/vid/asdc/beans/Resource.java | 12 +- .../java/org/openecomp/vid/asdc/beans/Service.java | 12 +- .../org/openecomp/vid/asdc/beans/SubResource.java | 12 +- .../openecomp/vid/asdc/beans/tosca/Capability.java | 12 +- .../openecomp/vid/asdc/beans/tosca/Constraint.java | 12 +- .../org/openecomp/vid/asdc/beans/tosca/Group.java | 12 +- .../org/openecomp/vid/asdc/beans/tosca/Import.java | 12 +- .../org/openecomp/vid/asdc/beans/tosca/Input.java | 22 +- .../vid/asdc/beans/tosca/NodeTemplate.java | 12 +- .../openecomp/vid/asdc/beans/tosca/Property.java | 12 +- .../vid/asdc/beans/tosca/Requirement.java | 12 +- .../org/openecomp/vid/asdc/beans/tosca/Schema.java | 12 +- .../vid/asdc/beans/tosca/SubstitutionMappings.java | 12 +- .../vid/asdc/beans/tosca/TopologyTemplate.java | 12 +- .../openecomp/vid/asdc/beans/tosca/ToscaCsar.java | 12 +- .../vid/asdc/beans/tosca/ToscaMetadata.java | 30 - .../openecomp/vid/asdc/beans/tosca/ToscaModel.java | 12 +- .../openecomp/vid/asdc/local/LocalAsdcClient.java | 388 ++++++++++ .../vid/asdc/memory/InMemoryAsdcClient.java | 12 +- .../org/openecomp/vid/asdc/parser/ToscaParser.java | 15 + .../openecomp/vid/asdc/parser/ToscaParserImpl.java | 225 ++++++ .../vid/asdc/parser/ToscaParserImpl2.java | 226 ++++++ .../openecomp/vid/asdc/rest/RestfulAsdcClient.java | 854 ++++++++++----------- .../openecomp/vid/controller/AaiController.java | 1 - .../openecomp/vid/controller/VidController.java | 285 +------ .../org/openecomp/vid/controller/WebConfig.java | 106 +++ .../java/org/openecomp/vid/model/ModelUtil.java | 61 -- .../main/java/org/openecomp/vid/model/Network.java | 2 +- .../java/org/openecomp/vid/model/NewNetwork.java | 56 ++ .../main/java/org/openecomp/vid/model/NewNode.java | 209 +++++ .../java/org/openecomp/vid/model/NewService.java | 252 ++++++ .../org/openecomp/vid/model/NewServiceModel.java | 259 +++++++ .../main/java/org/openecomp/vid/model/NewVNF.java | 123 +++ .../java/org/openecomp/vid/model/ServiceModel.java | 17 +- .../src/main/java/org/openecomp/vid/model/VNF.java | 2 +- .../java/org/openecomp/vid/model/VfModule.java | 4 +- .../java/org/openecomp/vid/model/VolumeGroup.java | 2 +- .../org/openecomp/vid/mso/MsoRestInterfaceIfc.java | 2 +- .../vid/properties/AsdcClientConfiguration.java | 34 +- .../openecomp/vid/properties/VidProperties.java | 21 +- .../org/openecomp/vid/services/VidService.java | 17 + .../org/openecomp/vid/services/VidServiceImpl.java | 79 ++ vid-app-common/src/main/resources/catalog.json | 174 +++++ vid-app-common/src/main/resources/sdcservices.json | 29 + .../src/main/resources/service-vf-csar.zip | Bin 0 -> 58693 bytes .../src/main/resources/service-vl-csar.zip | Bin 0 -> 34490 bytes .../vid/scripts/constants/componentConstants.js | 11 +- .../app/vid/scripts/constants/fieldConstants.js | 1 + .../scripts/controller/aaiSubscriberController.js | 2 +- .../app/vid/scripts/controller/subscriberSearch.js | 137 ++++ .../opencomp/vid/controller/VidControllerTest.java | 128 +++ .../java/org/opencomp/vid/testUtils/TestUtils.java | 65 ++ .../ecomp/vid/selenium/LogOutLeftPane.java | 2 +- .../core/MockApplicationContextTestSuite.java | 3 +- .../fusionapp/service/ProfileServiceTest.java | 16 +- vid-app-common/src/test/resources/asdc.properties | 6 + vid-app-common/src/test/resources/sampleTosca.csar | Bin 0 -> 6177 bytes vid-app-common/src/test/resources/vf-csar.JSON | 153 ++++ vid-app-common/src/test/resources/vl-csar.JSON | 109 +++ 69 files changed, 3439 insertions(+), 1025 deletions(-) create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/asdc/local/LocalAsdcClient.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/asdc/parser/ToscaParser.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/asdc/parser/ToscaParserImpl.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/asdc/parser/ToscaParserImpl2.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/controller/WebConfig.java delete mode 100755 vid-app-common/src/main/java/org/openecomp/vid/model/ModelUtil.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/model/NewNetwork.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/model/NewNode.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/model/NewService.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/model/NewServiceModel.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/model/NewVNF.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/services/VidService.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/services/VidServiceImpl.java create mode 100644 vid-app-common/src/main/resources/catalog.json create mode 100644 vid-app-common/src/main/resources/sdcservices.json create mode 100644 vid-app-common/src/main/resources/service-vf-csar.zip create mode 100644 vid-app-common/src/main/resources/service-vl-csar.zip create mode 100644 vid-app-common/src/test/java/org/opencomp/vid/controller/VidControllerTest.java create mode 100644 vid-app-common/src/test/java/org/opencomp/vid/testUtils/TestUtils.java create mode 100644 vid-app-common/src/test/resources/asdc.properties create mode 100644 vid-app-common/src/test/resources/sampleTosca.csar create mode 100644 vid-app-common/src/test/resources/vf-csar.JSON create mode 100644 vid-app-common/src/test/resources/vl-csar.JSON diff --git a/epsdk-app-onap/src/main/resources/portal_template.properties b/epsdk-app-onap/src/main/resources/portal_template.properties index ed6940148..c26be5547 100755 --- a/epsdk-app-onap/src/main/resources/portal_template.properties +++ b/epsdk-app-onap/src/main/resources/portal_template.properties @@ -1,6 +1,6 @@ # Properties read by ECOMP Framework library, ecompFW.jar -portal.api.impl.class = org.openecomp.portalapp.service.OnBoardingApiServiceImpl +portal.api.impl.class = org.openecomp.portalsdk.core.onboarding.client.OnBoardingApiServiceImpl portal.api.prefix = /api max.idle.time = 5 user.attribute.name = user_attribute diff --git a/vid-app-common/pom.xml b/vid-app-common/pom.xml index fbe74df58..fbc9d47f9 100755 --- a/vid-app-common/pom.xml +++ b/vid-app-common/pom.xml @@ -453,5 +453,21 @@ snakeyaml 1.16 + + org.skyscreamer + jsonassert + 1.5.0 + + + org.openecomp.sdc.sdc-tosca + sdc-tosca + 1.1.32-SNAPSHOT + + + net.javacrumbs.json-unit + json-unit + 1.23.0 + test + diff --git a/vid-app-common/src/main/java/org/openecomp/aai/util/AAIProperties.java b/vid-app-common/src/main/java/org/openecomp/aai/util/AAIProperties.java index ca710f6fc..9716ba827 100755 --- a/vid-app-common/src/main/java/org/openecomp/aai/util/AAIProperties.java +++ b/vid-app-common/src/main/java/org/openecomp/aai/util/AAIProperties.java @@ -1,9 +1,9 @@ /*- - * ============LICENSE_START======================================================= - * VID - * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * ============LICENSE_START======================================================= + * VID + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. 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 @@ -14,7 +14,7 @@ * 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. + * limitations under the License. * ============LICENSE_END========================================================= */ diff --git a/vid-app-common/src/main/java/org/openecomp/aai/util/AAIRestInterface.java b/vid-app-common/src/main/java/org/openecomp/aai/util/AAIRestInterface.java index cd5095191..243410050 100755 --- a/vid-app-common/src/main/java/org/openecomp/aai/util/AAIRestInterface.java +++ b/vid-app-common/src/main/java/org/openecomp/aai/util/AAIRestInterface.java @@ -276,43 +276,22 @@ public class AAIRestInterface { if (xml) responseType = "application/xml"; - initRestClient(); - - String clientCert = SystemProperties.getProperty(AAIProperties.AAI_USE_CLIENT_CERT); - - boolean useClientCert = false; - if (clientCert != null && - SystemProperties.getProperty(AAIProperties.AAI_USE_CLIENT_CERT).equalsIgnoreCase("true")) { - useClientCert = true; - } + initRestClient(); url = SystemProperties.getProperty(AAIProperties.AAI_SERVER_URL_BASE) + path; - final Response cres; - if (useClientCert == true) { - cres = client.target(url) - .request() - .accept(responseType) - .header("X-TransactionId", transId) - .header("X-FromAppId", fromAppId) - .header("Content-Type", "application/json") - .post(Entity.entity(payload, MediaType.APPLICATION_JSON)); - } else { - - String vidUsername = SystemProperties.getProperty(AAIProperties.AAI_VID_USERNAME); - String vidPassword = Password.deobfuscate(SystemProperties.getProperty(AAIProperties.AAI_VID_PASSWD_X)); - String encodeThis = vidUsername + ":" + vidPassword; - - cres = client.target(url) - .request() - .accept(responseType) - .header("X-TransactionId", transId) - .header("X-FromAppId", fromAppId) - .header("Content-Type", "application/json") - .header("Authorization", "Basic " + Base64.getEncoder().encodeToString(encodeThis.getBytes("utf-8"))) - .post(Entity.entity(payload, MediaType.APPLICATION_JSON)); - } - + String vidUsername = SystemProperties.getProperty(AAIProperties.AAI_VID_USERNAME); + String vidPassword = Password.deobfuscate(SystemProperties.getProperty(AAIProperties.AAI_VID_PASSWD_X)); + String encodeThis = vidUsername + ":" + vidPassword; + + final Response cres = client.target(url) + .request() + .accept(responseType) + .header("X-TransactionId", transId) + .header("X-FromAppId", fromAppId) + .header("Authorization", "Basic " + Base64.getEncoder().encodeToString(encodeThis.getBytes("utf-8"))) + .post(Entity.entity(payload, MediaType.APPLICATION_JSON)); + if (cres.getStatus() == 200 && cres.getStatus() <= 299) { logger.info(EELFLoggerDelegate.errorLogger, dateFormat.format(new Date()) + "<== " + methodName + " REST api POST was successful!"); logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " REST api POST was successful!"); diff --git a/vid-app-common/src/main/java/org/openecomp/aai/util/CustomJacksonJaxBJsonProvider.java b/vid-app-common/src/main/java/org/openecomp/aai/util/CustomJacksonJaxBJsonProvider.java index 2623d6b6f..1ed079219 100755 --- a/vid-app-common/src/main/java/org/openecomp/aai/util/CustomJacksonJaxBJsonProvider.java +++ b/vid-app-common/src/main/java/org/openecomp/aai/util/CustomJacksonJaxBJsonProvider.java @@ -1,9 +1,9 @@ /*- - * ============LICENSE_START======================================================= - * VID - * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * ============LICENSE_START======================================================= + * VID + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. 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 @@ -14,7 +14,7 @@ * 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. + * limitations under the License. * ============LICENSE_END========================================================= */ diff --git a/vid-app-common/src/main/java/org/openecomp/aai/util/HttpsAuthClient.java b/vid-app-common/src/main/java/org/openecomp/aai/util/HttpsAuthClient.java index 8335d15b2..6a3665edb 100755 --- a/vid-app-common/src/main/java/org/openecomp/aai/util/HttpsAuthClient.java +++ b/vid-app-common/src/main/java/org/openecomp/aai/util/HttpsAuthClient.java @@ -1,9 +1,9 @@ /*- - * ============LICENSE_START======================================================= - * VID - * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * ============LICENSE_START======================================================= + * VID + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. 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 @@ -14,7 +14,7 @@ * 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. + * limitations under the License. * ============LICENSE_END========================================================= */ diff --git a/vid-app-common/src/main/java/org/openecomp/aai/util/JettyObfuscationConversionCommandLineUtil.java b/vid-app-common/src/main/java/org/openecomp/aai/util/JettyObfuscationConversionCommandLineUtil.java index e4c4bcef1..e31b0c1c7 100755 --- a/vid-app-common/src/main/java/org/openecomp/aai/util/JettyObfuscationConversionCommandLineUtil.java +++ b/vid-app-common/src/main/java/org/openecomp/aai/util/JettyObfuscationConversionCommandLineUtil.java @@ -1,9 +1,9 @@ /*- - * ============LICENSE_START======================================================= - * VID - * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * ============LICENSE_START======================================================= + * VID + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. 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 @@ -14,7 +14,7 @@ * 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. + * limitations under the License. * ============LICENSE_END========================================================= */ diff --git a/vid-app-common/src/main/java/org/openecomp/vid/asdc/AsdcCatalogException.java b/vid-app-common/src/main/java/org/openecomp/vid/asdc/AsdcCatalogException.java index aafb11cad..c80787205 100755 --- a/vid-app-common/src/main/java/org/openecomp/vid/asdc/AsdcCatalogException.java +++ b/vid-app-common/src/main/java/org/openecomp/vid/asdc/AsdcCatalogException.java @@ -1,9 +1,9 @@ /*- - * ============LICENSE_START======================================================= - * VID - * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * ============LICENSE_START======================================================= + * VID + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. 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 @@ -14,7 +14,7 @@ * 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. + * limitations under the License. * ============LICENSE_END========================================================= */ diff --git a/vid-app-common/src/main/java/org/openecomp/vid/asdc/AsdcClient.java b/vid-app-common/src/main/java/org/openecomp/vid/asdc/AsdcClient.java index c4a8b1c0c..314ea037d 100755 --- a/vid-app-common/src/main/java/org/openecomp/vid/asdc/AsdcClient.java +++ b/vid-app-common/src/main/java/org/openecomp/vid/asdc/AsdcClient.java @@ -20,6 +20,8 @@ package org.openecomp.vid.asdc; +import java.io.IOException; +import java.nio.file.Path; import java.util.Collection; import java.util.Map; import java.util.UUID; @@ -27,7 +29,6 @@ import java.util.UUID; import org.openecomp.vid.asdc.beans.Artifact; import org.openecomp.vid.asdc.beans.Resource; import org.openecomp.vid.asdc.beans.Service; -import org.openecomp.vid.asdc.beans.tosca.ToscaCsar; /** * The Interface AsdcClient. @@ -77,7 +78,7 @@ public interface AsdcClient { * @return the resource tosca model * @throws AsdcCatalogException the sdc catalog exception */ - public ToscaCsar getResourceToscaModel(UUID uuid) throws AsdcCatalogException; + public Path getResourceToscaModel(UUID uuid) throws AsdcCatalogException; /** * Gets the service. @@ -122,7 +123,7 @@ public interface AsdcClient { * @return the service tosca model * @throws AsdcCatalogException the asdc catalog exception */ - public ToscaCsar getServiceToscaModel(UUID uuid) throws AsdcCatalogException; + public Path getServiceToscaModel(UUID uuid) throws AsdcCatalogException; //TODO: Collect TOSCA information from CSAR } diff --git a/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/Artifact.java b/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/Artifact.java index d24d1232a..4974d44a8 100755 --- a/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/Artifact.java +++ b/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/Artifact.java @@ -1,9 +1,9 @@ /*- - * ============LICENSE_START======================================================= - * VID - * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * ============LICENSE_START======================================================= + * VID + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. 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 @@ -14,7 +14,7 @@ * 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. + * limitations under the License. * ============LICENSE_END========================================================= */ diff --git a/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/Resource.java b/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/Resource.java index 3911c699a..000aae1a9 100755 --- a/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/Resource.java +++ b/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/Resource.java @@ -1,9 +1,9 @@ /*- - * ============LICENSE_START======================================================= - * VID - * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * ============LICENSE_START======================================================= + * VID + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. 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 @@ -14,7 +14,7 @@ * 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. + * limitations under the License. * ============LICENSE_END========================================================= */ diff --git a/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/Service.java b/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/Service.java index fa898e56a..2a83cbe3a 100755 --- a/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/Service.java +++ b/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/Service.java @@ -1,9 +1,9 @@ /*- - * ============LICENSE_START======================================================= - * VID - * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * ============LICENSE_START======================================================= + * VID + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. 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 @@ -14,7 +14,7 @@ * 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. + * limitations under the License. * ============LICENSE_END========================================================= */ diff --git a/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/SubResource.java b/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/SubResource.java index 6d7fb41b1..cd3ec7fee 100755 --- a/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/SubResource.java +++ b/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/SubResource.java @@ -1,9 +1,9 @@ /*- - * ============LICENSE_START======================================================= - * VID - * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * ============LICENSE_START======================================================= + * VID + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. 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 @@ -14,7 +14,7 @@ * 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. + * limitations under the License. * ============LICENSE_END========================================================= */ diff --git a/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/Capability.java b/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/Capability.java index 21a50d3bb..0d3f17ae4 100755 --- a/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/Capability.java +++ b/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/Capability.java @@ -1,9 +1,9 @@ /*- - * ============LICENSE_START======================================================= - * VID - * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * ============LICENSE_START======================================================= + * VID + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. 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 @@ -14,7 +14,7 @@ * 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. + * limitations under the License. * ============LICENSE_END========================================================= */ diff --git a/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/Constraint.java b/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/Constraint.java index b68b51a09..a91f38312 100755 --- a/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/Constraint.java +++ b/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/Constraint.java @@ -1,9 +1,9 @@ /*- - * ============LICENSE_START======================================================= - * VID - * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * ============LICENSE_START======================================================= + * VID + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. 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 @@ -14,7 +14,7 @@ * 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. + * limitations under the License. * ============LICENSE_END========================================================= */ package org.openecomp.vid.asdc.beans.tosca; diff --git a/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/Group.java b/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/Group.java index 275db3c0d..4d31faab1 100755 --- a/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/Group.java +++ b/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/Group.java @@ -1,9 +1,9 @@ /*- - * ============LICENSE_START======================================================= - * VID - * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * ============LICENSE_START======================================================= + * VID + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. 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 @@ -14,7 +14,7 @@ * 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. + * limitations under the License. * ============LICENSE_END========================================================= */ diff --git a/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/Import.java b/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/Import.java index 33f317579..d64a58066 100755 --- a/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/Import.java +++ b/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/Import.java @@ -1,9 +1,9 @@ /*- - * ============LICENSE_START======================================================= - * VID - * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * ============LICENSE_START======================================================= + * VID + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. 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 @@ -14,7 +14,7 @@ * 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. + * limitations under the License. * ============LICENSE_END========================================================= */ diff --git a/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/Input.java b/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/Input.java index f98820716..e5eec0121 100755 --- a/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/Input.java +++ b/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/Input.java @@ -1,9 +1,9 @@ /*- - * ============LICENSE_START======================================================= - * VID - * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * ============LICENSE_START======================================================= + * VID + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. 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 @@ -14,12 +14,14 @@ * 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. + * limitations under the License. * ============LICENSE_END========================================================= */ package org.openecomp.vid.asdc.beans.tosca; +import org.openecomp.sdc.toscaparser.api.elements.constraints.*; + import java.util.List; import java.util.ArrayList; @@ -42,7 +44,7 @@ public class Input { private Input entry_schema; /** The constraints */ - private List constraints; + private List constraints; /** The required field. If not set, the default is true */ private boolean required = true; @@ -51,7 +53,7 @@ public class Input { * Instantiates a new input. */ public Input() { - constraints = new ArrayList(); + constraints = new ArrayList(); } /** @@ -144,7 +146,7 @@ public class Input { * * @param c the new constraints */ - public void setConstraints(List c) { + public void setConstraints(List c) { this.constraints = c; } /** @@ -152,7 +154,7 @@ public class Input { * * @return the constraints */ - public List getConstraints() { + public List getConstraints() { return constraints; } diff --git a/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/NodeTemplate.java b/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/NodeTemplate.java index 73eead93f..97740b3f5 100755 --- a/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/NodeTemplate.java +++ b/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/NodeTemplate.java @@ -1,9 +1,9 @@ /*- - * ============LICENSE_START======================================================= - * VID - * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * ============LICENSE_START======================================================= + * VID + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. 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 @@ -14,7 +14,7 @@ * 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. + * limitations under the License. * ============LICENSE_END========================================================= */ diff --git a/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/Property.java b/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/Property.java index c8f048ced..16b921e2a 100755 --- a/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/Property.java +++ b/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/Property.java @@ -1,9 +1,9 @@ /*- - * ============LICENSE_START======================================================= - * VID - * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * ============LICENSE_START======================================================= + * VID + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. 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 @@ -14,7 +14,7 @@ * 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. + * limitations under the License. * ============LICENSE_END========================================================= */ diff --git a/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/Requirement.java b/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/Requirement.java index fce41ec6e..acb250997 100755 --- a/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/Requirement.java +++ b/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/Requirement.java @@ -1,9 +1,9 @@ /*- - * ============LICENSE_START======================================================= - * VID - * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * ============LICENSE_START======================================================= + * VID + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. 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 @@ -14,7 +14,7 @@ * 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. + * limitations under the License. * ============LICENSE_END========================================================= */ diff --git a/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/Schema.java b/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/Schema.java index bd3a2f0a5..f77c1b4b5 100755 --- a/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/Schema.java +++ b/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/Schema.java @@ -1,9 +1,9 @@ /*- - * ============LICENSE_START======================================================= - * VID - * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * ============LICENSE_START======================================================= + * VID + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. 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 @@ -14,7 +14,7 @@ * 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. + * limitations under the License. * ============LICENSE_END========================================================= */ diff --git a/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/SubstitutionMappings.java b/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/SubstitutionMappings.java index 026f29bbe..f59f24674 100755 --- a/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/SubstitutionMappings.java +++ b/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/SubstitutionMappings.java @@ -1,9 +1,9 @@ /*- - * ============LICENSE_START======================================================= - * VID - * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * ============LICENSE_START======================================================= + * VID + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. 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 @@ -14,7 +14,7 @@ * 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. + * limitations under the License. * ============LICENSE_END========================================================= */ diff --git a/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/TopologyTemplate.java b/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/TopologyTemplate.java index ce7ce5755..25b2c2c38 100755 --- a/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/TopologyTemplate.java +++ b/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/TopologyTemplate.java @@ -1,9 +1,9 @@ /*- - * ============LICENSE_START======================================================= - * VID - * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * ============LICENSE_START======================================================= + * VID + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. 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 @@ -14,7 +14,7 @@ * 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. + * limitations under the License. * ============LICENSE_END========================================================= */ diff --git a/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/ToscaCsar.java b/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/ToscaCsar.java index cca4ae7c2..9e9e1d00b 100755 --- a/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/ToscaCsar.java +++ b/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/ToscaCsar.java @@ -1,9 +1,9 @@ /*- - * ============LICENSE_START======================================================= - * VID - * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * ============LICENSE_START======================================================= + * VID + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. 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 @@ -14,7 +14,7 @@ * 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. + * limitations under the License. * ============LICENSE_END========================================================= */ diff --git a/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/ToscaMetadata.java b/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/ToscaMetadata.java index 41c7ca5ba..d42c1f150 100755 --- a/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/ToscaMetadata.java +++ b/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/ToscaMetadata.java @@ -61,9 +61,6 @@ public class ToscaMetadata { /** The resource vendor release. */ private String resourceVendorRelease; - /** the resourceVendorModelNumber */ - private String resourceVendorModelNumber; - /** The service ecomp naming. */ private String serviceEcompNaming; @@ -91,11 +88,6 @@ public class ToscaMetadata { /** The vf module model version. */ private String vfModuleModelVersion; - - /** serviceType */ - private String serviceType; - /** serviceRole */ - private String serviceRole; /** * Instantiates a new tosca metadata. @@ -466,26 +458,4 @@ public class ToscaMetadata { return vfModuleModelCustomizationUUID; } - - /** serviceType */ - public String getServiceType() { - return serviceType; - } - public void setServiceType(String serviceType) { - this.serviceType= serviceType; - } - /** serviceRole */ - public String getServiceRole() { - return serviceRole; - } - public void setServiceRole(String serviceRole) { - this.serviceRole= serviceRole; - } - /** resourceVendorModelNumber */ - public String getResourceVendorModelNumber() { - return resourceVendorModelNumber; - } - public void setResourceVendorModelNumber(String resourceVendorModelNumber) { - this.resourceVendorModelNumber= resourceVendorModelNumber; - } } diff --git a/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/ToscaModel.java b/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/ToscaModel.java index c9e42f291..77fbe9135 100755 --- a/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/ToscaModel.java +++ b/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/ToscaModel.java @@ -1,9 +1,9 @@ /*- - * ============LICENSE_START======================================================= - * VID - * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * ============LICENSE_START======================================================= + * VID + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. 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 @@ -14,7 +14,7 @@ * 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. + * limitations under the License. * ============LICENSE_END========================================================= */ diff --git a/vid-app-common/src/main/java/org/openecomp/vid/asdc/local/LocalAsdcClient.java b/vid-app-common/src/main/java/org/openecomp/vid/asdc/local/LocalAsdcClient.java new file mode 100644 index 000000000..8538f6ba6 --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/asdc/local/LocalAsdcClient.java @@ -0,0 +1,388 @@ +package org.openecomp.vid.asdc.local; + +import org.codehaus.jackson.JsonParseException; +import org.codehaus.jackson.map.JsonMappingException; +import org.codehaus.jackson.map.ObjectMapper; +import org.json.JSONArray; +import org.json.JSONObject; +import org.openecomp.vid.asdc.AsdcCatalogException; +import org.openecomp.vid.asdc.AsdcClient; +import org.openecomp.vid.asdc.beans.Artifact; +import org.openecomp.vid.asdc.beans.Resource; +import org.openecomp.vid.asdc.beans.Service; +import org.openecomp.vid.asdc.beans.tosca.ToscaCsar; +import org.openecomp.vid.asdc.beans.tosca.ToscaMeta; +import org.openecomp.vid.asdc.beans.tosca.ToscaModel; +import org.yaml.snakeyaml.Yaml; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.*; +import java.util.zip.ZipFile; + +/** + * The Class LocalAsdcClient. + */ +public class LocalAsdcClient implements AsdcClient { + + + /** + * The catalog. + */ + private final JSONObject catalog; + + /** + * The mapper. + */ + private final ObjectMapper mapper; + + /** + * The Class Builder. + */ + public static class Builder { + + /** + * The catalog. + */ + private JSONObject catalog = new JSONObject() + .put("resources", new JSONObject()) + .put("services", new JSONObject()); + + /** + * The mapper. + */ + private ObjectMapper mapper = new ObjectMapper(); + + /** + * Instantiates a new builder. + */ + public Builder() { + } + + /** + * Catalog. + * + * @param catalog the catalog + * @return the builder + */ + public org.openecomp.vid.asdc.local.LocalAsdcClient.Builder catalog(JSONObject catalog) { + this.catalog = catalog; + return this; + } + + /** + * Mapper. + * + * @param mapper the mapper + * @return the builder + */ + public org.openecomp.vid.asdc.local.LocalAsdcClient.Builder mapper(ObjectMapper mapper) { + this.mapper = mapper; + return this; + } + + /** + * Builds the. + * + * @return the in local sdc client + */ + public org.openecomp.vid.asdc.local.LocalAsdcClient build() { + return new org.openecomp.vid.asdc.local.LocalAsdcClient(this); + } + } + + /** + * Instantiates a new in local sdc client. + * + * @param builder the builder + */ + private LocalAsdcClient(org.openecomp.vid.asdc.local.LocalAsdcClient.Builder builder) { + catalog = builder.catalog; + mapper = builder.mapper; + } + + /** + * Gets the catalog. + * + * @return the catalog + */ + private JSONObject getCatalog() { + return catalog; + } + + /** + * Gets the mapper. + * + * @return the mapper + */ + private ObjectMapper getMapper() { + return mapper; + } + + /** + * Convert. + * + * @param the generic type + * @param json the json + * @param clazz the clazz + * @return the t + * @throws AsdcCatalogException the sdc catalog exception + */ + private T convert(JSONObject json, Class clazz) throws AsdcCatalogException { + try { + return getMapper().readValue(json.toString(), clazz); + } catch (JsonParseException e) { + throw new AsdcCatalogException("Failed to parse SDC response (bad data)", e); + } catch (JsonMappingException e) { + throw new AsdcCatalogException("Failed to map SDC response to internal VID data structure(s)", e); + } catch (IOException e) { + throw new AsdcCatalogException("Failed to get a response from SDC", e); + } + } + + /* (non-Javadoc) + * @see org.openecomp.vid.asdc.AsdcClient#getResource(java.util.UUID) + */ + public Resource getResource(UUID uuid) throws AsdcCatalogException { + final JSONObject resource = getCatalog().getJSONObject("resources") + .getJSONObject(uuid.toString()); + return convert(resource, Resource.class); + } + + /* (non-Javadoc) + * @see org.openecomp.vid.asdc.AsdcClient#getResources() + */ + public Collection getResources() throws AsdcCatalogException { + final Collection resources = new LinkedList(); + + for (String key : getCatalog().getJSONObject("resources").keySet()) { + final JSONObject json = getCatalog().getJSONObject("resources").getJSONObject(key); + final Resource resource = convert(json, Resource.class); + resources.add(resource); + } + + return resources; + } + + /* (non-Javadoc) + * @see org.openecomp.vid.asdc.AsdcClient#getResources(java.util.Map) + */ + public Collection getResources(Map filter) throws AsdcCatalogException { + final Collection resources = new LinkedList(); + + for (String key : getCatalog().getJSONObject("resources").keySet()) { + final JSONObject json = getCatalog().getJSONObject("resources").getJSONObject(key); + + boolean filterMatch = true; + + for (Map.Entry entry : filter.entrySet()) { + for (int i = 0; i < entry.getValue().length; i++) { + if (!json.getString(entry.getKey()).equals(entry.getValue()[i])) { + filterMatch = false; + break; + } + } + } + + if (filterMatch) resources.add(convert(json, Resource.class)); + } + + return resources; + } + + /* (non-Javadoc) + * @see org.openecomp.vid.asdc.AsdcClient#getService(java.util.UUID) + */ + public Service getService(UUID uuid) throws AsdcCatalogException { + + JSONObject serviceJsonObject = null; + final JSONArray categoryJsonArray = getCatalog().getJSONArray("services"); + + for (int i = 0; i < categoryJsonArray.length() ; i++) { + JSONObject jsonServiceObject = categoryJsonArray.getJSONObject(i); + if (jsonServiceObject.get("uuid").equals(uuid.toString())) { + serviceJsonObject = jsonServiceObject; + break; + } + } + + if (serviceJsonObject != null) + return convert(serviceJsonObject, Service.class); + else return null; + } + + /* (non-Javadoc) + * @see org.openecomp.vid.asdc.AsdcClient#getServices() + */ + public Collection getServices() throws AsdcCatalogException { + final Collection services = new LinkedList(); + + JSONArray servicesArr = getCatalog().getJSONArray("services"); + + for (Object objService : servicesArr) { + JSONObject jsonServiceItem = (JSONObject) objService; + final Service service = convert(jsonServiceItem, Service.class); + services.add(service); + } + + return services; + } + + /* (non-Javadoc) + * @see org.openecompt.vid.asdc.AsdcClient#getServices(java.util.Map) + */ + public Collection getServices(Map filter) throws AsdcCatalogException { + final Collection services = new LinkedList(); + + JSONArray catalogServices = catalog.getJSONArray("services"); + + for (int i = 0; i < catalogServices.length(); i++) { + + JSONObject serviceJson = catalogServices.getJSONObject(i); + + boolean filterMatch = true; + + for (Map.Entry entry : filter.entrySet()) { + for (int j = 0; j < entry.getValue().length; j++) { + if (!serviceJson.getString(entry.getKey()).equals(entry.getValue()[j])) { + filterMatch = false; + break; + } + } + } + if (filterMatch) services.add(convert(serviceJson, Service.class)); + } + return services; + } + + /* (non-Javadoc) + * @see org.openecomp.vid.asdc.AsdcClient#getResourceArtifact(java.util.UUID, java.util.UUID) + */ + public Artifact getResourceArtifact(UUID resourceUuid, UUID artifactUuid) throws AsdcCatalogException { + final JSONArray artifacts = getCatalog().getJSONObject("resources") + .getJSONObject(resourceUuid.toString()) + .getJSONArray("artifacts"); + + for (int i = 0; i < artifacts.length(); i++) { + final JSONObject artifact = artifacts.getJSONObject(i); + + if (artifact.getString("artifactUUID").equals(artifactUuid.toString())) { + return convert(artifact, Artifact.class); + } + } + + return null; + } + + /* (non-Javadoc) + * @see org.openecomp.vid.asdc.AsdcClient#getServiceArtifact(java.util.UUID, java.util.UUID) + */ + public Artifact getServiceArtifact(UUID serviceUuid, UUID artifactUuid) throws AsdcCatalogException { + final JSONArray artifacts = getCatalog().getJSONObject("services") + .getJSONObject(serviceUuid.toString()) + .getJSONArray("artifacts"); + + for (int i = 0; i < artifacts.length(); i++) { + final JSONObject artifact = artifacts.getJSONObject(i); + + if (artifact.getString("artifactUUID").equals(artifactUuid.toString())) { + return convert(artifact, Artifact.class); + } + } + + return null; + } + + /* (non-Javadoc) + * @see org.openecomp.vid.asdc.AsdcClient#getResourceToscaModel(java.util.UUID) + */ + public Path getResourceToscaModel(UUID resourceUuid) throws AsdcCatalogException { + final String toscaModelURL = getCatalog().getJSONObject("resources") + .getJSONObject(resourceUuid.toString()) + .getString("toscaModelURL"); + + + final InputStream toscaModelStream = getClass().getClassLoader().getResourceAsStream(toscaModelURL); + + if (toscaModelStream == null) return null; + + return null;//getToscaModel(toscaModelStream); + } + + /* (non-Javadoc) + * @see org.openecomp.vid.asdc.AsdcClient#getServiceToscaModel(java.util.UUID) + */ + public Path getServiceToscaModel(UUID serviceUuid) throws AsdcCatalogException { + + String toscaModelURL = null; + + final JSONArray categoryJsonArray = getCatalog().getJSONArray("services"); + + for (int i = 0; i < categoryJsonArray.length() ; i++) { + + JSONObject jsonServiceObject = categoryJsonArray.getJSONObject(i); + if (jsonServiceObject.get("uuid").equals(serviceUuid.toString())) { + toscaModelURL = jsonServiceObject.getString("toscaModelURL"); + } + } + + final InputStream toscaModelStream = getClass().getClassLoader().getResourceAsStream(toscaModelURL); + + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource(toscaModelURL).getFile()); + Path path = Paths.get(file.getPath()); + + if (toscaModelStream == null) return null; + + return path; + } + + /** + * Gets the tosca model. + * + * @param csarInputStream the csar input stream + * @return the tosca model + * @throws AsdcCatalogException the asdc catalog exception + */ + private ToscaCsar getToscaModel(InputStream csarInputStream) throws AsdcCatalogException { + final Path csarFile; + + try { + csarFile = Files.createTempFile("csar", ".zip"); + Files.copy(csarInputStream, csarFile, StandardCopyOption.REPLACE_EXISTING); + } catch (IOException e) { + throw new AsdcCatalogException("Caught IOException while creating CSAR", e); + } + + try (final ZipFile csar = new ZipFile(csarFile.toFile())) { + + final InputStream toscaMetaStream = csar.getInputStream(csar.getEntry("TOSCA-Metadata/TOSCA.meta")); + final ToscaMeta toscaMeta = new ToscaMeta.Builder(toscaMetaStream).build(); + final String entryDefinitions = toscaMeta.get("Entry-Definitions"); + final InputStream toscaParentEntryYamlStream = csar.getInputStream(csar.getEntry(entryDefinitions)); + + final Yaml yaml = new Yaml(); + final ToscaModel parentModel = yaml.loadAs(toscaParentEntryYamlStream, ToscaModel.class); + + final ToscaCsar.Builder csarBuilder = new ToscaCsar.Builder(parentModel); + + for (Map> imports : parentModel.getImports()) { + for (Map.Entry> entry : imports.entrySet()) { + final InputStream toscaChildEntryYamlStream = csar.getInputStream(csar.getEntry("Definitions/" + entry.getValue().get("file"))); + final ToscaModel childModel = yaml.loadAs(toscaChildEntryYamlStream, ToscaModel.class); + csarBuilder.addVnf(childModel); + } + } + + return csarBuilder.build(); + } catch (IOException e) { + throw new AsdcCatalogException("Caught IOException while processing CSAR", e); + } + } + +} diff --git a/vid-app-common/src/main/java/org/openecomp/vid/asdc/memory/InMemoryAsdcClient.java b/vid-app-common/src/main/java/org/openecomp/vid/asdc/memory/InMemoryAsdcClient.java index c5134bfca..123cc5788 100755 --- a/vid-app-common/src/main/java/org/openecomp/vid/asdc/memory/InMemoryAsdcClient.java +++ b/vid-app-common/src/main/java/org/openecomp/vid/asdc/memory/InMemoryAsdcClient.java @@ -7,7 +7,7 @@ * 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 @@ -168,7 +168,6 @@ public class InMemoryAsdcClient implements AsdcClient { } /* (non-Javadoc) - * @see org.openecomp.vid.asdc.AsdcClient#getResources() */ public Collection getResources() throws AsdcCatalogException { final Collection resources = new LinkedList (); @@ -299,7 +298,7 @@ public class InMemoryAsdcClient implements AsdcClient { /* (non-Javadoc) * @see org.openecomp.vid.asdc.AsdcClient#getResourceToscaModel(java.util.UUID) */ - public ToscaCsar getResourceToscaModel(UUID resourceUuid) throws AsdcCatalogException { + public Path getResourceToscaModel(UUID resourceUuid) throws AsdcCatalogException { final String toscaModelURL = getCatalog().getJSONObject("resources") .getJSONObject(resourceUuid.toString()) .getString("toscaModelURL"); @@ -309,13 +308,13 @@ public class InMemoryAsdcClient implements AsdcClient { if (toscaModelStream == null) return null; - return getToscaModel(toscaModelStream); + return null;//getToscaModel(toscaModelStream); } /* (non-Javadoc) * @see org.openecomp.vid.asdc.AsdcClient#getServiceToscaModel(java.util.UUID) */ - public ToscaCsar getServiceToscaModel(UUID serviceUuid) throws AsdcCatalogException { + public Path getServiceToscaModel(UUID serviceUuid) throws AsdcCatalogException { final String toscaModelURL = getCatalog().getJSONObject("services") .getJSONObject(serviceUuid.toString()) .getString("toscaModelURL"); @@ -324,7 +323,7 @@ public class InMemoryAsdcClient implements AsdcClient { if (toscaModelStream == null) return null; - return getToscaModel(toscaModelStream); + return null;//getToscaModel(toscaModelStream); } /** @@ -369,4 +368,5 @@ public class InMemoryAsdcClient implements AsdcClient { throw new AsdcCatalogException("Caught IOException while processing CSAR", e); } } + } diff --git a/vid-app-common/src/main/java/org/openecomp/vid/asdc/parser/ToscaParser.java b/vid-app-common/src/main/java/org/openecomp/vid/asdc/parser/ToscaParser.java new file mode 100644 index 000000000..eb1669846 --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/asdc/parser/ToscaParser.java @@ -0,0 +1,15 @@ +package org.openecomp.vid.asdc.parser; + +import java.nio.file.Path; + +import org.openecomp.sdc.tosca.parser.exceptions.SdcToscaParserException; +import org.openecomp.vid.asdc.AsdcCatalogException; +import org.openecomp.vid.asdc.beans.tosca.ToscaCsar; +import org.openecomp.vid.asdc.beans.Service; +import org.openecomp.vid.model.ServiceModel; + +public interface ToscaParser{ + ToscaCsar parse(Path path) throws AsdcCatalogException; + + ServiceModel makeServiceModel(String uuid,Path path,Service asdcServiceMetadata) throws Exception; +} \ No newline at end of file diff --git a/vid-app-common/src/main/java/org/openecomp/vid/asdc/parser/ToscaParserImpl.java b/vid-app-common/src/main/java/org/openecomp/vid/asdc/parser/ToscaParserImpl.java new file mode 100644 index 000000000..bea5da877 --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/asdc/parser/ToscaParserImpl.java @@ -0,0 +1,225 @@ +package org.openecomp.vid.asdc.parser; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Path; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.Map.Entry; +import java.util.zip.ZipFile; + +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.sdc.tosca.parser.exceptions.SdcToscaParserException; +import org.openecomp.vid.asdc.AsdcCatalogException; +import org.openecomp.vid.asdc.beans.Service; +import org.openecomp.vid.asdc.beans.tosca.NodeTemplate; +import org.openecomp.vid.asdc.beans.tosca.ToscaCsar; +import org.openecomp.vid.asdc.beans.tosca.ToscaMeta; +import org.openecomp.vid.asdc.beans.tosca.ToscaModel; +import org.openecomp.vid.model.ModelConstants; +import org.openecomp.vid.model.Network; +import org.openecomp.vid.model.Node; +import org.openecomp.vid.model.ServiceModel; +import org.openecomp.vid.model.VNF; +import org.openecomp.vid.properties.VidProperties; +import org.springframework.beans.factory.annotation.Autowired; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.error.YAMLException; + +public class ToscaParserImpl implements ToscaParser { + /** The Constant LOG. */ + static final EELFLoggerDelegate LOG = EELFLoggerDelegate.getLogger(ToscaParserImpl.class); + + @Autowired + private final static DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss:SSSS"); + + + private static final String asdcModelNamespace = VidProperties.getAsdcModelNamespace(); + private static final String vnfTag = asdcModelNamespace + ModelConstants.VNF; + private static final String networkTag = asdcModelNamespace + ModelConstants.NETWORK; + private static final String vfModuleTag = asdcModelNamespace + ModelConstants.VF_MODULE; + + + @Override + public ToscaCsar parse(Path path) throws AsdcCatalogException { + return getToscaCsar(path); + } + + private ToscaCsar getToscaCsar(final Path csarFile) throws AsdcCatalogException { + try (final ZipFile csar = new ZipFile(csarFile.toFile())) { + + final InputStream toscaMetaStream = csar.getInputStream(csar.getEntry("TOSCA-Metadata/TOSCA.meta")); + final ToscaMeta toscaMeta = new ToscaMeta.Builder(toscaMetaStream).build(); + final String entryDefinitions = toscaMeta.get("Entry-Definitions"); + final InputStream toscaParentEntryYamlStream = csar.getInputStream(csar.getEntry(entryDefinitions)); + + try { + final Yaml yaml = new Yaml(); + final ToscaModel parentModel = yaml.loadAs(toscaParentEntryYamlStream, ToscaModel.class); + + final ToscaCsar.Builder csarBuilder = new ToscaCsar.Builder(parentModel); + + for (Map> imports : parentModel.getImports()) { + LOG.debug("imports = " + imports.toString()); + for (Entry> entry : imports.entrySet()) { + if (entry.getValue() != null) { + String fname = entry.getValue().get("file"); + if ((fname != null) && (fname.startsWith("service") || fname.startsWith("resource"))) { + LOG.debug("fname = " + fname); + final InputStream toscaChildEntryYamlStream = csar + .getInputStream(csar.getEntry("Definitions/" + fname)); + + final ToscaModel childModel = yaml.loadAs(toscaChildEntryYamlStream, ToscaModel.class); + csarBuilder.addVnf(childModel); + } + } + } + } + + return csarBuilder.build(); + } catch (YAMLException e) { + throw new AsdcCatalogException("Caught exception while processing TOSCA YAML", e); + } + } catch (IOException e) { + throw new AsdcCatalogException("Caught IOException while processing CSAR", e); + } + } + + public ServiceModel makeServiceModel(String uuid, final Path serviceCsar,Service service ) throws AsdcCatalogException, SdcToscaParserException { + + + final ServiceModel serviceModel = new ServiceModel(); + ToscaCsar toscaCsar = getToscaCsar(serviceCsar); + String methodName = "getServices"; + LOG.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + methodName + " start"); + Boolean isNewFlow = false; + final Map vnfs = new HashMap(); + final Map networks = new HashMap(); + final ToscaModel asdcServiceToscaModel = toscaCsar.getParent(); + serviceModel.setService(ServiceModel.extractService(asdcServiceToscaModel, service)); + + + populateVnfsAndNetwork(methodName, isNewFlow, vnfs, networks, asdcServiceToscaModel, serviceModel); + + // If we see customization uuid under vnf or network, follow 1702 flow + if (isNewFlow) { + return (getCustomizedServices(asdcServiceToscaModel, serviceModel)); + } else { + VNF vnf = null; + for (ToscaModel vnfModel : toscaCsar.getChildren()) { + // using uuid to match should only be valid for 1610 models + final String vnfUuid = (vnfModel.getMetadata().getUUID()); + // find the VNF with that uuid, uuid is not the key anymore + vnf = findVNFAccordingToUUID(vnfs, vnfUuid); + if (vnf == null) { + LOG.warn("Couldn't find VNF object " + vnfUuid + ". Problem with Tosca model?"); + continue; + } + extractAndUpdateInputs(vnf, vnfModel); + ServiceModel.extractGroups(vnfModel, serviceModel); + } + + serviceModel.setVnfs(vnfs); + serviceModel.setNetworks(networks); + return serviceModel; + } + } + + private VNF findVNFAccordingToUUID(final Map vnfs, final String vnfUuid) { + VNF vnf = null; + for (Entry vnfComp : vnfs.entrySet()) { + if (((vnfComp.getValue().getUuid()).equalsIgnoreCase(vnfUuid))) { + // found the vnf + vnf = vnfComp.getValue(); + } + } + return vnf; + } + + private void extractAndUpdateInputs(VNF vnf, ToscaModel vnfModel) { + vnf.setInputs(vnfModel.gettopology_template().getInputs()); + } + + private static void populateVnfsAndNetwork(String methodName, Boolean isNewFlow, final Map vnfs, + final Map networks, final ToscaModel asdcServiceToscaModel, ServiceModel serviceModel) + throws AsdcCatalogException, SdcToscaParserException { + for (Entry component : extractNodeTemplates(asdcServiceToscaModel)) { + final String modelCustomizationName = component.getKey(); + LOG.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + methodName + + " model customization name: " + modelCustomizationName); + final NodeTemplate nodeTemplate = component.getValue(); + final String type = nodeTemplate.getType(); + + if (type.startsWith(vnfTag)) { + LOG.debug(EELFLoggerDelegate.debugLogger, + dateFormat.format(new Date()) + methodName + " found node template type: " + type); + final VNF vnf = new VNF(); + vnf.extractVnf(modelCustomizationName, nodeTemplate); +// populateNodeVersionIfMissing(nodeTemplate, vnf,service); + LOG.debug(EELFLoggerDelegate.debugLogger, + dateFormat.format(new Date()) + methodName + " VNF commands: " + vnf.getCommands()); + vnfs.put(modelCustomizationName, vnf); + isNewFlow = isNewFlow(vnf); + } + // Networks + if (type.startsWith(networkTag)) { + LOG.debug(EELFLoggerDelegate.debugLogger, + dateFormat.format(new Date()) + methodName + " found node template type: " + type); + final Network network = new Network(); + network.extractNetwork(modelCustomizationName, nodeTemplate); +// populateNodeVersionIfMissing(nodeTemplate, network, service); + isNewFlow = isNewFlow(network); + networks.put(modelCustomizationName, network); + + } + } + serviceModel.setVnfs(vnfs); + serviceModel.setNetworks(networks); + + } + + private static Set> extractNodeTemplates(final ToscaModel asdcServiceToscaModel) { + return asdcServiceToscaModel.gettopology_template().getnode_templates().entrySet(); + } + + private static boolean isNewFlow(Node node) { + return (node.getCustomizationUuid() != null) && (node.getCustomizationUuid().length() > 0); + } + + private static boolean isNodeVersionMissing(Node Node) { + return Node.getVersion() == null; + } + + private static void populateNodeVersionIfMissing(final NodeTemplate nodeTemplate, final Node node, Service service) + throws AsdcCatalogException { + if (isNodeVersionMissing(node)) { + node.setVersion(service.getVersion()); + } + } + + private ServiceModel getCustomizedServices(ToscaModel asdcServiceToscaModel, ServiceModel serviceModel) { + String methodName = "asdcServiceToscaModel"; + LOG.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + methodName + " start"); + + // asdcServiceToscaModel should have vf modules and vol groups populated + // at this point but + // they are not associated with the VNFs + ServiceModel.extractGroups(asdcServiceToscaModel,serviceModel); + // Now put the vf modules and volume groups under the VNF they belong + // too + serviceModel.associateGroups(); + return (serviceModel); + } + + + private UUID extractUUIDFromNodeTemplate(final NodeTemplate nodeTemplate) { + return UUID.fromString(nodeTemplate.getMetadata().getUUID()); + } + + +} \ No newline at end of file diff --git a/vid-app-common/src/main/java/org/openecomp/vid/asdc/parser/ToscaParserImpl2.java b/vid-app-common/src/main/java/org/openecomp/vid/asdc/parser/ToscaParserImpl2.java new file mode 100644 index 000000000..4819cae8f --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/asdc/parser/ToscaParserImpl2.java @@ -0,0 +1,226 @@ +package org.openecomp.vid.asdc.parser; + +import org.openecomp.sdc.tosca.parser.api.ISdcCsarHelper; +import org.openecomp.sdc.tosca.parser.exceptions.SdcToscaParserException; +import org.openecomp.sdc.tosca.parser.impl.FilterType; +import org.openecomp.sdc.tosca.parser.impl.SdcToscaParserFactory; +import org.openecomp.sdc.toscaparser.api.Group; +import org.openecomp.sdc.toscaparser.api.NodeTemplate; +import org.openecomp.sdc.toscaparser.api.Property; +import org.openecomp.sdc.toscaparser.api.elements.constraints.Constraint; +import org.openecomp.sdc.toscaparser.api.parameters.Input; +import org.openecomp.vid.asdc.beans.Service; +import org.openecomp.vid.model.*; + +import java.nio.file.Path; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ToscaParserImpl2 { + + + public class Constants { + public final static String uuid = "UUID"; + public final static String description = "description"; + public final static String ecompGeneratedNaming = "ecompGeneratedNaming"; + public final static String customizationUUID = "customizationUUID"; + public final static String vfModuleModelVersion = "vfModuleModelVersion"; + public final static String vfModuleModelCustomizationUUID = "vfModuleModelCustomizationUUID"; + public final static String volume_group = "volume_group"; + public final static String vfModuleModelInvariantUUID = "vfModuleModelInvariantUUID"; + public final static String vfModuleModelUUID = "vfModuleModelUUID"; + public final static String invariantUUID = "invariantUUID"; + public final static String version = "version"; + public final static String name = "name"; + public final static String category = "category"; + public final static String vfModuleModelName = "vfModuleModelName"; + public final static String getInput = "get_input"; + } + + public ToscaParserImpl2() { + + } + + public ServiceModel makeServiceModel(Path path, Service asdcServiceMetadata) throws Exception { + ServiceModel serviceModel = new ServiceModel(); + SdcToscaParserFactory factory = SdcToscaParserFactory.getInstance(); + ISdcCsarHelper sdcCsarHelper = factory.getSdcCsarHelper(path.toFile().getAbsolutePath()); + serviceModel.setService(extractServiceFromCsar(asdcServiceMetadata, sdcCsarHelper)); + serviceModel.setVolumeGroups(extractVolumeGroups(sdcCsarHelper)); + serviceModel.setVfModules(extractVfModuleFromCsar(sdcCsarHelper)); + serviceModel.setVnfs(extractVnfsFromCsar(sdcCsarHelper)); + serviceModel.setNetworks(extractNetworksFromCsar(sdcCsarHelper)); + return serviceModel; + } + + private org.openecomp.vid.model.Service extractServiceFromCsar(Service asdcServiceMetadata, ISdcCsarHelper csarHelper) throws SdcToscaParserException { + org.openecomp.vid.model.Service service = new org.openecomp.vid.model.Service(); + + service.setName(csarHelper.getServiceMetadata().getValue(Constants.name)); + service.setCategory(csarHelper.getServiceMetadata().getValue(Constants.category)); + service.setInvariantUuid(csarHelper.getServiceMetadata().getValue(Constants.invariantUUID)); + service.setUuid(csarHelper.getServiceMetadata().getValue(Constants.uuid)); + service.setVersion(asdcServiceMetadata.getVersion()); + service.setDescription(csarHelper.getServiceMetadata().getValue(Constants.description)); + service.setInputs(inputsListToInputsMap(csarHelper.getServiceInputs())); + service.setServiceEcompNaming(csarHelper.getServiceMetadata().getValue(Constants.ecompGeneratedNaming)); + return service; + } + + private Map extractVnfsFromCsar(ISdcCsarHelper csarHelper) { + List nodeTemplates = csarHelper.getServiceVfList(); + Map vnfsMaps = new HashMap(); + + for (NodeTemplate nodeTemplate : nodeTemplates) { + VNF vnf = new VNF(); + populateNodeFromNodeTemplate(nodeTemplate, csarHelper, vnf); + vnf.setModelCustomizationName(nodeTemplate.getName()); + vnfsMaps.put(nodeTemplate.getName(), vnf); + } + return vnfsMaps; + } + + private Map extractNetworksFromCsar(ISdcCsarHelper csarHelper) { + List nodeTemplates = csarHelper.getServiceVlList(); + Map networksMap = new HashMap(); + + for (NodeTemplate nodeTemplate : nodeTemplates) { + Network newNetwork = new Network(); + populateNodeFromNodeTemplate(nodeTemplate, csarHelper, newNetwork); + newNetwork.setModelCustomizationName(nodeTemplate.getName()); + networksMap.put(nodeTemplate.getName(), newNetwork); + } + return networksMap; + } + + private Map extractVfModuleFromCsar(ISdcCsarHelper csarHelper) { + List serviceVfList = csarHelper.getServiceVfList(); + HashMap vfModuleHashMap = new HashMap<>(); + + for (NodeTemplate nodeTemplate : serviceVfList) { + List groups = csarHelper.getVfModulesByVf(nodeTemplate.getMetaData().getValue(Constants.customizationUUID)); + for (Group group : groups) { + vfModuleHashMap.put(group.getName(), populateVfModuleFromGroup(group)); + } + } + return vfModuleHashMap; + } + + + private Map extractVolumeGroups(ISdcCsarHelper csarHelper) { + HashMap volumeGroupHashMap = new HashMap<>(); + for (NodeTemplate nodeTemplate : csarHelper.getServiceVfList()) { + List groups = csarHelper.getVfModulesByVf(csarHelper.getNodeTemplateCustomizationUuid(nodeTemplate)); + for (Group group : groups) { + boolean isVolumeGroup = Boolean.valueOf(group.getPropertyValue(Constants.volume_group).toString()); + if (isVolumeGroup) { + volumeGroupHashMap.put(group.getName(), populateVolumeGroupFromGroup(group)); + } + } + } + return volumeGroupHashMap; + } + + private Map inputsListToInputsMap(List inputList) { + Map inputs = new HashMap<>(); + for (org.openecomp.sdc.toscaparser.api.parameters.Input input : inputList) { + inputs.put(input.getName(), convertInput(input, new org.openecomp.vid.asdc.beans.tosca.Input())); + } + return inputs; + } + + private Node populateNodeFromNodeTemplate(NodeTemplate nodeTemplate, ISdcCsarHelper csarHelper, Node newNode) { + newNode.setCustomizationUuid(csarHelper.getNodeTemplateCustomizationUuid(nodeTemplate)); + newNode.setDescription(nodeTemplate.getMetaData().getValue(Constants.description)); + newNode.setInvariantUuid(nodeTemplate.getMetaData().getValue(Constants.invariantUUID)); + newNode.setUuid(nodeTemplate.getMetaData().getValue(Constants.uuid)); + newNode.setName(nodeTemplate.getMetaData().getValue(Constants.name)); + newNode.setVersion(nodeTemplate.getMetaData().getValue(Constants.version)); + newNode.setInputs(extractInputsAndCommandsForNodeTemplate(nodeTemplate, csarHelper, newNode)); + Map propertiesMap = setPropertiesOfVnf(nodeTemplate.getPropertiesObjects()); + newNode.setProperties(propertiesMap); + return newNode; + } + + private VfModule populateVfModuleFromGroup(Group group){ + VfModule vfModule = new VfModule(); + + vfModule.setVersion(group.getMetadata().getValue(Constants.vfModuleModelVersion)); + vfModule.setCustomizationUuid(group.getMetadata().getValue(Constants.vfModuleModelCustomizationUUID)); + vfModule.setModelCustomizationName(group.getMetadata().getValue(Constants.vfModuleModelName)); + vfModule.setName(group.getMetadata().getValue(Constants.vfModuleModelName)); + vfModule.setVolumeGroupAllowed(Boolean.valueOf((group.getPropertyValue(Constants.volume_group)).toString())); + vfModule.setDescription(group.getDescription()); + vfModule.setInvariantUuid(group.getMetadata().getValue(Constants.vfModuleModelInvariantUUID)); + vfModule.setUuid(group.getMetadata().getValue(Constants.vfModuleModelUUID)); + return vfModule; + } + + private VolumeGroup populateVolumeGroupFromGroup(Group group){ + VolumeGroup volumeGroup = new VolumeGroup(); + volumeGroup.setDescription(group.getDescription()); + volumeGroup.setInvariantUuid(group.getMetadata().getValue(Constants.vfModuleModelInvariantUUID)); + volumeGroup.setName(group.getMetadata().getValue(Constants.vfModuleModelName)); + volumeGroup.setModelCustomizationName(group.getMetadata().getValue(Constants.vfModuleModelName)); + volumeGroup.setVersion(group.getMetadata().getValue(Constants.vfModuleModelVersion)); + volumeGroup.setUuid(group.getMetadata().getValue(Constants.vfModuleModelUUID)); + return volumeGroup; + } + + + private Map extractInputsAndCommandsForNodeTemplate(NodeTemplate nodeTemplate, ISdcCsarHelper csarHelper, Node newNode){ + Map inputMap = new HashMap<>(); + Map commandPropertyMap = new HashMap<>(); + + List inputs = csarHelper.getServiceInputs(); + Map properties = csarHelper.filterNodeTemplatePropertiesByValue(nodeTemplate, FilterType.CONTAINS, Constants.getInput); + for (Map.Entry property : properties.entrySet()) { + String inputKey = property.getValue(); + String key = extractInputValue(inputKey); + for (Input input: inputs){ + if(input.getName().equals(key)){ + org.openecomp.vid.asdc.beans.tosca.Input localInput = new org.openecomp.vid.asdc.beans.tosca.Input(); + localInput = convertInput(input, localInput); + String name = property.getKey(); + commandPropertyMap.put(name, extractCommands(name, key)); + inputMap.put(name, localInput); + } + } + } + newNode.setCommands(commandPropertyMap); + return inputMap; + } + + private String extractInputValue(String inputKey) { + return inputKey.substring(inputKey.indexOf(":") + 1); + } + + private org.openecomp.vid.asdc.beans.tosca.Input convertInput(Input parserInput, org.openecomp.vid.asdc.beans.tosca.Input localInput){ + localInput.setDefault(parserInput.getDefault()); + localInput.setDescription(parserInput.getDescription()); + localInput.setRequired(parserInput.isRequired()); + localInput.setType(parserInput.getType()); + localInput.setConstraints(parserInput.getConstraints()); +// localInput.setentry_schema() + return localInput; + } + + private CommandProperty extractCommands(String displayName, String inputName){ + CommandProperty commandProperty = new CommandProperty(); + commandProperty.setDisplayName(displayName); + commandProperty.setCommand(Constants.getInput); + commandProperty.setInputName(inputName); + return commandProperty; + } + + private Map setPropertiesOfVnf(List properties) { + Map propertiesMap = new HashMap(); + for (Property property : properties) { + propertiesMap.put(property.getName(), property.getValue().toString()); + } + return propertiesMap; + } + + +} \ No newline at end of file diff --git a/vid-app-common/src/main/java/org/openecomp/vid/asdc/rest/RestfulAsdcClient.java b/vid-app-common/src/main/java/org/openecomp/vid/asdc/rest/RestfulAsdcClient.java index 9f7c3a57d..5b783f5cd 100755 --- a/vid-app-common/src/main/java/org/openecomp/vid/asdc/rest/RestfulAsdcClient.java +++ b/vid-app-common/src/main/java/org/openecomp/vid/asdc/rest/RestfulAsdcClient.java @@ -20,10 +20,28 @@ package org.openecomp.vid.asdc.rest; +import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.vid.asdc.AsdcCatalogException; +import org.openecomp.vid.asdc.AsdcClient; +import org.openecomp.vid.asdc.beans.Artifact; +import org.openecomp.vid.asdc.beans.Resource; +import org.openecomp.vid.asdc.beans.Service; +import org.openecomp.vid.asdc.parser.ToscaParserImpl; +import org.openecomp.vid.model.ModelConstants; +import org.openecomp.vid.properties.VidProperties; + +import javax.ws.rs.NotFoundException; +import javax.ws.rs.ProcessingException; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ResponseProcessingException; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.GenericType; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedHashMap; import java.io.IOException; import java.io.InputStream; -import java.io.FileInputStream; -import java.io.File; import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; @@ -35,441 +53,413 @@ import java.util.Collections; import java.util.Map; import java.util.Map.Entry; import java.util.UUID; -import java.util.zip.ZipFile; - -import javax.ws.rs.NotFoundException; -import javax.ws.rs.ProcessingException; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.client.Client; -import javax.ws.rs.client.ResponseProcessingException; -import javax.ws.rs.client.WebTarget; -import javax.ws.rs.core.GenericType; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.MultivaluedHashMap; - -import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; -import org.openecomp.vid.asdc.AsdcCatalogException; -import org.openecomp.vid.asdc.AsdcClient; -import org.openecomp.vid.asdc.beans.Artifact; -import org.openecomp.vid.asdc.beans.Resource; -import org.openecomp.vid.asdc.beans.Service; -import org.openecomp.vid.asdc.beans.tosca.ToscaCsar; -import org.openecomp.vid.asdc.beans.tosca.ToscaMeta; -import org.openecomp.vid.asdc.beans.tosca.ToscaModel; -import org.openecomp.vid.model.ModelConstants; -import org.yaml.snakeyaml.Yaml; -import org.yaml.snakeyaml.error.YAMLException; - -import org.openecomp.vid.properties.VidProperties; -import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; /** * The Class RestfulAsdcClient. */ public class RestfulAsdcClient implements AsdcClient { - /** The Constant LOG. */ - private static final EELFLoggerDelegate LOG = EELFLoggerDelegate.getLogger(RestfulAsdcClient.class); - - /** The Constant dateFormat. */ - final static DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss:SSSS"); - - /** The client. */ - private final Client client; - - /** The uri. */ - private final URI uri; - - /** The common headers. */ - private final MultivaluedHashMap commonHeaders; - - /** The auth. */ - private final String auth; - - /** - * The Class Builder. - */ - public static class Builder { - - /** The client. */ - private final Client client; - - /** The uri. */ - private final URI uri; - - /** The auth. */ - private String auth = null; - - /** - * Instantiates a new builder. - * - * @param client the client - * @param uri the uri - */ - public Builder(Client client, URI uri) { - this.client = client; - this.client.register(JacksonJsonProvider.class); - this.uri = uri; - } - - /** - * Auth. - * - * @param auth the auth - * @return the builder - */ - public Builder auth(String auth) { - this.auth = auth; - return this; - } - - /** - * Builds the. - * - * @return the restful asdc client - */ - public RestfulAsdcClient build() { - return new RestfulAsdcClient(this); - } - } - - /** - * Instantiates a new restful asdc client. - * - * @param builder the builder - */ - private RestfulAsdcClient(Builder builder) { - client = builder.client; - uri = builder.uri; - auth = builder.auth; - - commonHeaders = new MultivaluedHashMap (); - commonHeaders.put("X-ECOMP-InstanceID", Collections.singletonList((Object) "VID")); - commonHeaders.put("Authorization", Collections.singletonList((Object) (auth))); - } - - /** - * Gets the client. - * - * @return the client - */ - private Client getClient() { return client; } - - /* (non-Javadoc) - * @see org.openecomp.vid.asdc.AsdcClient#getResource(java.util.UUID) - */ - public Resource getResource(UUID uuid) throws AsdcCatalogException { - String path = VidProperties.getPropertyWithDefault(ModelConstants.ASDC_RESOURCE_API_PATH, ModelConstants.DEFAULT_ASDC_RESOURCE_API_PATH); - try { - return getClient() - .target(uri) - .path(path + "/" + uuid.toString() + "/metadata") - .request(MediaType.APPLICATION_JSON_TYPE) - .headers(commonHeaders) - .header("Content-Type", MediaType.APPLICATION_JSON) - .get(Resource.class); - } catch (ResponseProcessingException e) { - //Couldn't convert response to Java type - throw new AsdcCatalogException("ASDC response could not be processed", e); - } catch (ProcessingException e) { - //IO problems during request - throw new AsdcCatalogException("Failed to get a response from ASDC service", e); - } catch (WebApplicationException e) { - //Web service returned data, but the response status wasn't a good one (i.e. non 2xx) - throw new AsdcCatalogException(e); - } - } - - /* (non-Javadoc) - * @see org.openecomp.vid.asdc.AsdcClient#getResources() - */ - public Collection getResources() throws AsdcCatalogException { - String path = VidProperties.getPropertyWithDefault(ModelConstants.ASDC_RESOURCE_API_PATH, ModelConstants.DEFAULT_ASDC_RESOURCE_API_PATH); - try { - return getClient() - .target(uri) - .path(path) - .request(MediaType.APPLICATION_JSON_TYPE) - .headers(commonHeaders) - .header("Content-Type", MediaType.APPLICATION_JSON) - .get(new GenericType> () {}); - } catch (ResponseProcessingException e) { - //Couldn't convert response to Java type - throw new AsdcCatalogException("ASDC response could not be processed", e); - } catch (ProcessingException e) { - //IO problems during request - throw new AsdcCatalogException("Failed to get a response from ASDC service", e); - } catch (WebApplicationException e) { - //Web service returned data, but the response status wasn't a good one (i.e. non 2xx) - throw new AsdcCatalogException(e); - } - } - - /* (non-Javadoc) - * @see org.openecomp.vid.asdc.AsdcClient#getResources(java.util.Map) - */ - public Collection getResources(Map filter) throws AsdcCatalogException { - String path = VidProperties.getPropertyWithDefault(ModelConstants.ASDC_RESOURCE_API_PATH, ModelConstants.DEFAULT_ASDC_RESOURCE_API_PATH); - WebTarget target = getClient() - .target(uri) - .path(path); - - for (Entry filterEntry : filter.entrySet()) { - target = target.queryParam(filterEntry.getKey(), (Object []) filterEntry.getValue()); - } - - try { - return target.request() - .accept(MediaType.APPLICATION_JSON_TYPE) - .headers(commonHeaders) - .header("Content-Type", MediaType.APPLICATION_JSON) - .get(new GenericType> () {}); - } catch (ResponseProcessingException e) { - //Couldn't convert response to Java type - throw new AsdcCatalogException("ASDC response could not be processed", e); - } catch (ProcessingException e) { - //IO problems during request - throw new AsdcCatalogException("Failed to get a response from ASDC service", e); - } catch (NotFoundException e) { - throw e; - } catch (WebApplicationException e) { - //Web service returned data, but the response status wasn't a good one (i.e. non 2xx) - throw new AsdcCatalogException(e); - } - } - - /* (non-Javadoc) - * @see org.openecomp.vid.asdc.AsdcClient#getResourceArtifact(java.util.UUID, java.util.UUID) - */ - public Artifact getResourceArtifact(UUID resourceUuid, UUID artifactUuid) throws AsdcCatalogException { - String path = VidProperties.getPropertyWithDefault(ModelConstants.ASDC_RESOURCE_API_PATH, ModelConstants.DEFAULT_ASDC_RESOURCE_API_PATH); - try { - return getClient() - .target(uri) - .path(path + "/" + resourceUuid + "/artifacts/" + artifactUuid) - .request(MediaType.APPLICATION_JSON_TYPE) - .headers(commonHeaders) - .header("Content-Type", MediaType.APPLICATION_JSON) - .get(Artifact.class); - } catch (ResponseProcessingException e) { - //Couldn't convert response to Java type - throw new AsdcCatalogException("ASDC response could not be processed", e); - } catch (ProcessingException e) { - //IO problems during request - throw new AsdcCatalogException("Failed to get a response from ASDC service", e); - } catch (WebApplicationException e) { - //Web service returned data, but the response status wasn't a good one (i.e. non 2xx) - throw new AsdcCatalogException(e); - } - } - - /* (non-Javadoc) - * @see org.openecomp.vid.asdc.AsdcClient#getService(java.util.UUID) - */ - public Service getService(UUID uuid) throws AsdcCatalogException { - - String path = VidProperties.getPropertyWithDefault(ModelConstants.ASDC_SVC_API_PATH, ModelConstants.DEFAULT_ASDC_SVC_API_PATH); - try { - return getClient() - .target(uri) - .path( path + "/" + uuid.toString() + "/metadata") - .request(MediaType.APPLICATION_JSON) - .headers(commonHeaders) - .get(Service.class); - } catch (ResponseProcessingException e) { - //Couldn't convert response to Java type - throw new AsdcCatalogException("ASDC response could not be processed", e); - } catch (ProcessingException e) { - //IO problems during request - throw new AsdcCatalogException("Failed to get a response from ASDC service", e); - } catch (WebApplicationException e) { - //Web service returned data, but the response status wasn't a good one (i.e. non 2xx) - throw new AsdcCatalogException(e); - } - } - - /* (non-Javadoc) - * @see org.openecomp.vid.asdc.AsdcClient#getServices() - */ - public Collection getServices() throws AsdcCatalogException { - String path = VidProperties.getPropertyWithDefault(ModelConstants.ASDC_SVC_API_PATH, ModelConstants.DEFAULT_ASDC_SVC_API_PATH); - try { - return getClient() - .target(uri) - .path(path) - .request() - .accept(MediaType.APPLICATION_JSON_TYPE) - .headers(commonHeaders) - .header("Content-Type", MediaType.APPLICATION_JSON) - .get(new GenericType> () {}); - } catch (ResponseProcessingException e) { - //Couldn't convert response to Java type - throw new AsdcCatalogException("ASDC response could not be processed", e); - } catch (ProcessingException e) { - //IO problems during request - throw new AsdcCatalogException("Failed to get a response from ASDC service", e); - } catch (WebApplicationException e) { - //Web service returned data, but the response status wasn't a good one (i.e. non 2xx) - throw new AsdcCatalogException(e); - } - } - - /* (non-Javadoc) - * @see org.openecomp.vid.asdc.AsdcClient#getServices(java.util.Map) - */ - public Collection getServices(Map filter) throws AsdcCatalogException { - - String path = VidProperties.getPropertyWithDefault(ModelConstants.ASDC_SVC_API_PATH, ModelConstants.DEFAULT_ASDC_SVC_API_PATH); - WebTarget target = getClient() - .target(uri) - .path(path); - - - for (Entry filterEntry : filter.entrySet()) { - target = target.queryParam(filterEntry.getKey(), (Object []) filterEntry.getValue()); - } - - try { - return target.request() - .accept(MediaType.APPLICATION_JSON_TYPE) - .headers(commonHeaders) - .header("Content-Type", MediaType.APPLICATION_JSON) - .get(new GenericType> () {}); - } catch (ResponseProcessingException e) { - //Couldn't convert response to Java type - throw new AsdcCatalogException("ASDC response could not be processed", e); - } catch (ProcessingException e) { - //IO problems during request - throw new AsdcCatalogException("Failed to get a response from ASDC service", e); - } catch (NotFoundException e) { - throw e; - } catch (WebApplicationException e) { - //Web service returned data, but the response status wasn't a good one (i.e. non 2xx) - throw new AsdcCatalogException(e); - } - } - - /* (non-Javadoc) - * @see org.openecomp.vid.asdc.AsdcClient#getServiceArtifact(java.util.UUID, java.util.UUID) - */ - public Artifact getServiceArtifact(UUID serviceUuid, UUID artifactUuid) throws AsdcCatalogException { - String path = VidProperties.getPropertyWithDefault(ModelConstants.ASDC_SVC_API_PATH, ModelConstants.DEFAULT_ASDC_SVC_API_PATH); - try { - return getClient() - .target(uri) - .path(path + "/" + serviceUuid + "/artifacts/" + artifactUuid) - .request(MediaType.APPLICATION_JSON_TYPE) - .headers(commonHeaders) - .header("Content-Type", MediaType.APPLICATION_JSON) - .get(Artifact.class); - } catch (ResponseProcessingException e) { - //Couldn't convert response to Java type - throw new AsdcCatalogException("ASDC response could not be processed", e); - } catch (ProcessingException e) { - //IO problems during request - throw new AsdcCatalogException("Failed to get a response from ASDC service", e); - } catch (WebApplicationException e) { - //Web service returned data, but the response status wasn't a good one (i.e. non 2xx) - throw new AsdcCatalogException(e); - } - } - - /* (non-Javadoc) - * @see org.openecomp.vid.asdc.AsdcClient#getResourceToscaModel(java.util.UUID) - */ - public ToscaCsar getResourceToscaModel(UUID resourceUuid) throws AsdcCatalogException { - String path = VidProperties.getPropertyWithDefault(ModelConstants.ASDC_RESOURCE_API_PATH, ModelConstants.DEFAULT_ASDC_RESOURCE_API_PATH); - try (final InputStream csarInputStream = (InputStream) getClient() - .target(uri) - .path(path + "/" + resourceUuid + "/toscaModel") - .request(MediaType.APPLICATION_OCTET_STREAM_TYPE) - .headers(commonHeaders) - .header("Content-Type", MediaType.APPLICATION_OCTET_STREAM) - .get(InputStream.class)) { - - return getToscaModel(csarInputStream); - } catch (IOException e) { - throw new AsdcCatalogException("Failed to retrieve resource TOSCA model from ASDC", e); - } - } - - /* (non-Javadoc) - * @see org.openecomp.vid.asdc.AsdcClient#getServiceToscaModel(java.util.UUID) - */ - public ToscaCsar getServiceToscaModel(UUID serviceUuid) throws AsdcCatalogException { - String path = VidProperties.getPropertyWithDefault(ModelConstants.ASDC_SVC_API_PATH, ModelConstants.DEFAULT_ASDC_SVC_API_PATH); - try { - final InputStream csarInputStream = (InputStream) getClient() - .target(uri) - .path(path + "/" + serviceUuid + "/toscaModel") - .request(MediaType.APPLICATION_OCTET_STREAM_TYPE) - .headers(commonHeaders) - .header("Content-Type", MediaType.APPLICATION_OCTET_STREAM) - .get(InputStream.class); - - return getToscaModel(csarInputStream); - } catch (ResponseProcessingException e) { - //Couldn't convert response to Java type - throw new AsdcCatalogException("ASDC response could not be processed", e); - } catch (ProcessingException e) { - //IO problems during request - throw new AsdcCatalogException("Failed to get a response from ASDC service", e); - } catch (WebApplicationException e) { - //Web service returned data, but the response status wasn't a good one (i.e. non 2xx) - throw new AsdcCatalogException(e); - } - } - - /** - * Gets the tosca model. - * - * @param csarInputStream the csar input stream - * @return the tosca model - * @throws AsdcCatalogException the asdc catalog exception - */ - private ToscaCsar getToscaModel(InputStream csarInputStream) throws AsdcCatalogException { - final Path csarFile; - try { - csarFile = Files.createTempFile("csar", ".zip"); - Files.copy(csarInputStream, csarFile, StandardCopyOption.REPLACE_EXISTING); - } catch (IOException e) { - throw new AsdcCatalogException("Caught IOException while creating CSAR", e); - } + /** + * The Class Builder. + */ + public static class Builder { + + /** + * The client. + */ + private final Client client; + + /** + * The uri. + */ + private final URI uri; + + /** + * The auth. + */ + private String auth = null; + + /** + * Instantiates a new builder. + * + * @param client the client + * @param uri the uri + */ + public Builder(Client client, URI uri) { + this.client = client; + this.client.register(JacksonJsonProvider.class); + this.uri = uri; + } + + /** + * Auth. + * + * @param auth the auth + * @return the builder + */ + public Builder auth(String auth) { + this.auth = auth; + return this; + } + + /** + * Builds the. + * + * @return the restful asdc client + */ + public RestfulAsdcClient build() { + return new RestfulAsdcClient(this); + } + } + + /** + * The Constant LOG. + */ + static final EELFLoggerDelegate LOG = EELFLoggerDelegate.getLogger(RestfulAsdcClient.class); + + /** + * The Constant dateFormat. + */ + final static DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss:SSSS"); + + /** + * The client. + */ + private final Client client; + + /** + * The uri. + */ + private final URI uri; + + /** + * The common headers. + */ + private final MultivaluedHashMap commonHeaders; + + /** + * The auth. + */ + private final String auth; + + ToscaParserImpl p = new ToscaParserImpl(); + + /** + * Instantiates a new restful asdc client. + * + * @param builder the builder + */ + private RestfulAsdcClient(Builder builder) { + client = builder.client; + uri = builder.uri; + auth = builder.auth; + + commonHeaders = new MultivaluedHashMap(); + commonHeaders.put("X-ECOMP-InstanceID", Collections.singletonList((Object) "VID")); + commonHeaders.put("Authorization", Collections.singletonList((Object) (auth))); + } + + private Path createTmpFile(InputStream csarInputStream) throws AsdcCatalogException { + final Path csarFile; + try { + csarFile = Files.createTempFile("csar", ".zip"); + Files.copy(csarInputStream, csarFile, StandardCopyOption.REPLACE_EXISTING); + } catch (IOException e) { + throw new AsdcCatalogException("Caught IOException while creating CSAR", e); + } + return csarFile; + } + + /** + * Gets the client. + * + * @return the client + */ + private Client getClient() { + return client; + } + + /* (non-Javadoc) + * @see org.openecomp.vid.asdc.AsdcClient#getResource(java.util.UUID) + */ + public Resource getResource(UUID uuid) throws AsdcCatalogException { + String path = VidProperties.getPropertyWithDefault(ModelConstants.ASDC_RESOURCE_API_PATH, ModelConstants.DEFAULT_ASDC_RESOURCE_API_PATH); + try { + return getClient() + .target(uri) + .path(path + "/" + uuid.toString() + "/metadata") + .request(MediaType.APPLICATION_JSON_TYPE) + .headers(commonHeaders) + .header("Content-Type", MediaType.APPLICATION_JSON) + .get(Resource.class); + } catch (ResponseProcessingException e) { + //Couldn't convert response to Java type + throw new AsdcCatalogException("ASDC response could not be processed", e); + } catch (ProcessingException e) { + //IO problems during request + throw new AsdcCatalogException("Failed to get a response from ASDC service", e); + } catch (WebApplicationException e) { + //Web service returned data, but the response status wasn't a good one (i.e. non 2xx) + throw new AsdcCatalogException(e); + } + } + + /* (non-Javadoc) + * @see org.openecomp.vid.asdc.AsdcClient#getResourceArtifact(java.util.UUID, java.util.UUID) + */ + public Artifact getResourceArtifact(UUID resourceUuid, UUID artifactUuid) throws AsdcCatalogException { + String path = VidProperties.getPropertyWithDefault(ModelConstants.ASDC_RESOURCE_API_PATH, ModelConstants.DEFAULT_ASDC_RESOURCE_API_PATH); + try { + return getClient() + .target(uri) + .path(path + "/" + resourceUuid + "/artifacts/" + artifactUuid) + .request(MediaType.APPLICATION_JSON_TYPE) + .headers(commonHeaders) + .header("Content-Type", MediaType.APPLICATION_JSON) + .get(Artifact.class); + } catch (ResponseProcessingException e) { + //Couldn't convert response to Java type + throw new AsdcCatalogException("ASDC response could not be processed", e); + } catch (ProcessingException e) { + //IO problems during request + throw new AsdcCatalogException("Failed to get a response from ASDC service", e); + } catch (WebApplicationException e) { + //Web service returned data, but the response status wasn't a good one (i.e. non 2xx) + throw new AsdcCatalogException(e); + } + } + + /* (non-Javadoc) + * @see org.openecomp.vid.asdc.AsdcClient#getResources() + */ + public Collection getResources() throws AsdcCatalogException { + String path = VidProperties.getPropertyWithDefault(ModelConstants.ASDC_RESOURCE_API_PATH, ModelConstants.DEFAULT_ASDC_RESOURCE_API_PATH); + try { + return getClient() + .target(uri) + .path(path) + .request(MediaType.APPLICATION_JSON_TYPE) + .headers(commonHeaders) + .header("Content-Type", MediaType.APPLICATION_JSON) + .get(new GenericType>() { + }); + } catch (ResponseProcessingException e) { + //Couldn't convert response to Java type + throw new AsdcCatalogException("ASDC response could not be processed", e); + } catch (ProcessingException e) { + //IO problems during request + throw new AsdcCatalogException("Failed to get a response from ASDC service", e); + } catch (WebApplicationException e) { + //Web service returned data, but the response status wasn't a good one (i.e. non 2xx) + throw new AsdcCatalogException(e); + } + } + + /* (non-Javadoc) + * @see org.openecomp.vid.asdc.AsdcClient#getResources(java.util.Map) + */ + public Collection getResources(Map filter) throws AsdcCatalogException { + String path = VidProperties.getPropertyWithDefault(ModelConstants.ASDC_RESOURCE_API_PATH, ModelConstants.DEFAULT_ASDC_RESOURCE_API_PATH); + WebTarget target = getClient() + .target(uri) + .path(path); + + for (Entry filterEntry : filter.entrySet()) { + target = target.queryParam(filterEntry.getKey(), (Object[]) filterEntry.getValue()); + } + + try { + return target.request() + .accept(MediaType.APPLICATION_JSON_TYPE) + .headers(commonHeaders) + .header("Content-Type", MediaType.APPLICATION_JSON) + .get(new GenericType>() { + }); + } catch (ResponseProcessingException e) { + //Couldn't convert response to Java type + throw new AsdcCatalogException("ASDC response could not be processed", e); + } catch (ProcessingException e) { + //IO problems during request + throw new AsdcCatalogException("Failed to get a response from ASDC service", e); + } catch (NotFoundException e) { + throw e; + } catch (WebApplicationException e) { + //Web service returned data, but the response status wasn't a good one (i.e. non 2xx) + throw new AsdcCatalogException(e); + } + } + + /* (non-Javadoc) + * @see org.openecomp.vid.asdc.AsdcClient#getResourceToscaModel(java.util.UUID) + */ + public Path getResourceToscaModel(UUID resourceUuid) throws AsdcCatalogException { + String path = VidProperties.getPropertyWithDefault(ModelConstants.ASDC_RESOURCE_API_PATH, ModelConstants.DEFAULT_ASDC_RESOURCE_API_PATH); + try (final InputStream csarInputStream = (InputStream) getClient() + .target(uri) + .path(path + "/" + resourceUuid + "/toscaModel") + .request(MediaType.APPLICATION_OCTET_STREAM_TYPE) + .headers(commonHeaders) + .header("Content-Type", MediaType.APPLICATION_OCTET_STREAM) + .get(InputStream.class)) { + + return getToscaCsar(csarInputStream); + } catch (IOException e) { + throw new AsdcCatalogException("Failed to retrieve resource TOSCA model from ASDC", e); + } + } + + /* (non-Javadoc) + * @see org.openecomp.vid.asdc.AsdcClient#getService(java.util.UUID) + */ + public Service getService(UUID uuid) throws AsdcCatalogException { + + String path = VidProperties.getPropertyWithDefault(ModelConstants.ASDC_SVC_API_PATH, ModelConstants.DEFAULT_ASDC_SVC_API_PATH); + try { + return getClient() + .target(uri) + .path(path + "/" + uuid.toString() + "/metadata") + .request(MediaType.APPLICATION_JSON) + .headers(commonHeaders) + .get(Service.class); + } catch (ResponseProcessingException e) { + //Couldn't convert response to Java type + throw new AsdcCatalogException("ASDC response could not be processed", e); + } catch (ProcessingException e) { + //IO problems during request + throw new AsdcCatalogException("Failed to get a response from ASDC service", e); + } catch (WebApplicationException e) { + //Web service returned data, but the response status wasn't a good one (i.e. non 2xx) + throw new AsdcCatalogException(e); + } + } + + /* (non-Javadoc) + * @see org.openecomp.vid.asdc.AsdcClient#getServiceArtifact(java.util.UUID, java.util.UUID) + */ + public Artifact getServiceArtifact(UUID serviceUuid, UUID artifactUuid) throws AsdcCatalogException { + String path = VidProperties.getPropertyWithDefault(ModelConstants.ASDC_SVC_API_PATH, ModelConstants.DEFAULT_ASDC_SVC_API_PATH); - try (final ZipFile csar = new ZipFile(csarFile.toFile())) { - - final InputStream toscaMetaStream = csar.getInputStream(csar.getEntry("TOSCA-Metadata/TOSCA.meta")); - final ToscaMeta toscaMeta = new ToscaMeta.Builder(toscaMetaStream).build(); - final String entryDefinitions = toscaMeta.get("Entry-Definitions"); - final InputStream toscaParentEntryYamlStream = csar.getInputStream(csar.getEntry(entryDefinitions)); - - try { - final Yaml yaml = new Yaml(); - final ToscaModel parentModel = yaml.loadAs(toscaParentEntryYamlStream, ToscaModel.class); - - final ToscaCsar.Builder csarBuilder = new ToscaCsar.Builder(parentModel); - - for (Map> imports : parentModel.getImports()) { - LOG.debug("imports = " + imports.toString()); - for (Entry> entry : imports.entrySet()) { - if ( entry.getValue() != null) { - String fname = entry.getValue().get("file"); - if ( ( fname != null ) && (fname.startsWith("service") || fname.startsWith("resource")) ) { - LOG.debug("fname = " + fname); - final InputStream toscaChildEntryYamlStream = csar.getInputStream(csar.getEntry("Definitions/" + fname )); - final ToscaModel childModel = yaml.loadAs(toscaChildEntryYamlStream, ToscaModel.class); - csarBuilder.addVnf(childModel); - } - } - } - } - - return csarBuilder.build(); - } catch (YAMLException e) { - throw new AsdcCatalogException("Caught exception while processing TOSCA YAML", e); - } - } catch (IOException e) { - throw new AsdcCatalogException("Caught IOException while processing CSAR", e); - } - } + try { + return getClient() + .target(uri) + .path(path + "/" + serviceUuid + "/artifacts/" + artifactUuid) + .request(MediaType.APPLICATION_JSON_TYPE) + .headers(commonHeaders) + .header("Content-Type", MediaType.APPLICATION_JSON) + .get(Artifact.class); + } catch (ResponseProcessingException e) { + //Couldn't convert response to Java type + throw new AsdcCatalogException("ASDC response could not be processed", e); + } catch (ProcessingException e) { + //IO problems during request + throw new AsdcCatalogException("Failed to get a response from ASDC service", e); + } catch (WebApplicationException e) { + //Web service returned data, but the response status wasn't a good one (i.e. non 2xx) + throw new AsdcCatalogException(e); + } + } + + /* (non-Javadoc) + * @see org.openecomp.vid.asdc.AsdcClient#getServices() + */ + public Collection getServices() throws AsdcCatalogException { + String path = VidProperties.getPropertyWithDefault(ModelConstants.ASDC_SVC_API_PATH, ModelConstants.DEFAULT_ASDC_SVC_API_PATH); + try { + return getClient() + .target(uri) + .path(path) + .request() + .accept(MediaType.APPLICATION_JSON_TYPE) + .headers(commonHeaders) + .header("Content-Type", MediaType.APPLICATION_JSON) + .get(new GenericType>() { + }); + } catch (ResponseProcessingException e) { + //Couldn't convert response to Java type + throw new AsdcCatalogException("ASDC response could not be processed", e); + } catch (ProcessingException e) { + //IO problems during request + throw new AsdcCatalogException("Failed to get a response from ASDC service", e); + } catch (WebApplicationException e) { + //Web service returned data, but the response status wasn't a good one (i.e. non 2xx) + throw new AsdcCatalogException(e); + } + } + + /* (non-Javadoc) + * @see org.openecomp.vid.asdc.AsdcClient#getServices(java.util.Map) + */ + public Collection getServices(Map filter) throws AsdcCatalogException { + + String path = VidProperties.getPropertyWithDefault(ModelConstants.ASDC_SVC_API_PATH, ModelConstants.DEFAULT_ASDC_SVC_API_PATH); + WebTarget target = getClient() + .target(uri) + .path(path); + + + for (Entry filterEntry : filter.entrySet()) { + target = target.queryParam(filterEntry.getKey(), (Object[]) filterEntry.getValue()); + } + + try { + return target.request() + .accept(MediaType.APPLICATION_JSON_TYPE) + .headers(commonHeaders) + .header("Content-Type", MediaType.APPLICATION_JSON) + .get(new GenericType>() { + }); + } catch (ResponseProcessingException e) { + //Couldn't convert response to Java type + throw new AsdcCatalogException("ASDC response could not be processed", e); + } catch (ProcessingException e) { + //IO problems during request + throw new AsdcCatalogException("Failed to get a response from ASDC service", e); + } catch (NotFoundException e) { + throw e; + } catch (WebApplicationException e) { + //Web service returned data, but the response status wasn't a good one (i.e. non 2xx) + throw new AsdcCatalogException(e); + } + } + + + /* (non-Javadoc) + * @see org.openecomp.vid.asdc.AsdcClient#getServiceToscaModel(java.util.UUID) + */ + public Path getServiceToscaModel(UUID serviceUuid) throws AsdcCatalogException { + String path = VidProperties.getPropertyWithDefault(ModelConstants.ASDC_SVC_API_PATH, ModelConstants.DEFAULT_ASDC_SVC_API_PATH); + try { + final InputStream csarInputStream = (InputStream) getClient() + .target(uri) + .path(path + "/" + serviceUuid + "/toscaModel") + .request(MediaType.APPLICATION_OCTET_STREAM_TYPE) + .headers(commonHeaders) + .header("Content-Type", MediaType.APPLICATION_OCTET_STREAM) + .get(InputStream.class); + + + return getToscaCsar(csarInputStream); + } catch (ResponseProcessingException e) { + //Couldn't convert response to Java type + throw new AsdcCatalogException("ASDC response could not be processed", e); + } catch (ProcessingException e) { + //IO problems during request + throw new AsdcCatalogException("Failed to get a response from ASDC service", e); + } catch (WebApplicationException e) { + //Web service returned data, but the response status wasn't a good one (i.e. non 2xx) + throw new AsdcCatalogException(e); + } + } + + + /** + * Gets the tosca model. + * + * @param csarInputStream the csar input stream + * @return the tosca model + * @throws AsdcCatalogException the asdc catalog exception + */ + private Path getToscaCsar(InputStream csarInputStream) throws AsdcCatalogException { + return createTmpFile(csarInputStream); + } } diff --git a/vid-app-common/src/main/java/org/openecomp/vid/controller/AaiController.java b/vid-app-common/src/main/java/org/openecomp/vid/controller/AaiController.java index 92b752859..861ddf8da 100755 --- a/vid-app-common/src/main/java/org/openecomp/vid/controller/AaiController.java +++ b/vid-app-common/src/main/java/org/openecomp/vid/controller/AaiController.java @@ -302,7 +302,6 @@ public class AaiController extends RestrictedBaseController{ * @param namedQueryId the named query id * @param globalCustomerId the global customer id * @param serviceType the service type - * @param serviceInstance the service instance * @return ResponseEntity The response entity */ @RequestMapping(value="/aai_get_models_by_service_type/{namedQueryId}/{globalCustomerId}/{serviceType}", method = RequestMethod.GET) diff --git a/vid-app-common/src/main/java/org/openecomp/vid/controller/VidController.java b/vid-app-common/src/main/java/org/openecomp/vid/controller/VidController.java index f7bf3a54e..9972ae5d3 100755 --- a/vid-app-common/src/main/java/org/openecomp/vid/controller/VidController.java +++ b/vid-app-common/src/main/java/org/openecomp/vid/controller/VidController.java @@ -20,144 +20,39 @@ package org.openecomp.vid.controller; -import java.io.InputStream; -import java.net.URI; -import java.net.URISyntaxException; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Arrays; -import java.util.Collection; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; -import java.util.UUID; - -import javax.net.ssl.SSLContext; -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.client.ClientBuilder; -import javax.ws.rs.client.Client; - -import org.json.JSONObject; -import org.json.JSONTokener; +import org.openecomp.portalsdk.core.controller.RestrictedBaseController; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.sdc.tosca.parser.exceptions.SdcToscaParserException; +import org.openecomp.vid.asdc.AsdcCatalogException; import org.openecomp.vid.exceptions.VidServiceUnavailableException; -import org.openecomp.vid.model.ModelUtil; -import org.openecomp.vid.model.ModelConstants; -import org.openecomp.vid.model.Network; import org.openecomp.vid.model.ServiceModel; -import org.openecomp.vid.model.VNF; -import org.openecomp.vid.model.VfModule; -import org.openecomp.vid.model.VolumeGroup; -//import org.openecomp.vid.model.Service; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.Bean; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.ModelAndView; +import org.openecomp.vid.services.*; -import org.openecomp.portalsdk.core.controller.RestrictedBaseController; -import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; -import org.openecomp.vid.asdc.AsdcCatalogException; -import org.openecomp.vid.asdc.AsdcClient; -import org.openecomp.vid.asdc.beans.Resource; -import org.openecomp.vid.asdc.beans.Service; -import org.openecomp.vid.asdc.beans.tosca.Group; -import org.openecomp.vid.asdc.beans.tosca.NodeTemplate; -import org.openecomp.vid.asdc.beans.tosca.ToscaCsar; -import org.openecomp.vid.asdc.beans.tosca.ToscaModel; -import org.openecomp.vid.asdc.memory.InMemoryAsdcClient; -import org.openecomp.vid.asdc.rest.RestfulAsdcClient; -import org.openecomp.vid.properties.AsdcClientConfiguration; -import org.openecomp.vid.properties.AsdcClientConfiguration.AsdcClientType; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.openecomp.vid.properties.VidProperties; -import java.security.KeyManagementException; -import java.security.NoSuchAlgorithmException; +import javax.servlet.http.HttpServletRequest; +import java.util.Collection; +import java.util.Map; + +//import org.openecomp.vid.model.Service; -/** - * The Class VidController. - */ @RestController public class VidController extends RestrictedBaseController { - /** The Constant LOG. */ private static final EELFLoggerDelegate LOG = EELFLoggerDelegate.getLogger(VidController.class); - - /** The Constant dateFormat. */ - final static DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss:SSSS"); - - /** The app context. */ - @Autowired - private ApplicationContext appContext; - - /** - * Gets the object mapper. - * - * @return the object mapper - */ - @Bean - public ObjectMapper getObjectMapper() { - return new ObjectMapper(); - } - - /** - * Gets the asdc client. - * - * @return the asdc client - */ - @Bean - public AsdcClient getAsdcClient() { - - final AsdcClientConfiguration asdcClientConfig = appContext.getBean(AsdcClientConfiguration.class); - switch (asdcClientConfig.getAsdcClientType()) { - case IN_MEMORY: - final InputStream asdcCatalogFile = VidController.class.getClassLoader().getResourceAsStream("catalog.json"); - final JSONTokener tokener = new JSONTokener(asdcCatalogFile); - final JSONObject catalog = new JSONObject(tokener); + private final VidService service; - return new InMemoryAsdcClient.Builder().catalog(catalog).build(); - case REST: + @Autowired + public VidController(VidService vidService) throws SdcToscaParserException{ - final String protocol = asdcClientConfig.getAsdcClientProtocol(); - final String host = asdcClientConfig.getAsdcClientHost(); - final int port = asdcClientConfig.getAsdcClientPort(); - final String auth = asdcClientConfig.getAsdcClientAuth(); - Client cl = null; - if ( protocol.equalsIgnoreCase("https") ) { - try { - SSLContext ctx = SSLContext.getInstance("TLSv1.2"); - ctx.init(null, null, null); - cl = ClientBuilder.newBuilder().sslContext(ctx).build(); - } - catch ( NoSuchAlgorithmException n ) { - throw new RuntimeException("SDC Client could not be instantiated due to unsupported protocol TLSv1.2", n); - } - catch ( KeyManagementException k ) { - throw new RuntimeException("SDC Client could not be instantiated due to a key management exception", k); - } - } - else { - cl = ClientBuilder.newBuilder().build(); - } - - try { - final URI uri = new URI(protocol + "://" + host + ":" + port + "/"); - return new RestfulAsdcClient.Builder(cl, uri) - .auth(auth) - .build(); - } catch (URISyntaxException e) { - throw new RuntimeException("SDC Client could not be instantiated due to a syntax error in the URI", e); - } - - default: - throw new RuntimeException(asdcClientConfig.getAsdcClientType() + " is invalid; must be one of " + Arrays.toString(AsdcClientType.values())); - } + service = vidService; } - +// /** * Gets the services. * @@ -168,7 +63,8 @@ public class VidController extends RestrictedBaseController { @RequestMapping(value={"/rest/models/services"}, method = RequestMethod.GET) public Collection getServices(HttpServletRequest request) throws VidServiceUnavailableException { try { - return getAsdcClient().getServices(request.getParameterMap()); + Map requestParams = request.getParameterMap(); + return service.getServices(requestParams); } catch (AsdcCatalogException e) { LOG.error("Failed to retrieve service definitions from SDC", e); throw new VidServiceUnavailableException("Failed to retrieve service definitions from SDC", e); @@ -188,159 +84,14 @@ public class VidController extends RestrictedBaseController { */ @RequestMapping(value={"/rest/models/services/{uuid}"}, method = RequestMethod.GET) public ServiceModel getServices(@PathVariable("uuid") String uuid) throws VidServiceUnavailableException { - String methodName = "getServices"; - LOG.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + methodName + " start"); - boolean isNewFlow = false; - - String asdcModelNamespaces[] = VidProperties.getAsdcModelNamespace(); - String[] vnfTags = ModelUtil.getTags(asdcModelNamespaces, ModelConstants.VNF); - String[] networkTags = ModelUtil.getTags(asdcModelNamespaces, ModelConstants.NETWORK); - String[] vfModuleTags = ModelUtil.getTags(asdcModelNamespaces, ModelConstants.VF_MODULE); - try { - final ServiceModel serviceModel = new ServiceModel(); - final Map vnfs = new HashMap (); - final Map networks = new HashMap (); - - final ToscaCsar serviceCsar = getAsdcClient().getServiceToscaModel(UUID.fromString(uuid)); - final Service asdcServiceMetadata = getAsdcClient().getService(UUID.fromString(uuid)); - final ToscaModel asdcServiceToscaModel = serviceCsar.getParent(); - - serviceModel.setService(ServiceModel.extractService(asdcServiceToscaModel, asdcServiceMetadata)); - - for (Entry component: asdcServiceToscaModel.gettopology_template().getnode_templates().entrySet()) { - final String modelCustomizationName = component.getKey(); - LOG.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + methodName + " model customization name: " + modelCustomizationName); - final NodeTemplate nodeTemplate = component.getValue(); - final String type = nodeTemplate.getType(); - - // is it a VNF? - if ( ModelUtil.isType (type, vnfTags) ) { - LOG.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + methodName + " found node template type: " + type); - - final UUID vnfUuid = UUID.fromString(nodeTemplate.getMetadata().getUUID()); - final VNF vnf = new VNF(); - vnf.extractVnf(modelCustomizationName, nodeTemplate); - - if (vnf.getVersion() == null) { - // vnf version should always be populated. The call below may not return the correct metadata since - // uuid is not unique - final Resource vnfMetadata = getAsdcClient().getResource(UUID.fromString(nodeTemplate.getMetadata().getUUID())); - vnf.setVersion(vnfMetadata.getVersion()); - } - - LOG.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + methodName + " VNF commands: " + vnf.getCommands()); - vnfs.put(modelCustomizationName, vnf); - if ( (vnf.getCustomizationUuid() != null) && (vnf.getCustomizationUuid().length() > 0 ) ) { - isNewFlow = true; - } - } - - // is it a Network? - if ( ModelUtil.isType (type, networkTags) ) { - LOG.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + methodName + " found node template type: " + type); - final UUID networkUuid = UUID.fromString(nodeTemplate.getMetadata().getUUID()); - final Network network = new Network(); - network.extractNetwork(modelCustomizationName, nodeTemplate); - - if (network.getVersion() == null) { - // network version should always be populated. The call below may not return the correct metadata since - // uuid is not unique - final Resource networkMetadata = getAsdcClient().getResource(UUID.fromString(nodeTemplate.getMetadata().getUUID())); - network.setVersion(networkMetadata.getVersion()); - } - if ( (network.getCustomizationUuid() != null) && (network.getCustomizationUuid().length() > 0 ) ) { - isNewFlow = true; - } - networks.put(modelCustomizationName, network); - - } - } - serviceModel.setVnfs(vnfs); - serviceModel.setNetworks(networks); - // If we see customization uuid under vnf or network, follow 1702 flow - if ( isNewFlow ) { - return ( getCustomizedServices(asdcServiceToscaModel, serviceModel) ); - } - VNF vnf = null; - for (ToscaModel vnfModel : serviceCsar.getChildren()) { - - // using uuid to match should only be valid for 1610 models - - final String vnfUuid = (vnfModel.getMetadata().getUUID()); - // find the VNF with that uuid, uuid is not the key anymore - for ( Entry vnfComp : vnfs.entrySet() ) { - if ( ( ( vnfComp.getValue().getUuid() ).equalsIgnoreCase(vnfUuid) ) ) { - // found the vnf - vnf = vnfComp.getValue(); - } - } - final Map vfModules = new HashMap (); - final Map volumeGroups = new HashMap (); - - if (vnf == null) { - LOG.warn("Couldn't find VNF object " + vnfUuid + ". Problem with Tosca model?"); - continue; - } - - vnf.setInputs(vnfModel.gettopology_template().getInputs()); - - for (Entry component1 : vnfModel.gettopology_template().getGroups().entrySet()) { - final Group group = component1.getValue(); - final String type = group.getType(); - final String modelCustomizationName = component1.getKey(); - - // VF Module Customization UUID: We may have the complete set of all VF Modules for all VNFs under service and VF Modules under each VNF. - // Keep using the VF Modules under VNFs but we need to get the customization uuid from the service level and put them - // under each VF module at the VNF level - if ( ModelUtil.isType (type, vfModuleTags) ) { - - VfModule vfMod = VfModule.extractVfModule(modelCustomizationName, group); - - // Add the vf module customization uuid from the service model - // The key of the VF Module in the service level will be the VF instance name appended to the VF Module name: - // .. - /* String normalizedVnfCustomizationName = VNF.normalizeName (vnf.getModelCustomizationName()); - org.openecomp.vid.model.Service.extractVfModuleCustomizationUUID (serviceModel.getService(), normalizedVnfCustomizationName, vfMod);*/ - - vfModules.put(modelCustomizationName, vfMod); - - if ( vfMod.isVolumeGroupAllowed() ) { - volumeGroups.put(modelCustomizationName, VolumeGroup.extractVolumeGroup(modelCustomizationName, group)); - } - - } - } - - vnf.setVfModules(vfModules); - vnf.setVolumeGroups(volumeGroups); - } - - serviceModel.setVnfs(vnfs); - serviceModel.setNetworks(networks); - - return serviceModel; + return service.getService(uuid); } catch (AsdcCatalogException e) { LOG.error("Failed to retrieve service definitions from SDC", e); throw new VidServiceUnavailableException("Failed to retrieve service definitions from SDC", e); } - catch (Exception e) { - LOG.error("Failed to retrieve service definitions from SDC", e); - throw new VidServiceUnavailableException("Failed to retrieve service definitions from SDC", e); - } } - public ServiceModel getCustomizedServices(ToscaModel asdcServiceToscaModel, ServiceModel serviceModel) { - String methodName = "asdcServiceToscaModel"; - LOG.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + methodName + " start"); - - // asdcServiceToscaModel should have vf modules and vol groups populated at this point but - // they are not associated with the VNFs - serviceModel.extractGroups(asdcServiceToscaModel); - // Now put the vf modules and volume groups under the VNF they belong too - serviceModel.associateGroups(); - return (serviceModel); - } /** * Gets the services view. diff --git a/vid-app-common/src/main/java/org/openecomp/vid/controller/WebConfig.java b/vid-app-common/src/main/java/org/openecomp/vid/controller/WebConfig.java new file mode 100644 index 000000000..9a258a109 --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/controller/WebConfig.java @@ -0,0 +1,106 @@ +package org.openecomp.vid.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.io.IOUtils; +import org.json.JSONObject; +import org.json.JSONTokener; +import org.openecomp.vid.asdc.AsdcClient; +import org.openecomp.vid.asdc.local.LocalAsdcClient; +import org.openecomp.vid.asdc.memory.InMemoryAsdcClient; +import org.openecomp.vid.asdc.rest.RestfulAsdcClient; +import org.openecomp.vid.asdc.parser.ToscaParserImpl2; +import org.openecomp.vid.properties.AsdcClientConfiguration; +import org.openecomp.vid.properties.AsdcClientConfiguration.AsdcClientType; +import org.openecomp.vid.services.VidService; +import org.openecomp.vid.services.VidServiceImpl; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import javax.net.ssl.SSLContext; +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; + +@Configuration +public class WebConfig { + + /** + * Gets the object mapper. + * + * @return the object mapper + */ + @Bean + public ObjectMapper getObjectMapper() { + return new ObjectMapper(); + } + + @Bean + public VidService vidService(AsdcClient asdcClient) { + return new VidServiceImpl(asdcClient); + } + + @Bean + public AsdcClient asdcClient(AsdcClientConfiguration asdcClientConfig) throws IOException { + switch (asdcClientConfig.getAsdcClientType()) { + case IN_MEMORY: + final InputStream asdcCatalogFile = VidController.class.getClassLoader().getResourceAsStream("catalog.json"); + final JSONTokener tokener = new JSONTokener(asdcCatalogFile); + final JSONObject catalog = new JSONObject(tokener); + + return new InMemoryAsdcClient.Builder().catalog(catalog).build(); + case REST: + + final String protocol = asdcClientConfig.getAsdcClientProtocol(); + final String host = asdcClientConfig.getAsdcClientHost(); + final int port = asdcClientConfig.getAsdcClientPort(); + final String auth = asdcClientConfig.getAsdcClientAuth(); + Client cl = null; + if (protocol.equalsIgnoreCase("https")) { + try { + SSLContext ctx = SSLContext.getInstance("TLSv1.2"); + ctx.init(null, null, null); + cl = ClientBuilder.newBuilder().sslContext(ctx).build(); + } catch (NoSuchAlgorithmException n) { + throw new RuntimeException("SDC Client could not be instantiated due to unsupported protocol TLSv1.2", n); + } catch (KeyManagementException k) { + throw new RuntimeException("SDC Client could not be instantiated due to a key management exception", k); + } + } else { + cl = ClientBuilder.newBuilder().build(); + } + + try { + final URI uri = new URI(protocol + "://" + host + ":" + port + "/"); + return new RestfulAsdcClient.Builder(cl, uri) + .auth(auth) + .build(); + } catch (URISyntaxException e) { + throw new RuntimeException("SDC Client could not be instantiated due to a syntax error in the URI", e); + } + + case LOCAL: + + final InputStream asdcServicesFile = VidController.class.getClassLoader().getResourceAsStream("sdcservices.json"); + + final JSONTokener jsonTokener = new JSONTokener(IOUtils.toString(asdcServicesFile)); + final JSONObject sdcServicesCatalog = new JSONObject(jsonTokener); + + return new LocalAsdcClient.Builder().catalog(sdcServicesCatalog).build(); + + default: + throw new RuntimeException(asdcClientConfig.getAsdcClientType() + " is invalid; must be one of " + Arrays.toString(AsdcClientType.values())); + } + } + + @Bean + public ToscaParserImpl2 getToscaParser() { + return new ToscaParserImpl2(); + } + +} diff --git a/vid-app-common/src/main/java/org/openecomp/vid/model/ModelUtil.java b/vid-app-common/src/main/java/org/openecomp/vid/model/ModelUtil.java deleted file mode 100755 index 4606aa021..000000000 --- a/vid-app-common/src/main/java/org/openecomp/vid/model/ModelUtil.java +++ /dev/null @@ -1,61 +0,0 @@ -/*- - * ============LICENSE_START======================================================= - * VID - * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. 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 org.openecomp.vid.model; - -/** - * The Class ModelUtil. - * - */ -public class ModelUtil { - /** - * Gets the tags for the given element according to the configured namespace - * @param namespaces the namespace list from the configuration - * @param constantValue the constant portion of the tag name, i.e. resource.vf... - * @return the tags - */ - public static String[] getTags ( String[] namespaces, String constantValue ) { - String[] tags; - if ( namespaces == null || namespaces.length == 0 ) { - return null; - } - int le = namespaces.length; - tags = new String[le]; - for ( int i = 0; i < le; i++ ) { - tags[i] = namespaces[i] + constantValue; - } - return (tags); - } - /** - * Determine if a note template type matches a set of configurable tags - * @param type the node template type - * @param tags the model configurable namespaces - * @return true if type starts with a tag in the array, false otherwise - */ - public static boolean isType ( String type, String[] tags ) { - if ( (tags != null) && (tags.length > 0) ) { - for ( int i = 0; i < tags.length; i++ ) { - if ( type.startsWith (tags[i]) ) { - return (true); - } - } - } - return (false); - } -} diff --git a/vid-app-common/src/main/java/org/openecomp/vid/model/Network.java b/vid-app-common/src/main/java/org/openecomp/vid/model/Network.java index 5ab55881b..e763496fc 100755 --- a/vid-app-common/src/main/java/org/openecomp/vid/model/Network.java +++ b/vid-app-common/src/main/java/org/openecomp/vid/model/Network.java @@ -49,7 +49,7 @@ public class Network extends Node { * * @param modelCustomizationName the new model customization name */ - private void setModelCustomizationName(String modelCustomizationName) { + public void setModelCustomizationName(String modelCustomizationName) { this.modelCustomizationName = modelCustomizationName; } diff --git a/vid-app-common/src/main/java/org/openecomp/vid/model/NewNetwork.java b/vid-app-common/src/main/java/org/openecomp/vid/model/NewNetwork.java new file mode 100644 index 000000000..48e3a55fd --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/model/NewNetwork.java @@ -0,0 +1,56 @@ +/*- + * ============LICENSE_START======================================================= + * VID + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. 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 org.openecomp.vid.model; + +import org.openecomp.vid.asdc.beans.tosca.NodeTemplate; + +/** + * The Class Network. + */ +public class NewNetwork extends NewNode { + + /** The model customization name. */ + private String modelCustomizationName; + + /** + * Instantiates a new network. + */ + public NewNetwork() { + super(); + } + /** + * Gets the model customization name. + * + * @return the model customization name + */ + public String getModelCustomizationName() { + return modelCustomizationName; + } + /** + * Sets the model customization name. + * + * @param modelCustomizationName the new model customization name + */ + public void setModelCustomizationName(String modelCustomizationName) { + this.modelCustomizationName = modelCustomizationName; + } + +} diff --git a/vid-app-common/src/main/java/org/openecomp/vid/model/NewNode.java b/vid-app-common/src/main/java/org/openecomp/vid/model/NewNode.java new file mode 100644 index 000000000..681131fc9 --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/model/NewNode.java @@ -0,0 +1,209 @@ +package org.openecomp.vid.model; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.HashMap; +import java.util.Map; + +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.vid.asdc.beans.tosca.Input; + + +public class NewNode { + + /** The Constant LOG. */ + private static final EELFLoggerDelegate LOG = EELFLoggerDelegate.getLogger(Node.class); + + /** The Constant dateFormat. */ + final static DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss:SSSS"); + + /** The uuid. */ + private String uuid; + + /** The invariant uuid. */ + private String invariantUuid; + + /** The description. */ + private String description; + + /** The name. */ + private String name; + + /** The version. */ + private String version; + + /** The model customization uuid. */ + private String customizationUuid; + + /** The inputs. */ + private Map inputs; + + /** The get_input or other constructs from node template properties. */ + private Map commands; + + /** The get_input or other constructs from node template properties. */ + private Map properties; + /** + * Instantiates a new node. + */ + public NewNode() { + this.commands = new HashMap(); + this.properties = new HashMap(); + } + + /** + * Gets the uuid. + * + * @return the uuid + */ + public String getUuid() { + return uuid; + } + + /** + * Gets the invariant uuid. + * + * @return the invariant uuid + */ + public String getInvariantUuid() { + return invariantUuid; + } + + /** + * Gets the description. + * + * @return the description + */ + public String getDescription() { + return description; + } + + /** + * Gets the name. + * + * @return the name + */ + public String getName() { + return name; + } + + /** + * Gets the version. + * + * @return the version + */ + public String getVersion() { + return version; + } + + /** + * Gets the customization uuid. + * + * @return the model customization uuid + */ + public String getCustomizationUuid() { + return customizationUuid; + } + /** + * Gets the inputs. + * + * @return the inputs + */ + public Map getInputs() { + return inputs; + } + /** + * Gets the commands. + * + * @return the commands + */ + public Map getCommands() { + return commands; + } + /** + * Gets the properties. + * + * @return the properties + */ + public Map getProperties() { + return properties; + } + /** + * Sets the uuid. + * + * @param uuid the new uuid + */ + public void setUuid(String uuid) { + this.uuid = uuid; + } + + /** + * Sets the invariant uuid. + * + * @param invariantUuid the new invariant uuid + */ + public void setInvariantUuid(String invariantUuid) { + this.invariantUuid = invariantUuid; + } + + /** + * Sets the description. + * + * @param description the new description + */ + public void setDescription(String description) { + this.description = description; + } + + /** + * Sets the name. + * + * @param name the new name + */ + public void setName(String name) { + this.name = name; + } + + /** + * Sets the version. + * + * @param version the new version + */ + public void setVersion(String version) { + this.version = version; + } + /** + * Sets the customization uuid. + * + * @param u the new customization uuid + */ + public void setCustomizationUuid(String u) { + this.customizationUuid = u; + } + + /** + * Sets the inputs. + * + * @param inputs the inputs + */ + public void setInputs(Map inputs) { + this.inputs = inputs; + } + /** + * Sets the commands. + * + * @param m the commands + */ + public void setCommands( Mapm ) { + commands = m; + } + /** + * Sets the properties. + * + * @param p the properties + */ + public void setProperties( Mapp) { + properties = p; + } + +} diff --git a/vid-app-common/src/main/java/org/openecomp/vid/model/NewService.java b/vid-app-common/src/main/java/org/openecomp/vid/model/NewService.java new file mode 100644 index 000000000..1dcf0224e --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/model/NewService.java @@ -0,0 +1,252 @@ +/*- + * ============LICENSE_START======================================================= + * VID + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. 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 org.openecomp.vid.model; + +import java.util.Map; +import java.util.UUID; + +import org.openecomp.vid.asdc.beans.tosca.Input; + +/** + * The Class Service. + */ +public class NewService { + + /** The uuid. */ + private String uuid; + + /** The invariant uuid. */ + private String invariantUuid; + + /** The name. */ + private String name; + + /** The version. */ + private String version; + + /** The tosca model URL. */ + private String toscaModelURL; + + /** The category. */ + private String category; + + /** The description. */ + private String description; + + /** The service ecomp naming flag */ + private String serviceEcompNaming; + + /** The inputs. */ + private Map inputs; + + /** + * Gets the uuid. + * + * @return the uuid + */ + public String getUuid() { + return uuid; + } + + /** + * Gets the invariant uuid. + * + * @return the invariant uuid + */ + public String getInvariantUuid() { + return invariantUuid; + } + + /** + * Gets the name. + * + * @return the name + */ + public String getName() { + return name; + } + + /** + * Gets the version. + * + * @return the version + */ + public String getVersion() { + return version; + } + + /** + * Gets the tosca model URL. + * + * @return the tosca model URL + */ + public String getToscaModelURL() { + return toscaModelURL; + } + + /** + * Gets the category. + * + * @return the category + */ + public String getCategory() { + return category; + } + + /** + * Gets the description. + * + * @return the description + */ + public String getDescription() { + return description; + } + + /** + * Gets the inputs. + * + * @return the inputs + */ + public Map getInputs() { + return inputs; + } + /** + * Get the serviceEcompNaming value + * + * @return serviceEcompNaming + */ + public String getServiceEcompNaming() { + return serviceEcompNaming; + } + /** + * Sets the uuid. + * + * @param uuid the new uuid + */ + public void setUuid(String uuid) { + this.uuid = uuid; + } + + /** + * Sets the invariant uuid. + * + * @param invariantUuid the new invariant uuid + */ + public void setInvariantUuid(String invariantUuid) { + this.invariantUuid = invariantUuid; + } + + /** + * Sets the name. + * + * @param name the new name + */ + public void setName(String name) { + this.name = name; + } + + /** + * Sets the version. + * + * @param version the new version + */ + public void setVersion(String version) { + this.version = version; + } + + /** + * Sets the tosca model URL. + * + * @param toscaModelURL the new tosca model URL + */ + public void setToscaModelURL(String toscaModelURL) { + this.toscaModelURL = toscaModelURL; + } + + /** + * Sets the category. + * + * @param category the new category + */ + public void setCategory(String category) { + this.category = category; + } + + /** + * Sets the description. + * + * @param description the new description + */ + public void setDescription(String description) { + this.description = description; + } + + /** + * Sets the inputs. + * + * @param inputs the inputs + */ + public void setInputs(Map inputs) { + this.inputs = inputs; + } + /** + * Sets the service ecomp naming. + * + * @param serviceEcompNaming the new service ecomp naming + */ + public void setServiceEcompNaming(String serviceEcompNaming) { + this.serviceEcompNaming = serviceEcompNaming; + } + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final UUID uuid = UUID.fromString(getUuid()); + + return uuid.hashCode(); + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object o) { + if (o == this) return true; + if (!(o instanceof NewService)) return false; + + final NewService service = (NewService) o; + + return (service.getUuid().equals(getUuid())); + } + /*public static void extractVfModuleCustomizationUUID (Service s, String vnfCustomizationName, VfModule vfMod ) { + + //Look for vnfCustomizationName..vfModuleCustomizationName + String nameToFind = vnfCustomizationName + ".." + vfMod.getModelCustomizationName(); + for (Entry vfModuleComponent : s.getVfModules().entrySet()) { + VfModule xMod = vfModuleComponent.getValue(); + if ( (xMod.getModelCustomizationName() != null) && (xMod.getModelCustomizationName().equalsIgnoreCase(nameToFind)) ) { + vfMod.setCustomizationUuid( xMod.getCustomizationUuid()); + return; + } + } + }*/ +} diff --git a/vid-app-common/src/main/java/org/openecomp/vid/model/NewServiceModel.java b/vid-app-common/src/main/java/org/openecomp/vid/model/NewServiceModel.java new file mode 100644 index 000000000..6fda979c1 --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/model/NewServiceModel.java @@ -0,0 +1,259 @@ +/*- + * ============LICENSE_START======================================================= + * VID + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. 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 org.openecomp.vid.model; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import org.openecomp.vid.asdc.beans.tosca.Group; +import org.openecomp.vid.asdc.beans.tosca.ToscaModel; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.vid.properties.VidProperties; +/** + * The Class ServiceModel. + */ +public class NewServiceModel { + + /** The Constant LOG. */ + private static final EELFLoggerDelegate LOG = EELFLoggerDelegate.getLogger(NewServiceModel.class); + + /** The Constant dateFormat. */ + final static DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss:SSSS"); + /** The service. */ + private Service service; + + /** The vnfs. */ + private Map vnfs; + + /** The networks. */ + private Map networks; + + /** + * The vf modules. The VNF also has vfmodules but the vfmodules at the service level may have additional info + * that is not present in the VNF, like the vf module customization String + */ + private Map vfModules; + /** + * The volume groups. The VNF also has volume groups but the volume groups will be populated at the service level + * for newer models + */ + private Map volumeGroups; + /** + * Instantiates a new service model. + */ + public NewServiceModel() {} + + /** + * Gets the service. + * + * @return the service + */ + public Service getService() { + return service; + } + + /** + * Gets the vnfs. + * + * @return the vnfs + */ + public Map getVnfs() { + return vnfs; + } + + /** + * Gets the networks. + * + * @return the networks + */ + public Map getNetworks() { + return networks; + } + + /** + * Sets the service. + * + * @param service the new service + */ + public void setService(Service service) { + this.service = service; + } + + /** + * Sets the vnfs. + * + * @param vnfs the vnfs + */ + public void setVnfs(Map vnfs) { + this.vnfs = vnfs; + } + + /** + * Sets the networks. + * + * @param networks the networks + */ + public void setNetworks(Map networks) { + this.networks = networks; + } + /** + * Gets the vf modules. + * + * @return the vf modules + */ + public Map getVfModules() { + return vfModules; + } + /** + * Gets the volume groups. + * + * @return the volume groups + */ + public Map getVolumeGroups() { + return volumeGroups; + } + /** + * Sets the vf modules. + * + * @param vfModules the vf modules + */ + public void setVfModules(Map vfModules) { + this.vfModules = vfModules; + } + /** + * Sets the volume groups. + * + * @param volumeGroups the volume groups + */ + public void setVolumeGroups(Map volumeGroups) { + this.volumeGroups = volumeGroups; + } + /** + * Extract service. + * + * @param serviceToscaModel the service tosca model + * @param asdcServiceMetadata the asdc service metadata + * @return the service + */ + public static Service extractService(ToscaModel serviceToscaModel, org.openecomp.vid.asdc.beans.Service asdcServiceMetadata) { + + final Service service = new Service(); + + service.setCategory(serviceToscaModel.getMetadata().getCategory()); + service.setInvariantUuid(serviceToscaModel.getMetadata().getInvariantUUID()); + service.setName(serviceToscaModel.getMetadata().getName()); + service.setUuid(serviceToscaModel.getMetadata().getUUID()); + service.setDescription(serviceToscaModel.getMetadata().getDescription()); + service.setServiceEcompNaming(serviceToscaModel.getMetadata().getServiceEcompNaming()); + service.setInputs(serviceToscaModel.gettopology_template().getInputs()); + //FIXME: SDC is not sending the Version with the Tosca Model for 1610 - they should send it in 1702 + //THIS IS A TEMPORARY FIX, AT SOME POINT UNCOMMENT ME + //service.setVersion(serviceToscaModel.getMetadata().getVersion()); + service.setVersion(asdcServiceMetadata.getVersion()); + + return service; + } + public static void extractGroups (ToscaModel serviceToscaModel,NewServiceModel serviceModel) { + // Get the groups. The groups may duplicate the groups that are in the VNF model and have + // additional data like the VF module customization String> + + final Map vfModules = new HashMap (); + final Map volumeGroups = new HashMap (); + + String asdcModelNamespace = VidProperties.getAsdcModelNamespace(); + String vfModuleTag = asdcModelNamespace + ModelConstants.VF_MODULE; + + for (Entry component : serviceToscaModel.gettopology_template().getGroups().entrySet()) { + final Group group = component.getValue(); + final String type = group.getType(); + final String customizationName = component.getKey(); + + if (type.startsWith(vfModuleTag)) { + VfModule vfMod = VfModule.extractVfModule(customizationName, group); + vfModules.put(customizationName, vfMod); + if ( vfMod.isVolumeGroupAllowed() ) { + //volume groups have the same customization name as the vf module + volumeGroups.put(customizationName, VolumeGroup.extractVolumeGroup(customizationName,group)); + } + } + } + // add this point vfModules and volume groups are disconnected from VNF + serviceModel.setVfModules (vfModules); + serviceModel.setVolumeGroups (volumeGroups); + + } + /** + * Populate the vf modules and volume groups that we may have under the service level under each VNF. + */ +// public void associateGroups() { +// String methodName = "associateGroups()"; +// LOG.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + methodName + " start"); +// // go through the vnfs, get the vnf normalized name and look for a vf module with a customization name that starts +// // with vnf + ".." +// String vnfCustomizationName = null; +// String normalizedVnfCustomizationName = null; +// String vfModuleCustomizationName = null; +// NewVNF tmpVnf = null; +// +// if ( ( getVnfs() != null ) && (!(getVnfs().isEmpty())) ) { +// for (Entry vnfComponent : getVnfs().entrySet()) { +// vnfCustomizationName = vnfComponent.getValue().getModelCustomizationName(); +// normalizedVnfCustomizationName = VNF.normalizeName(vnfCustomizationName); +// +// LOG.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + methodName + +// " VNF customizationName=" + vnfCustomizationName + "normalized customization name=" + normalizedVnfCustomizationName); +// +// // now check to see if there is a vf module with customization name that starts with normalizedVnfCustomizationName +// +// if (( getVfModules() != null ) && (!(getVfModules().isEmpty()))) { +// for (Entry vfModuleComponent : getVfModules().entrySet()) { +// vfModuleCustomizationName = vfModuleComponent.getValue().getModelCustomizationName(); +// +// LOG.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + methodName + +// " VF Module customizationName=" + vfModuleCustomizationName ); +// if ( vfModuleCustomizationName.startsWith(normalizedVnfCustomizationName + ".." )) { +// +// // this vf module belongs to the VNF +// tmpVnf = vnfComponent.getValue(); +// (tmpVnf.getVfModules()).put(vfModuleComponent.getKey(), vfModuleComponent.getValue()); +// +// LOG.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + methodName + +// " Associated VF Module customizationName=" + vfModuleComponent.getKey() + " with VNF customization name=" + vnfCustomizationName); +// +// // now find if this vf module has volume groups, if so, find the volume group with the same customization name and put it under the VNF +// if ( vfModuleComponent.getValue().isVolumeGroupAllowed() ) { +// if (( getVolumeGroups() != null ) && (!(getVolumeGroups().isEmpty()))) { +// if (getVolumeGroups().containsKey((vfModuleCustomizationName))) { +// (vnfComponent.getValue().getVolumeGroups()).put(vfModuleCustomizationName, (getVolumeGroups()).get(vfModuleCustomizationName)); +// } +// } +// } +// } +// } +// } +// } +// } + +// } +} diff --git a/vid-app-common/src/main/java/org/openecomp/vid/model/NewVNF.java b/vid-app-common/src/main/java/org/openecomp/vid/model/NewVNF.java new file mode 100644 index 000000000..e84f963c5 --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/model/NewVNF.java @@ -0,0 +1,123 @@ +package org.openecomp.vid.model; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Pattern; + +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.vid.asdc.beans.tosca.NodeTemplate; + +public class NewVNF extends NewNode { + + /** The Constant LOG. */ + private static final EELFLoggerDelegate LOG = EELFLoggerDelegate.getLogger(VNF.class); + + /** The Constant dateFormat. */ + final static DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss:SSSS"); + + /** The pattern used to normalize VNF names */ + final static Pattern COMPONENT_INSTANCE_NAME_DELIMETER_PATTERN = Pattern.compile("[\\.\\-]+"); + + /** The model customization name. */ + private String modelCustomizationName; + + /** The vf modules. */ + private Map vfModules = new HashMap(); + + /** The volume groups. */ + private Map volumeGroups = new HashMap(); + + /** + * Instantiates a newvnf. + */ + public NewVNF() { + super(); + } + + /** + * Gets the model customization name. + * + * @return the model customization name + */ + public String getModelCustomizationName() { + return modelCustomizationName; + } + + /** + * Gets the vf modules. + * + * @return the vf modules + */ + public Map getVfModules() { + return vfModules; + } + + /** + * Sets the vf modules. + * + * @param vfModules the vf modules + */ + public void setVfModules(Map vfModules) { + this.vfModules = vfModules; + } + + /** + * Gets the volume groups. + * + * @return the volume groups + */ + public Map getVolumeGroups() { + return volumeGroups; + } + + /** + * Sets the volume groups. + * + * @param volumeGroups the volume groups + */ + public void setVolumeGroups(Map volumeGroups) { + this.volumeGroups = volumeGroups; + } + + + /** + * Sets the model customization name. + * + * @param modelCustomizationName the new model customization name + */ + public void setModelCustomizationName(String modelCustomizationName) { + this.modelCustomizationName = modelCustomizationName; + } + /** + * Normalize the VNF name + * @param originalName + * @return the normalized name + */ + public static String normalizeName (String originalName) { + + String normalizedName = originalName.toLowerCase(); + normalizedName = COMPONENT_INSTANCE_NAME_DELIMETER_PATTERN.matcher(normalizedName).replaceAll(" "); + String[] splitArr = null; + + try { + splitArr = normalizedName.split(" "); + } + catch (Exception ex ) { + return (normalizedName); + } + StringBuffer sb = new StringBuffer(); + if ( splitArr != null ) { + for (String splitElement : splitArr) { + sb.append(splitElement); + } + return (sb.toString()); + } + else { + return (normalizedName); + } + + } + +} diff --git a/vid-app-common/src/main/java/org/openecomp/vid/model/ServiceModel.java b/vid-app-common/src/main/java/org/openecomp/vid/model/ServiceModel.java index 98e65316b..8742931c6 100755 --- a/vid-app-common/src/main/java/org/openecomp/vid/model/ServiceModel.java +++ b/vid-app-common/src/main/java/org/openecomp/vid/model/ServiceModel.java @@ -30,11 +30,11 @@ import java.util.Map.Entry; import org.openecomp.vid.asdc.beans.tosca.Group; import org.openecomp.vid.asdc.beans.tosca.ToscaModel; import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; -import org.openecomp.vid.controller.VidController; import org.openecomp.vid.properties.VidProperties; /** * The Class ServiceModel. */ +@SuppressWarnings("ALL") public class ServiceModel { /** The Constant LOG. */ @@ -176,21 +176,22 @@ public class ServiceModel { return service; } - public void extractGroups (ToscaModel serviceToscaModel) { + public static void extractGroups (ToscaModel serviceToscaModel,ServiceModel serviceModel) { // Get the groups. The groups may duplicate the groups that are in the VNF model and have // additional data like the VF module customization String> final Map vfModules = new HashMap (); final Map volumeGroups = new HashMap (); - String asdcModelNamespaces[] = VidProperties.getAsdcModelNamespace(); - String[] vfModuleTags = ModelUtil.getTags(asdcModelNamespaces, ModelConstants.VF_MODULE); - + + String asdcModelNamespace = VidProperties.getAsdcModelNamespace(); + String vfModuleTag = asdcModelNamespace + ModelConstants.VF_MODULE; + for (Entry component : serviceToscaModel.gettopology_template().getGroups().entrySet()) { final Group group = component.getValue(); final String type = group.getType(); final String customizationName = component.getKey(); - if ( ModelUtil.isType (type, vfModuleTags) ) { + if (type.startsWith(vfModuleTag)) { VfModule vfMod = VfModule.extractVfModule(customizationName, group); vfModules.put(customizationName, vfMod); if ( vfMod.isVolumeGroupAllowed() ) { @@ -200,8 +201,8 @@ public class ServiceModel { } } // add this point vfModules and volume groups are disconnected from VNF - this.setVfModules (vfModules); - this.setVolumeGroups (volumeGroups); + serviceModel.setVfModules (vfModules); + serviceModel.setVolumeGroups (volumeGroups); } /** diff --git a/vid-app-common/src/main/java/org/openecomp/vid/model/VNF.java b/vid-app-common/src/main/java/org/openecomp/vid/model/VNF.java index be37c947b..62e101ef8 100755 --- a/vid-app-common/src/main/java/org/openecomp/vid/model/VNF.java +++ b/vid-app-common/src/main/java/org/openecomp/vid/model/VNF.java @@ -130,7 +130,7 @@ public class VNF extends Node { * * @param modelCustomizationName the new model customization name */ - private void setModelCustomizationName(String modelCustomizationName) { + public void setModelCustomizationName(String modelCustomizationName) { this.modelCustomizationName = modelCustomizationName; } /** diff --git a/vid-app-common/src/main/java/org/openecomp/vid/model/VfModule.java b/vid-app-common/src/main/java/org/openecomp/vid/model/VfModule.java index 4a031a6c6..3f6f1da22 100755 --- a/vid-app-common/src/main/java/org/openecomp/vid/model/VfModule.java +++ b/vid-app-common/src/main/java/org/openecomp/vid/model/VfModule.java @@ -207,7 +207,7 @@ public class VfModule { * * @param volumeGroupAllowed the new volume group allowed */ - private void setVolumeGroupAllowed(boolean volumeGroupAllowed) { + public void setVolumeGroupAllowed(boolean volumeGroupAllowed) { this.volumeGroupAllowed = volumeGroupAllowed; } /** @@ -223,7 +223,7 @@ public class VfModule { * * @param modelCustomizationName the new model customization name */ - private void setModelCustomizationName(String modelCustomizationName) { + public void setModelCustomizationName(String modelCustomizationName) { this.modelCustomizationName = modelCustomizationName; } /** diff --git a/vid-app-common/src/main/java/org/openecomp/vid/model/VolumeGroup.java b/vid-app-common/src/main/java/org/openecomp/vid/model/VolumeGroup.java index ea8e151d6..d09ef9a33 100755 --- a/vid-app-common/src/main/java/org/openecomp/vid/model/VolumeGroup.java +++ b/vid-app-common/src/main/java/org/openecomp/vid/model/VolumeGroup.java @@ -170,7 +170,7 @@ public class VolumeGroup { * * @param u the new customization name */ - private void setModelCustomizationName(String u) { + public void setModelCustomizationName(String u) { this.modelCustomizationName = u; } diff --git a/vid-app-common/src/main/java/org/openecomp/vid/mso/MsoRestInterfaceIfc.java b/vid-app-common/src/main/java/org/openecomp/vid/mso/MsoRestInterfaceIfc.java index b0dc906be..1a6dbcccb 100755 --- a/vid-app-common/src/main/java/org/openecomp/vid/mso/MsoRestInterfaceIfc.java +++ b/vid-app-common/src/main/java/org/openecomp/vid/mso/MsoRestInterfaceIfc.java @@ -70,7 +70,7 @@ public interface MsoRestInterfaceIfc { */ public void Post(T t, RequestDetails r, String sourceID, String path, RestObject restObject) throws Exception; - /** + /*** * Log request. * * @param r the r diff --git a/vid-app-common/src/main/java/org/openecomp/vid/properties/AsdcClientConfiguration.java b/vid-app-common/src/main/java/org/openecomp/vid/properties/AsdcClientConfiguration.java index 6d5f9522c..232023b4e 100755 --- a/vid-app-common/src/main/java/org/openecomp/vid/properties/AsdcClientConfiguration.java +++ b/vid-app-common/src/main/java/org/openecomp/vid/properties/AsdcClientConfiguration.java @@ -24,31 +24,36 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; +import org.springframework.context.annotation.PropertySources; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; /** * The Class AsdcClientConfiguration. */ @Configuration -@PropertySource(value="${container.classpath:}/WEB-INF/conf/asdc.properties") + +@PropertySources({ + @PropertySource(value="asdc.properties", ignoreResourceNotFound = true), + @PropertySource(value="${container.classpath:}/WEB-INF/conf/asdc.properties", ignoreResourceNotFound = true) +}) public class AsdcClientConfiguration { - - @Bean - public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() { - return new PropertySourcesPlaceholderConfigurer(); + + @Bean + public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() { + return new PropertySourcesPlaceholderConfigurer(); } @Value("${asdc.client.type}") private AsdcClientType asdcClientType; - + /** The asdc client host. */ @Value("${asdc.client.rest.host}") private String asdcClientHost; - + /** The asdc client port. */ @Value("${asdc.client.rest.port}") private int asdcClientPort; - + /** The asdc client auth. */ @Value("${asdc.client.rest.auth}") public String asdcClientAuth; @@ -56,7 +61,7 @@ public class AsdcClientConfiguration { /** The asdc client protocol. */ @Value("${asdc.client.rest.protocol}") public String asdcClientProtocol; - + /** * Gets the asdc client type. * @@ -101,16 +106,19 @@ public class AsdcClientConfiguration { public String getAsdcClientProtocol() { return asdcClientProtocol; } - + /** * The Enum AsdcClientType. */ public enum AsdcClientType { - + /** The in memory. */ IN_MEMORY, - + /** The rest. */ - REST + REST, + + /** The local. */ + LOCAL } } diff --git a/vid-app-common/src/main/java/org/openecomp/vid/properties/VidProperties.java b/vid-app-common/src/main/java/org/openecomp/vid/properties/VidProperties.java index 38062df40..141b9b27c 100755 --- a/vid-app-common/src/main/java/org/openecomp/vid/properties/VidProperties.java +++ b/vid-app-common/src/main/java/org/openecomp/vid/properties/VidProperties.java @@ -53,27 +53,21 @@ public class VidProperties extends SystemProperties { * * @return the property value or a default value */ - public static String[] getAsdcModelNamespace() { + public static String getAsdcModelNamespace() { String methodName = "getAsdcModelNamespace "; - String[] asdcModelNamespaces = null; - String value = null; + String asdcModelNamespace = ModelConstants.DEFAULT_ASDC_MODEL_NAMESPACE; try { - value = SystemProperties.getProperty(ModelConstants.ASDC_MODEL_NAMESPACE); - if ( value == null || value.isEmpty()) { - asdcModelNamespaces = new String[1]; - asdcModelNamespaces[0] = ModelConstants.DEFAULT_ASDC_MODEL_NAMESPACE; + asdcModelNamespace = SystemProperties.getProperty(ModelConstants.ASDC_MODEL_NAMESPACE); + if ( asdcModelNamespace == null || asdcModelNamespace.isEmpty()) { + asdcModelNamespace = ModelConstants.DEFAULT_ASDC_MODEL_NAMESPACE; } - else { - asdcModelNamespaces = value.split(","); - } } catch ( Exception e ) { LOG.error (EELFLoggerDelegate.errorLogger, dateFormat.format(new Date()) + methodName + "unable to find the value, using the default " + ModelConstants.DEFAULT_ASDC_MODEL_NAMESPACE); - asdcModelNamespaces = new String[1]; - asdcModelNamespaces[0] = ModelConstants.DEFAULT_ASDC_MODEL_NAMESPACE; + asdcModelNamespace = ModelConstants.DEFAULT_ASDC_MODEL_NAMESPACE; } - return (asdcModelNamespaces); + return (asdcModelNamespace); } /** * Gets the specified property value. If the property is not defined, returns a default value. @@ -96,5 +90,4 @@ public class VidProperties extends SystemProperties { } return (propValue); } - } diff --git a/vid-app-common/src/main/java/org/openecomp/vid/services/VidService.java b/vid-app-common/src/main/java/org/openecomp/vid/services/VidService.java new file mode 100644 index 000000000..4fb0ff160 --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/services/VidService.java @@ -0,0 +1,17 @@ +package org.openecomp.vid.services; + +import org.openecomp.vid.asdc.AsdcCatalogException; +import org.openecomp.vid.asdc.beans.Service; +import org.openecomp.vid.model.ServiceModel; + +import java.util.Collection; +import java.util.Map; + +public interface VidService { + + Collection getServices(Map requestParams) + throws AsdcCatalogException; + + ServiceModel getService(String uuid) throws AsdcCatalogException; + +} \ No newline at end of file diff --git a/vid-app-common/src/main/java/org/openecomp/vid/services/VidServiceImpl.java b/vid-app-common/src/main/java/org/openecomp/vid/services/VidServiceImpl.java new file mode 100644 index 000000000..9844842f8 --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/services/VidServiceImpl.java @@ -0,0 +1,79 @@ +package org.openecomp.vid.services; + +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.sdc.tosca.parser.exceptions.SdcToscaParserException; +import org.openecomp.vid.asdc.AsdcCatalogException; +import org.openecomp.vid.asdc.AsdcClient; +import org.openecomp.vid.asdc.beans.Service; +import org.openecomp.vid.asdc.parser.ToscaParser; +import org.openecomp.vid.asdc.parser.ToscaParserImpl; +import org.openecomp.vid.asdc.parser.ToscaParserImpl2; +import org.openecomp.vid.model.ServiceModel; +import org.springframework.beans.factory.annotation.Autowired; + +import java.nio.file.Path; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Collection; +import java.util.Map; +import java.util.UUID; + +/** + * The Class VidController. + */ + +public class VidServiceImpl implements VidService { + /** + * The Constant LOG. + */ + private static final EELFLoggerDelegate LOG = EELFLoggerDelegate.getLogger(VidServiceImpl.class); + /** + * The Constant dateFormat. + */ + private final static DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss:SSSS"); + protected final AsdcClient asdcClient; + @Autowired + private ToscaParserImpl2 toscaParser; + + public VidServiceImpl(AsdcClient asdcClient) { + this.asdcClient = asdcClient; + } + + /* + * (non-Javadoc) + * + * @see org.openecomp.vid.controller.VidService#getServices(java.util.Map) + */ + @Override + public Collection getServices(Map requestParams) + throws AsdcCatalogException { + return asdcClient.getServices(requestParams); + } + + /* + * (non-Javadoc) + * + * @see org.openecomp.vid.controller.VidService#getService(java.lang.String) + */ + @Override + public ServiceModel getService(String uuid) throws AsdcCatalogException { + final Path serviceCsar = asdcClient.getServiceToscaModel(UUID.fromString(uuid)); + ToscaParser tosca = new ToscaParserImpl(); + serviceCsar.toFile().getAbsolutePath(); + ServiceModel serviceModel = null; + try { + final Service asdcServiceMetadata = asdcClient.getService(UUID.fromString(uuid)); + try { + serviceModel = toscaParser.makeServiceModel(serviceCsar, asdcServiceMetadata); + } + catch (SdcToscaParserException e){ + serviceModel = tosca.makeServiceModel(uuid, serviceCsar, asdcServiceMetadata); + } + } catch (Exception e) { + e.printStackTrace(); + } + return serviceModel; + } + + +} \ No newline at end of file diff --git a/vid-app-common/src/main/resources/catalog.json b/vid-app-common/src/main/resources/catalog.json new file mode 100644 index 000000000..20c7d5769 --- /dev/null +++ b/vid-app-common/src/main/resources/catalog.json @@ -0,0 +1,174 @@ +{ + "services": { + "0346aa9f-57b7-458a-9681-daf5b19d52b0": { + "uuid": "0346aa9f-57b7-458a-9681-daf5b19d52b0", + "name": "The Worst Service", + "version": "1.0", + "toscaModelURL": "sampleTosca.csar", + "category": "Bad Services", + "lifecycleState": "NOT_CERTIFIED_CHECKOUT", + "lastUpdaterUserId": "example@example.org", + "lastUpdaterFullName": "Example User", + "distributionStatus": "DISTRIBUTION_REJECTED", + "artifacts": [], + "resources": [] + }, + "1346aa9f-57b7-458a-9681-daf5b19d52b1": { + "uuid": "1346aa9f-57b7-458a-9681-daf5b19d52b1", + "name": "The Worst Service", + "version": "1.1", + "toscaModelURL": "sampleTosca.csar", + "category": "Bad Services", + "lifecycleState": "CERTIFIED", + "lastUpdaterUserId": "example@example.org", + "lastUpdaterFullName": "Example User", + "distributionStatus": "DISTRIBUTED", + "artifacts": [], + "resources": [] + }, + "3346aa9f-57b7-458a-9681-daf5b19d52b3": { + "uuid": "3346aa9f-57b7-458a-9681-daf5b19d52b3", + "name": "Bland Service", + "version": "1.9", + "toscaModelURL": "sampleTosca.csar", + "category": "Neutral Services", + "lifecycleState": "CERTIFIED", + "lastUpdaterUserId": "example@example.org", + "lastUpdaterFullName": "Example User", + "distributionStatus": "DISTRIBUTION_NOT_APPROVED", + "artifacts": [], + "resources": [] + }, + "2346aa9f-57b7-458a-9681-daf5b19d52b2": { + "uuid": "2346aa9f-57b7-458a-9681-daf5b19d52b2", + "name": "The Best Service", + "version": "1.3", + "toscaModelURL": "sampleTosca.csar", + "category": "Good Services", + "lifecycleState": "CERTIFIED", + "lastUpdaterUserId": "example@example.org", + "lastUpdaterFullName": "Example User", + "distributionStatus": "DISTRIBUTION_APPROVED", + "artifacts": [ + { + "artifactUUID": "0cf78c81-1246-45e7-a190-eaa309ee5680", + "generatedFromUUID": "3cf78c81-1246-45e7-a190-eaa309ee5680", + "artifactName": "The Worst Artifact", + "artifactType": "HEAT", + "artifactDescription": "This is the worst artifact", + "artifactURL": "http://www.openecomp.org/", + "artifactTimeout": "60", + "artifactChecksum": "A worthy checksum", + "artifactVersion": "0.1" + }, + { + "artifactUUID": "1cf78c81-1246-45e7-a190-eaa309ee5681", + "generatedFromUUID": "3cf78c81-1246-45e7-a190-eaa309ee5680", + "artifactName": "The Worst Artifact", + "artifactType": "HEAT", + "artifactDescription": "This is the worst artifact", + "artifactURL": "http://www.openecomp.org/", + "artifactTimeout": "60", + "artifactChecksum": "A worthy checksum", + "artifactVersion": "0.1" + }, + { + "artifactUUID": "2cf78c81-1246-45e7-a190-eaa309ee5682", + "generatedFromUUID": "3cf78c81-1246-45e7-a190-eaa309ee5680", + "artifactName": "The Worst Artifact", + "artifactType": "HEAT", + "artifactDescription": "This is the worst artifact", + "artifactURL": "http://www.openecomp.org/", + "artifactTimeout": "60", + "artifactChecksum": "A worthy checksum", + "artifactVersion": "0.1" + } + ], + "resources": [] + } + }, + "resources": { + "2f92b5b0-10ff-4cf4-9531-88546fe88a42": { + "uuid": "2f92b5b0-10ff-4cf4-9531-88546fe88a42", + "invariantUUID": "df92b5b0-10ff-4cf4-9531-88546fe88a4d", + "name": "The Worst Resource", + "version": "0.1", + "toscaModelURL": "sampleTosca.csar", + "toscaModel": "http://www.openecomp.org/", + "toscaResourceName": "The Worst Resource (TOSCA)", + "category": "Bad Resources", + "subCategory": "Really Bad Resources", + "resourceType": "VF", + "lifecycleState": "CERTIFIED", + "lastUpdaterUserId": "example@example.org", + "lastUpdaterFullName": "Example User", + "artifacts": [ + { + "artifactUUID": "0cf78c81-1246-45e7-a190-eaa309ee5680", + "generatedFromUUID": "3cf78c81-1246-45e7-a190-eaa309ee5680", + "artifactName": "The Worst Artifact", + "artifactType": "HEAT", + "artifactDescription": "This is the worst artifact", + "artifactURL": "http://www.openecomp.org/", + "artifactTimeout": "60", + "artifactChecksum": "A worthy checksum", + "artifactVersion": "0.1" + }, + { + "artifactUUID": "1cf78c81-1246-45e7-a190-eaa309ee5681", + "generatedFromUUID": "3cf78c81-1246-45e7-a190-eaa309ee5680", + "artifactName": "The Worst Artifact", + "artifactType": "HEAT", + "artifactDescription": "This is the worst artifact", + "artifactURL": "http://www.openecomp.org/", + "artifactTimeout": "60", + "artifactChecksum": "A worthy checksum", + "artifactVersion": "0.1" + }, + { + "artifactUUID": "2cf78c81-1246-45e7-a190-eaa309ee5682", + "generatedFromUUID": "3cf78c81-1246-45e7-a190-eaa309ee5680", + "artifactName": "The Worst Artifact", + "artifactType": "HEAT", + "artifactDescription": "This is the worst artifact", + "artifactURL": "http://www.openecomp.org/", + "artifactTimeout": "60", + "artifactChecksum": "A worthy checksum", + "artifactVersion": "0.1" + } + ] + }, + "0f92b5b0-10ff-4cf4-9531-88546fe88a40": { + "uuid": "0f92b5b0-10ff-4cf4-9531-88546fe88a40", + "invariantUUID": "df92b5b0-10ff-4cf4-9531-88546fe88a4d", + "name": "The Worst Resource", + "version": "0.1", + "toscaModelURL": "sampleTosca.csar", + "toscaModel": "http://www.openecomp.org/", + "toscaResourceName": "The Worst Resource (TOSCA)", + "category": "Bad Resources", + "subCategory": "Really Bad Resources", + "resourceType": "VF", + "lifecycleState": "CERTIFIED", + "lastUpdaterUserId": "example@example.org", + "lastUpdaterFullName": "Example User", + "artifacts": [] + }, + "1f92b5b0-10ff-4cf4-9531-88546fe88a41": { + "uuid": "1f92b5b0-10ff-4cf4-9531-88546fe88a41", + "invariantUUID": "df92b5b0-10ff-4cf4-9531-88546fe88a4d", + "name": "The Worst Resource", + "version": "0.1", + "toscaModelURL": "sampleTosca.csar", + "toscaModel": "http://www.openecomp.org/", + "toscaResourceName": "The Worst Resource (TOSCA)", + "category": "Bad Resources", + "subCategory": "Really Bad Resources", + "resourceType": "VF", + "lifecycleState": "CERTIFIED", + "lastUpdaterUserId": "example@example.org", + "lastUpdaterFullName": "Example User", + "artifacts": [] + } + } +} \ No newline at end of file diff --git a/vid-app-common/src/main/resources/sdcservices.json b/vid-app-common/src/main/resources/sdcservices.json new file mode 100644 index 000000000..9ef37c303 --- /dev/null +++ b/vid-app-common/src/main/resources/sdcservices.json @@ -0,0 +1,29 @@ +{ + "services": [ + { + "uuid": "f430728a-4530-42be-a577-1206b9484cef", + "invariantUUID": "f430728a-4530-42be-a577-1206b9484cef", + "name": "1707vidnf", + "version": "1.0", + "toscaModelURL": "./service-vf-csar.zip", + "category": "Mobility", + "lifecycleState": "CERTIFIED", + "lastUpdaterUserId": "sa997j", + "distributionStatus": "DISTRIBUTED" + }, + { + "uuid": "f430728a-4530-42be-a577-1206b9484cef", + "invariantUUID": "f430728a-4530-42be-a577-1206b9484cef", + "name": "4-27_vMME_Service", + "version": "1.0", + "toscaModelURL": "./service-vf-csar.zip", + "category": "Mobility", + "lifecycleState": "CERTIFIED", + "lastUpdaterUserId": "rg276b", + "lastUpdaterFullName": null, + "distributionStatus": "DISTRIBUTED", + "artifacts": null, + "resources": null + } + ] +} \ No newline at end of file diff --git a/vid-app-common/src/main/resources/service-vf-csar.zip b/vid-app-common/src/main/resources/service-vf-csar.zip new file mode 100644 index 0000000000000000000000000000000000000000..f66d0846417d3c245971e8d2b8ccca34e4f39f94 GIT binary patch literal 58693 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 expectedNetworksMap = getExpectedServiceModel().getNetworks(); + Map actualNetworksMap = p2.makeServiceModel(getCsarPath(), getServiceByUuid()).getNetworks(); + for (Map.Entry entry : expectedNetworksMap.entrySet()) { + Network expectedNetwork = entry.getValue(); + Network actualNetwork = actualNetworksMap.get(entry.getKey()); + Assert.assertEquals(expectedNetwork.getModelCustomizationName(), actualNetwork.getModelCustomizationName()); + verifyBaseNodeProperties(expectedNetwork, actualNetwork); + compareProperties(expectedNetwork.getProperties(), actualNetwork.getProperties()); + } + } + + //Because we are not supporting the old flow, the JSON are different by definition. + @Test + public void assertEqualsBetweenVnfsOfTosca() throws Exception { + Map expectedVnfsMap = getExpectedServiceModel().getVnfs(); + Map actualVnfsMap = p2.makeServiceModel(getCsarPath(), getServiceByUuid()).getVnfs(); + for (Map.Entry entry : expectedVnfsMap.entrySet()) { + VNF expectedVnf = entry.getValue(); + VNF actualVnf = actualVnfsMap.get(entry.getKey()); + verifyBaseNodeProperties(expectedVnf, actualVnf); + Assert.assertEquals(expectedVnf.getModelCustomizationName(), actualVnf.getModelCustomizationName()); + compareProperties(expectedVnf.getProperties(), actualVnf.getProperties()); + assertJsonStringEqualsIgnoreNulls(om.writeValueAsString(expectedVnf), om.writeValueAsString(actualVnf)); + } + } + + @Test + public void assertEqualsBetweenVolumeGroups() throws Exception { + Map actualVolumeGroups = p2.makeServiceModel(getCsarPath(), getServiceByUuid()).getVolumeGroups(); + Map expectedVolumeGroups = getExpectedServiceModel().getVolumeGroups(); + JsonAssert.assertJsonEquals(actualVolumeGroups, expectedVolumeGroups); + } + + @Test + public void assertEqualsBetweenVfModules() throws Exception { + Map actualVfModules = p2.makeServiceModel(getCsarPath(), getServiceByUuid()).getVfModules(); + Map expectedVfModules = getExpectedServiceModel().getVfModules(); + JsonAssert.assertJsonEquals(actualVfModules, expectedVfModules); + } + + private void verifyBaseNodeProperties(Node expectedNode, Node actualNode) { + Assert.assertEquals(expectedNode.getName(), actualNode.getName()); + Assert.assertEquals(expectedNode.getCustomizationUuid(), actualNode.getCustomizationUuid()); + Assert.assertEquals(expectedNode.getDescription(), actualNode.getDescription()); + Assert.assertEquals(expectedNode.getInvariantUuid(), actualNode.getInvariantUuid()); + Assert.assertEquals(expectedNode.getUuid(), actualNode.getUuid()); + Assert.assertEquals(expectedNode.getVersion(), actualNode.getVersion()); + } + + private void compareProperties(Map expectedProperties, Map actualProperties) { + for (Map.Entry property : expectedProperties.entrySet()) { + String expectedValue = property.getValue(); + String key = property.getKey(); + String actualValue = actualProperties.get(key); + Assert.assertEquals(expectedValue, actualValue); + } + } + + private NewServiceModel getExpectedServiceModel() throws IOException { + String expectedJsonAsString = IOUtils.toString(jsonFile).toString(); + return om.readValue(expectedJsonAsString,NewServiceModel.class); + } + + private Path getCsarPath() throws AsdcCatalogException { + return asdcClient.getServiceToscaModel(UUID.fromString(uuid)); + } + + private org.openecomp.vid.asdc.beans.Service getServiceByUuid() throws AsdcCatalogException { + return asdcClient.getService(UUID.fromString(uuid)); + } +} \ No newline at end of file diff --git a/vid-app-common/src/test/java/org/opencomp/vid/testUtils/TestUtils.java b/vid-app-common/src/test/java/org/opencomp/vid/testUtils/TestUtils.java new file mode 100644 index 000000000..29115e0aa --- /dev/null +++ b/vid-app-common/src/test/java/org/opencomp/vid/testUtils/TestUtils.java @@ -0,0 +1,65 @@ +package org.opencomp.vid.testUtils; + +import org.json.JSONArray; +import org.json.JSONObject; +import org.junit.Assert; + +import java.util.Iterator; + +import static fj.parser.Parser.fail; + +/** + * Created by Oren on 6/7/17. + */ +public class TestUtils { + + /** + * The method compares between two jsons. the function assert that the actual object does not reduce or change the functionallity/parsing of the expected json. + * This means that if the expected JSON has a key which is null or the JSON doesn't have a key which contained in the expected JSON the assert will succeed and the will pass. + * For example : For JSON expected = {a:null} and actual {a:3} the test will pass + * Other example : For JSON expected = {a:3} and actual {a:null} the test will fail + * + * @param expected JSON + * @param actual JSON + */ + public static void assertJsonStringEqualsIgnoreNulls(String expected, String actual) { + if (expected == null || expected == JSONObject.NULL) {return;} + + JSONObject expectedJSON = new JSONObject(expected); + JSONObject actualJSON = new JSONObject(actual); + Iterator keys = expectedJSON.keys(); + + while( keys.hasNext() ) { + String key = (String)keys.next(); + Object expectedValue = expectedJSON.get(key); + if (expectedValue == JSONObject.NULL){ + continue; + } + + Object actualValue = actualJSON.get(key); + + if (expectedValue instanceof JSONObject) { + String expectedVal = expectedValue.toString(); + String actualVal = actualValue.toString(); + assertJsonStringEqualsIgnoreNulls(expectedVal, actualVal); + } + else if (expectedValue instanceof JSONArray) { + if (actualValue instanceof JSONArray) { + JSONArray expectedJSONArray = (JSONArray)expectedValue; + JSONArray actualJSONArray = (JSONArray)expectedValue; + for (int i = 0; i < expectedJSONArray.length(); i++) { + String expectedItem = expectedJSONArray.getJSONObject(i).toString(); + String actualItem = actualJSONArray.getJSONObject(i).toString(); + assertJsonStringEqualsIgnoreNulls(expectedItem, actualItem); + } + } + else { + fail("expected: " + expectedValue + " got:" + actualValue); + } + } + else { + Assert.assertEquals(expectedValue, actualValue); + } + } + } +} diff --git a/vid-app-common/src/test/java/org/openecomp/ecomp/vid/selenium/LogOutLeftPane.java b/vid-app-common/src/test/java/org/openecomp/ecomp/vid/selenium/LogOutLeftPane.java index 66ea075e2..55aed8fb1 100755 --- a/vid-app-common/src/test/java/org/openecomp/ecomp/vid/selenium/LogOutLeftPane.java +++ b/vid-app-common/src/test/java/org/openecomp/ecomp/vid/selenium/LogOutLeftPane.java @@ -40,7 +40,7 @@ import org.testng.annotations.Test; /** * The Class LogOutLeftPane. */ -@Test(enabled=true) +@Test(enabled=false) public class LogOutLeftPane { /** The login button. */ diff --git a/vid-app-common/src/test/java/org/openecomp/fusion/core/MockApplicationContextTestSuite.java b/vid-app-common/src/test/java/org/openecomp/fusion/core/MockApplicationContextTestSuite.java index 64d72abef..2f5828b61 100755 --- a/vid-app-common/src/test/java/org/openecomp/fusion/core/MockApplicationContextTestSuite.java +++ b/vid-app-common/src/test/java/org/openecomp/fusion/core/MockApplicationContextTestSuite.java @@ -38,7 +38,7 @@ import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; - +import org.testng.annotations.Test; import org.openecomp.portalsdk.core.conf.AppConfig; import org.openecomp.portalsdk.core.objectcache.AbstractCacheManager; import org.openecomp.portalsdk.core.util.SystemProperties; @@ -61,6 +61,7 @@ import org.openecomp.portalsdk.core.util.CacheManager; @WebAppConfiguration @ContextConfiguration(loader = AnnotationConfigWebContextLoader.class, classes = {MockAppConfig.class}) @ActiveProfiles(value="test") +@Test(enabled=false) public class MockApplicationContextTestSuite { /** The wac. */ diff --git a/vid-app-common/src/test/java/org/openecomp/fusionapp/service/ProfileServiceTest.java b/vid-app-common/src/test/java/org/openecomp/fusionapp/service/ProfileServiceTest.java index ce3e6ba5f..85c550d4c 100755 --- a/vid-app-common/src/test/java/org/openecomp/fusionapp/service/ProfileServiceTest.java +++ b/vid-app-common/src/test/java/org/openecomp/fusionapp/service/ProfileServiceTest.java @@ -36,6 +36,7 @@ import org.openecomp.portalsdk.core.service.UserProfileService; /** * The Class ProfileServiceTest. */ + public class ProfileServiceTest extends MockApplicationContextTestSuite { /** The service. */ @@ -51,14 +52,13 @@ public class ProfileServiceTest extends MockApplicationContextTestSuite { */ @Test public void testFindAll() { - - try { - List profiles = service.findAll(); - Assert.assertTrue(profiles.size() > 0); - } - catch (Exception e) { - //TODO: this is only to make maven to not complaint - return; + + List profiles; + try { + profiles = service.findAll(); + Assert.assertTrue(profiles.size() > 0); + } catch (Exception e) { + Assert.assertTrue(false); } } diff --git a/vid-app-common/src/test/resources/asdc.properties b/vid-app-common/src/test/resources/asdc.properties new file mode 100644 index 000000000..8352ff213 --- /dev/null +++ b/vid-app-common/src/test/resources/asdc.properties @@ -0,0 +1,6 @@ +asdc.client.type=REST + +asdc.client.rest.protocol=http +asdc.client.rest.host=c2.vm1.sdc.simpledemo.openecomp.org +asdc.client.rest.port=8080 +asdc.client.rest.auth=Basic dmlkOktwOGJKNFNYc3pNMFdYbGhhazNlSGxjc2UyZ0F3ODR2YW9HR21KdlV5MlU= diff --git a/vid-app-common/src/test/resources/sampleTosca.csar b/vid-app-common/src/test/resources/sampleTosca.csar new file mode 100644 index 0000000000000000000000000000000000000000..d9c469a15671e499e06ef5a3c05e17f2fe547944 GIT binary patch literal 6177 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 A4FCWD literal 0 HcmV?d00001 diff --git a/vid-app-common/src/test/resources/vf-csar.JSON b/vid-app-common/src/test/resources/vf-csar.JSON new file mode 100644 index 000000000..bd1a01935 --- /dev/null +++ b/vid-app-common/src/test/resources/vf-csar.JSON @@ -0,0 +1,153 @@ +{ + "networks": { + }, + "service": { + "category": "Mobility", + "description": "Bla bla", + "inputs": { + "greatdefect0_availability_zone_max_count": { + "constraints": [ + ], + "default": 1, + "description": "", + "entry_schema": null, + "required": true, + "type": "integer" + }, + "greatdefect0_itc_name_0": { + "constraints": [ + ], + "default": "ab", + "description": "ixla itc instance name", + "entry_schema": null, + "required": true, + "type": "string" + }, + "greatdefect0_vf_module_id": { + "constraints": [ + ], + "default": "abc", + "description": "Unique ID for this VF Module instance", + "entry_schema": null, + "required": true, + "type": "string" + } + }, + "invariantUuid": "bd9bf71a-df22-4c95-8638-89f15d875395", + "name": "Moriya_new_test", + "serviceEcompNaming": "true", + "toscaModelURL": null, + "uuid": "48a52540-8772-4368-9cdb-1f124ea5c931", + "version": "1.0" + }, + "vfModules": { + "greatdefect0..Greatdefect..base_ixla..module-0": { + "commands": { + }, + "customizationUuid": "316e323d-611d-4007-a647-b1d2ecdaee9e", + "description": null, + "invariantUuid": "80ff85fb-cb11-42cb-9737-e47095d42756", + "modelCustomizationName": "Greatdefect..base_ixla..module-0", + "name": "Greatdefect..base_ixla..module-0", + "uuid": "01166434-ef34-4969-aaf2-626203d72e48", + "version": "3", + "volumeGroupAllowed": false + }, + "greatdefect0..Greatdefect..module_1_ixla..module-2": { + "commands": { + }, + "customizationUuid": "1106fca3-235a-4f92-8d5a-960a7336b32f", + "description": null, + "invariantUuid": "e0297a51-c670-452e-b31c-c5b37c6ad40f", + "modelCustomizationName": "Greatdefect..module_1_ixla..module-2", + "name": "Greatdefect..module_1_ixla..module-2", + "uuid": "6f09e053-56a6-4fbb-8299-e1de616825cc", + "version": "3", + "volumeGroupAllowed": false + }, + "greatdefect0..Greatdefect..module_2_ixla..module-1": { + "commands": { + }, + "customizationUuid": "b52c1fda-fbbf-4de3-ad9b-190d4a14990c", + "description": null, + "invariantUuid": "23befc6e-aa97-4004-b215-4979c3f84913", + "modelCustomizationName": "Greatdefect..module_2_ixla..module-1", + "name": "Greatdefect..module_2_ixla..module-1", + "uuid": "dea8e41f-c996-4557-b521-263210d96baa", + "version": "3", + "volumeGroupAllowed": false + } + }, + "vnfs": { + "greatdefect 0": { + "commands": { + "availability_zone_max_count": { + "command": "get_input", + "displayName": "availability_zone_max_count", + "inputName": "greatdefect0_availability_zone_max_count" + }, + "itc_name_0": { + "command": "get_input", + "displayName": "itc_name_0", + "inputName": "greatdefect0_itc_name_0" + }, + "vf_module_id": { + "command": "get_input", + "displayName": "vf_module_id", + "inputName": "greatdefect0_vf_module_id" + } + }, + "customizationUuid": "9123ced3-fbcd-42f7-b103-5965c54bbd66", + "description": "checl-IdanWithSecondFix", + "inputs": { + "availability_zone_max_count": { + "constraints": [ + ], + "default": 1, + "description": "", + "entry_schema": null, + "required": true, + "type": "integer" + }, + "itc_name_0": { + "constraints": [ + ], + "default": "ab", + "description": "ixla itc instance name", + "entry_schema": null, + "required": true, + "type": "string" + }, + "vf_module_id": { + "constraints": [ + ], + "default": "abc", + "description": "Unique ID for this VF Module instance", + "entry_schema": null, + "required": true, + "type": "string" + } + }, + "invariantUuid": "d149c45a-b42f-419a-9fac-f9c359fc2034", + "modelCustomizationName": "greatdefect 0", + "name": "greatdefect", + "properties": { + "availability_zone_max_count": "get_input:greatdefect0_availability_zone_max_count", + "itc_flavor_name": "nv.c8r24d160", + "itc_image_name": "NIMBUS_IXLA-ITC_8.20_EA_KVM_210117.qcow2", + "itc_name_0": "get_input:greatdefect0_itc_name_0", + "itm_flavor_name": "nv.c2r4d50", + "itm_image_name": "NIMBUS_IXLA-ITM_8.20.EA_KVM.qcow2", + "vf_module_id": "get_input:greatdefect0_vf_module_id" + }, + "uuid": "3b25707a-d345-4a80-8744-73adf8f2e67b", + "version": "3.0", + "vfModules": { + }, + "volumeGroups": { + } + } + }, + "volumeGroups": { + } +} diff --git a/vid-app-common/src/test/resources/vl-csar.JSON b/vid-app-common/src/test/resources/vl-csar.JSON new file mode 100644 index 000000000..ce1f1b79f --- /dev/null +++ b/vid-app-common/src/test/resources/vl-csar.JSON @@ -0,0 +1,109 @@ +{ + "networks": { + "ExtVL 0": { + "commands": { + "exVL_naming#naming_policy": { + "command": "get_input", + "displayName": "exVL_naming#naming_policy", + "inputName": "extvl0_exVL_naming_naming_policy" + }, + "network_role": { + "command": "get_input", + "displayName": "network_role", + "inputName": "extvl0_network_role" + }, + "network_scope": { + "command": "get_input", + "displayName": "network_scope", + "inputName": "extvl0_network_scope" + } + }, + "customizationUuid": "664f8aa7-3989-46ac-81c0-dd72a8a63f26", + "description": "ECOMP generic virtual link (network) base type for all other service-level and global networks", + "inputs": { + "exVL_naming#naming_policy": { + "constraints": [ + ], + "default": null, + "description": "Reference to naming policy that ECOMP will use when the name is auto-generated", + "entry_schema": null, + "required": true, + "type": "string" + }, + "network_role": { + "constraints": [ + ], + "default": null, + "description": "Reference to naming policy that ECOMP will use when the name is auto-generated", + "entry_schema": null, + "required": true, + "type": "string" + }, + "network_scope": { + "constraints": [ + ], + "default": null, + "description": "Reference to naming policy that ECOMP will use when the name is auto-generated", + "entry_schema": null, + "required": true, + "type": "string" + } + }, + "invariantUuid": "379f816b-a7aa-422f-be30-17114ff50b7c", + "modelCustomizationName": "ExtVL 0", + "name": "ExtVL", + "properties": { + "exVL_naming": "{naming_policy={get_input=extvl0_exVL_naming_naming_policy}}", + "network_role": "get_input:extvl0_network_role", + "network_scope": "get_input:extvl0_network_scope" + }, + "uuid": "af584529-d7f0-420e-a6f3-c38b689c030f", + "version": "4.0" + } + }, + "service": { + "category": "Network L1-3", + "description": "dsfg", + "inputs": { + "extvl0_exVL_naming_naming_policy": { + "constraints": [ + ], + "default": null, + "description": "Reference to naming policy that ECOMP will use when the name is auto-generated", + "entry_schema": null, + "required": true, + "type": "string" + }, + "extvl0_network_role": { + "constraints": [ + ], + "default": null, + "description": "Unique label that defines the role that this network performs. example: vce oam network, vnat sr-iov1 network\n", + "entry_schema": null, + "required": true, + "type": "string" + }, + "extvl0_network_scope": { + "constraints": [ + ], + "default": null, + "description": "Uniquely identifies the network scope. Valid values for the network scope includes: VF - VF-level network. Intra-VF network which connects the VFCs (VMs) inside the VF. SERVICE - Service-level network. Intra-Service network which connects the VFs within the service GLOBAL - Global network which can be shared by multiple services\n", + "entry_schema": null, + "required": true, + "type": "string" + } + }, + "invariantUuid": "d752a44c-ac7b-4bda-8111-fb52312d101e", + "name": "Macro_flow_test", + "serviceEcompNaming": "true", + "toscaModelURL": null, + "uuid": "68101369-6f08-4e99-9a28-fa6327d344f3", + "version": "1.0" + }, + "vfModules": { + }, + "vnfs": { + }, + "volumeGroups": { + } +} -- 2.16.6