From 66b3afa06776e9944ad515206d281d67747c9770 Mon Sep 17 00:00:00 2001 From: "Leonard, Mark (ml401d)" Date: Wed, 21 Mar 2018 18:40:41 +0000 Subject: [PATCH] Incorporate the ECOMP SDC Artefact Generator code Remove the Maven dependency on openecomp-sdc-artifact-generator-core, because this is not maintained in the ONAP source code repositories. The functionality provided by the SDC Artifact Generator is replaced with equivalent code that depends on the SDC-TOSCA parsing library, without introducing any backwards-compatibility breaks. The intention is to make this Maven project more maintainable by depending only the common ONAP libraries. Change-Id: I01d78575d3b7c70a11e4c7989a021de3c0913a06 Issue-ID: AAI-943 Signed-off-by: mark.j.leonard --- License.txt | 20 - README.md | 2 +- ajsc-shared-config/README.txt | 8 - ajsc-shared-config/etc/aft.properties | 15 - .../etc/basic-logback_root_logger_level_off.xml | 25 +- ajsc-shared-config/etc/logback.xml | 403 ++-- ajsc-shared-config/etc/spm2.jks | Bin 62008 -> 0 bytes appconfig-local/auth/auth_policy.json | 47 + appconfig-local/babel-auth.properties | 2 + bundleconfig-local/README.txt | 1 - bundleconfig-local/RELEASE_NOTES.txt | 2 - .../etc/appprops/AAFUserRoles.properties | 2 - .../appprops/PostProcessorInterceptors.properties | 1 - .../appprops/PreProcessorInterceptors.properties | 1 - .../etc/appprops/app-intercepts.properties | 2 - .../etc/appprops/methodMapper.properties | 2 - .../etc/sysprops/sys-props.properties | 19 +- pom.xml | 253 ++- scripts/get-latest-xsd-version.sh | 7 + .../ajsc/babel_v1/babel/v1/conf/coreBeans.groovy | 9 + .../babel_v1/babel/v1/routes/coreServices.route | 4 + src/main/bin/start.sh | 24 +- .../java/org/onap/aai/auth/AAIAuthException.java | 25 +- .../org/onap/aai/auth/AAIMicroServiceAuth.java | 11 +- .../org/onap/aai/auth/AAIMicroServiceAuthCore.java | 18 +- src/main/java/org/onap/aai/auth/FileWatcher.java | 14 +- .../org/onap/aai/babel/config/BabelAuthConfig.java | 39 +- .../aai/babel/csar/CsarConverterException.java | 6 +- .../onap/aai/babel/csar/CsarToXmlConverter.java | 27 +- .../csar/extractor/InvalidArchiveException.java | 10 +- .../aai/babel/csar/extractor/YamlExtractor.java | 62 +- .../ConfigurationsToBabelArtifactConverter.java | 56 + .../vnfcatalog/InvalidNumberOfNodesException.java | 33 + .../csar/vnfcatalog/ToscaToCatalogException.java | 49 + .../csar/vnfcatalog/VendorImageConfiguration.java | 104 + .../csar/vnfcatalog/VnfVendorImageExtractor.java | 265 +++ .../onap/aai/babel/logging/ApplicationMsgs.java | 33 +- .../java/org/onap/aai/babel/logging/LogHelper.java | 520 +++++ .../babel/parser/ArtifactGeneratorToscaParser.java | 302 +++ .../org/onap/aai/babel/request/RequestHeaders.java | 84 + .../babel/service/GenerateArtifactsService.java | 12 +- .../service/GenerateArtifactsServiceImpl.java | 127 +- .../org/onap/aai/babel/service/InfoService.java | 80 + .../onap/aai/babel/service/data/BabelArtifact.java | 31 +- .../onap/aai/babel/service/data/BabelRequest.java | 32 +- .../aai/babel/util/RequestValidationException.java | 12 +- .../org/onap/aai/babel/util/RequestValidator.java | 15 +- .../aai/babel/xml/generator/ArtifactGenerator.java | 18 +- .../aai/babel/xml/generator/ModelGenerator.java | 51 +- .../generator/XmlArtifactGenerationException.java | 10 +- .../xml/generator/api/AaiArtifactGenerator.java | 239 +++ .../babel/xml/generator/api/AaiModelGenerator.java | 64 + .../xml/generator/api/AaiModelGeneratorImpl.java | 260 +++ .../babel/xml/generator/api/ArtifactGenerator.java | 41 + .../babel/xml/generator/data/AdditionalParams.java | 35 + .../aai/babel/xml/generator/data/Artifact.java | 96 + .../aai/babel/xml/generator/data/ArtifactType.java | 27 + .../babel/xml/generator/data/GenerationData.java | 65 + .../xml/generator/data/GeneratorConstants.java | 92 + .../babel/xml/generator/data/GeneratorUtil.java | 65 + .../aai/babel/xml/generator/data/GroupType.java | 26 + .../generator/data/WidgetConfigurationUtil.java | 43 + .../generator/error/IllegalAccessException.java | 30 + .../xml/generator/logging/CategoryLogLevel.java | 29 + .../babel/xml/generator/model/AllotedResource.java | 28 + .../xml/generator/model/AllotedResourceWidget.java | 31 + .../babel/xml/generator/model/FlavorWidget.java | 31 + .../aai/babel/xml/generator/model/ImageWidget.java | 31 + .../aai/babel/xml/generator/model/L3Network.java | 28 + .../babel/xml/generator/model/L3NetworkWidget.java | 31 + .../aai/babel/xml/generator/model/LIntfWidget.java | 31 + .../onap/aai/babel/xml/generator/model/Model.java | 248 +++ .../aai/babel/xml/generator/model/OamNetwork.java | 31 + .../xml/generator/model/ProvidingService.java | 28 + .../aai/babel/xml/generator/model/Resource.java | 54 + .../babel/xml/generator/model/ResourceWidget.java | 24 + .../aai/babel/xml/generator/model/Service.java | 43 + .../babel/xml/generator/model/ServiceWidget.java | 28 + .../babel/xml/generator/model/TenantWidget.java | 31 + .../xml/generator/model/TunnelXconnectWidget.java | 31 + .../babel/xml/generator/model/VServerWidget.java | 44 + .../aai/babel/xml/generator/model/VfModule.java | 109 + .../babel/xml/generator/model/VfModuleWidget.java | 31 + .../aai/babel/xml/generator/model/VfWidget.java | 31 + .../aai/babel/xml/generator/model/VfcWidget.java | 31 + .../babel/xml/generator/model/VirtualFunction.java | 28 + .../xml/generator/model/VolumeGroupWidget.java | 31 + .../babel/xml/generator/model/VolumeWidget.java | 31 + .../onap/aai/babel/xml/generator/model/Widget.java | 211 ++ .../aai/babel/xml/generator/types/Cardinality.java | 25 + .../onap/aai/babel/xml/generator/types/Model.java | 53 + .../aai/babel/xml/generator/types/ModelType.java | 27 + .../aai/babel/xml/generator/types/ModelWidget.java | 45 + .../resources/babel-logging-resources.properties | 36 +- .../org/onap/aai/babel/MicroServiceAuthTest.java | 39 +- .../babel/csar/extractor/YamlExtractorTest.java | 12 +- .../babel/csar/fixture/ArtifactInfoBuilder.java | 6 +- .../babel/csar/fixture/TestArtifactInfoImpl.java | 45 +- .../java/org/onap/aai/babel/logging/LogReader.java | 102 + .../aai/babel/logging/TestApplicationLogger.java | 235 ++ .../org/onap/aai/babel/parser/TestToscaParser.java | 88 + .../aai/babel/service/CsarToXmlConverterTest.java | 126 +- .../service/TestGenerateArtifactsServiceImpl.java | 131 +- .../onap/aai/babel/service/TestInfoService.java | 69 + .../org/onap/aai/babel/util/ArtifactTestUtils.java | 85 +- .../onap/aai/babel/util/TestRequestValidator.java | 40 +- .../babel/xml/generator/model/TestVfModule.java | 121 ++ .../aai/babel/xml/generator/model/TestWidget.java | 55 + src/test/resources/babel-auth.properties | 2 + src/test/resources/babel-beans.xml | 35 + src/test/resources/compressedArtifacts/Duff.txt | 1 + .../compressedArtifacts/catalog_csar.csar | Bin 0 -> 39894 bytes .../compressedArtifacts/noVnfConfiguration.csar | Bin 0 -> 89209 bytes .../generatedXml/AAI-SCP-Test-VSP-resource-1.0.xml | 51 + .../AAI-SD-WAN-Service-Test-service-1.0.xml | 43 +- .../AAI-SD-WAN-Test-VSP-resource-1.0.xml | 4 +- ...TestVsp..asc_heat-int2..module-0-resource-1.xml | 141 ++ ...AI-SdWanTestVsp..DUMMY..module-0-resource-2.xml | 5 +- .../AAI-Tunnel_XConnTest-resource-2.0.xml | 2 +- .../resources/jsonFiles/invalid_csar_request.json | 2 +- .../resources/jsonFiles/invalid_json_request.json | 2 +- .../jsonFiles/missing_artifact_name_request.json | 2 +- .../missing_artifact_version_request.json | 2 +- src/test/resources/jsonFiles/success_request.json | 3 - src/test/resources/logback.xml | 190 ++ src/test/resources/response/response.json | 2 +- src/test/resources/ymlFiles/artifacts.yml | 54 + src/test/resources/ymlFiles/data.yml | 2241 ++++++++++++++++++++ 128 files changed, 8453 insertions(+), 952 deletions(-) delete mode 100644 License.txt delete mode 100644 ajsc-shared-config/README.txt delete mode 100644 ajsc-shared-config/etc/aft.properties delete mode 100644 ajsc-shared-config/etc/spm2.jks create mode 100644 appconfig-local/auth/auth_policy.json create mode 100644 appconfig-local/babel-auth.properties delete mode 100644 bundleconfig-local/RELEASE_NOTES.txt create mode 100644 scripts/get-latest-xsd-version.sh create mode 100644 src/main/ajsc/babel_v1/babel/v1/conf/coreBeans.groovy create mode 100644 src/main/ajsc/babel_v1/babel/v1/routes/coreServices.route create mode 100644 src/main/java/org/onap/aai/babel/csar/vnfcatalog/ConfigurationsToBabelArtifactConverter.java create mode 100644 src/main/java/org/onap/aai/babel/csar/vnfcatalog/InvalidNumberOfNodesException.java create mode 100644 src/main/java/org/onap/aai/babel/csar/vnfcatalog/ToscaToCatalogException.java create mode 100644 src/main/java/org/onap/aai/babel/csar/vnfcatalog/VendorImageConfiguration.java create mode 100644 src/main/java/org/onap/aai/babel/csar/vnfcatalog/VnfVendorImageExtractor.java create mode 100644 src/main/java/org/onap/aai/babel/logging/LogHelper.java create mode 100644 src/main/java/org/onap/aai/babel/parser/ArtifactGeneratorToscaParser.java create mode 100644 src/main/java/org/onap/aai/babel/request/RequestHeaders.java create mode 100644 src/main/java/org/onap/aai/babel/service/InfoService.java create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/api/AaiArtifactGenerator.java create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/api/AaiModelGenerator.java create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/api/AaiModelGeneratorImpl.java create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/api/ArtifactGenerator.java create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/data/AdditionalParams.java create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/data/Artifact.java create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/data/ArtifactType.java create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/data/GenerationData.java create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/data/GeneratorConstants.java create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/data/GeneratorUtil.java create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/data/GroupType.java create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/data/WidgetConfigurationUtil.java create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/error/IllegalAccessException.java create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/logging/CategoryLogLevel.java create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/model/AllotedResource.java create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/model/AllotedResourceWidget.java create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/model/FlavorWidget.java create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/model/ImageWidget.java create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/model/L3Network.java create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/model/L3NetworkWidget.java create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/model/LIntfWidget.java create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/model/Model.java create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/model/OamNetwork.java create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/model/ProvidingService.java create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/model/Resource.java create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/model/ResourceWidget.java create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/model/Service.java create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/model/ServiceWidget.java create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/model/TenantWidget.java create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/model/TunnelXconnectWidget.java create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/model/VServerWidget.java create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/model/VfModule.java create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/model/VfModuleWidget.java create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/model/VfWidget.java create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/model/VfcWidget.java create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/model/VirtualFunction.java create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/model/VolumeGroupWidget.java create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/model/VolumeWidget.java create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/model/Widget.java create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/types/Cardinality.java create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/types/Model.java create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/types/ModelType.java create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/types/ModelWidget.java create mode 100644 src/test/java/org/onap/aai/babel/logging/LogReader.java create mode 100644 src/test/java/org/onap/aai/babel/logging/TestApplicationLogger.java create mode 100644 src/test/java/org/onap/aai/babel/parser/TestToscaParser.java create mode 100644 src/test/java/org/onap/aai/babel/service/TestInfoService.java create mode 100644 src/test/java/org/onap/aai/babel/xml/generator/model/TestVfModule.java create mode 100644 src/test/java/org/onap/aai/babel/xml/generator/model/TestWidget.java create mode 100644 src/test/resources/babel-auth.properties create mode 100644 src/test/resources/babel-beans.xml create mode 100644 src/test/resources/compressedArtifacts/Duff.txt create mode 100644 src/test/resources/compressedArtifacts/catalog_csar.csar create mode 100644 src/test/resources/compressedArtifacts/noVnfConfiguration.csar create mode 100644 src/test/resources/generatedXml/AAI-SCP-Test-VSP-resource-1.0.xml create mode 100644 src/test/resources/generatedXml/AAI-ScpTestVsp..asc_heat-int2..module-0-resource-1.xml delete mode 100644 src/test/resources/jsonFiles/success_request.json create mode 100644 src/test/resources/logback.xml create mode 100644 src/test/resources/ymlFiles/artifacts.yml create mode 100644 src/test/resources/ymlFiles/data.yml diff --git a/License.txt b/License.txt deleted file mode 100644 index 3452576..0000000 --- a/License.txt +++ /dev/null @@ -1,20 +0,0 @@ -============LICENSE_START======================================================= -org.onap.aai -================================================================================ -Copyright © 2017 AT&T Intellectual Property. All rights reserved. -Copyright © 2017 European Software Marketing Ltd. -================================================================================ -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========================================================= - -ECOMP is a trademark and service mark of AT&T Intellectual Property. \ No newline at end of file diff --git a/README.md b/README.md index e41e13f..62f04bf 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ ## Introduction Babel is a microservice in the AAI project that can be used by clients that work with TOSCA CSAR files. -It parses the TOSCA CSAR to generate xml files from a set of yaml files found in the TOSCA CSAR file. +It parses the TOSCA CSAR to generate xml files from a set of YAML files found in the TOSCA CSAR file. ## Compiling Babel Babel service can be compiled easily using maven command `mvn clean install` diff --git a/ajsc-shared-config/README.txt b/ajsc-shared-config/README.txt deleted file mode 100644 index e0f7ff1..0000000 --- a/ajsc-shared-config/README.txt +++ /dev/null @@ -1,8 +0,0 @@ -#Copyright (c) 2016 AT&T Intellectual Property. All rights reserved. - -The ajsc-shared-config folder is included in the service project to provide the functionality of the AJSC_SHARED_CONFIG -location that will exist in CSI envs. This includes the logback.xml for logging configurations, and some csm related -artifacts necessary for proper functionality of the csm framework within the CSI env. Within the 2 profiles that can -be utilized to run the AJSC locally, "runLocal" and "runAjsc", the system propery, "AJSC_SHARED_CONFIG", has been set -to point to this directory. The files in this folder will NOT be copied/moved anywhere within the AJSC SWM package. These -files will already be in existence within the CSI env. \ No newline at end of file diff --git a/ajsc-shared-config/etc/aft.properties b/ajsc-shared-config/etc/aft.properties deleted file mode 100644 index 95c7762..0000000 --- a/ajsc-shared-config/etc/aft.properties +++ /dev/null @@ -1,15 +0,0 @@ -#Copyright (c) 2016 AT&T Intellectual Property. All rights reserved. -# Flow test 319 -# The DEFAULT setup for this file is for deployment to soa cloud node which will use the "bundleconfig/etc/spm2.jks" location -# For Testing Locally, you can set the system property, csiEnable=true, found within bundleconfig-local/etc/sysprops/sys-props.properties -# and switch com.att.aft.keyStore and com.att.aft.trustStore values commented out below to "ajsc-shared-config/etc/spm2.jks" - -#replace proper values for the dummy values. -com.att.aft.discovery.client.environment=TEST -com.att.aft.discovery.client.latitude=35.318900 -com.att.aft.discovery.client.longitude=-80.762200 -com.att.aft.alias=fusionbus -com.att.aft.keyStore=bundleconfig/etc/key.jks -com.att.aft.keyStorePassword=password -com.att.aft.trustStore=bundleconfig/etc/key.jks -com.att.aft.trustStorePassword=password diff --git a/ajsc-shared-config/etc/basic-logback_root_logger_level_off.xml b/ajsc-shared-config/etc/basic-logback_root_logger_level_off.xml index 4ebe2db..e7568ca 100644 --- a/ajsc-shared-config/etc/basic-logback_root_logger_level_off.xml +++ b/ajsc-shared-config/etc/basic-logback_root_logger_level_off.xml @@ -1,6 +1,3 @@ - @@ -13,8 +10,7 @@ - + DEBUG @@ -25,16 +21,14 @@ 1 9 - + 5MB "%d [%thread] %-5level %logger{1024} - %msg%n" - + ERROR @@ -45,8 +39,7 @@ 1 9 - + 5MB @@ -61,9 +54,8 @@ localhost USER - + AJSC_AUDIT: [%thread] [%logger] %msg @@ -72,9 +64,8 @@ localhost USER - + AJSC_AUDIT: [%thread] [%logger] mdc:[%mdc] %msg diff --git a/ajsc-shared-config/etc/logback.xml b/ajsc-shared-config/etc/logback.xml index 7830b10..6f6cb49 100644 --- a/ajsc-shared-config/etc/logback.xml +++ b/ajsc-shared-config/etc/logback.xml @@ -1,212 +1,193 @@ - - - - - - - - - - - - - - - - - - - - - - - - - ${errorLogPattern} - - - - - - - - - - - ${logDirectory}/${generalLogName}.log - - ${logDirectory}/${generalLogName}.%d{yyyy-MM-dd}.log.zip - - 60 - - - ${errorLogPattern} - - - - - - INFO - - 256 - - - - - - - - ${logDirectory}/${auditLogName}.log - - ${logDirectory}/${auditLogName}.%d{yyyy-MM-dd}.log.zip - - 60 - - - ${auditMetricPattern} - - - - 256 - - - - - ${logDirectory}/${metricsLogName}.log - - ${logDirectory}/${metricsLogName}.%d{yyyy-MM-dd}.log.zip - - 60 - - - - ${auditMetricPattern} - - - - - - 256 - - - - - ${logDirectory}/${debugLogName}.log - - ${logDirectory}/${debugLogName}.%d{yyyy-MM-dd}.log.zip - - 60 - - - ${errorLogPattern} - - - - - 256 - - false - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + ${logDirectory}/${generalLogName}.log + + ${logDirectory}/${generalLogName}.%d{yyyy-MM-dd}.log.zip + + 60 + + + ${errorLogPattern} + + + + + + INFO + + 256 + + + + + + + ${logDirectory}/${auditLogName}.log + + ${logDirectory}/${auditLogName}.%d{yyyy-MM-dd}.log.zip + + 60 + + + ${auditLogPattern} + + + + 256 + + + + + ${logDirectory}/${metricsLogName}.log + + ${logDirectory}/${metricsLogName}.%d{yyyy-MM-dd}.log.zip + + 60 + + + ${metricsLogPattern} + + + + + 256 + + + + + + ${logDirectory}/${debugLogName}.log + + + ${logDirectory}/${debugLogName}.%d{yyyy-MM-dd}.log.zip + + 60 + + + ${errorLogPattern} + + + + + + + + + e.level.toInt() < INFO.toInt() + + + DENY + NEUTRAL + + 256 + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ajsc-shared-config/etc/spm2.jks b/ajsc-shared-config/etc/spm2.jks deleted file mode 100644 index 8ff2a00a105aab80a301cb1e67b567d272d71466..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 62008 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 + + + + + + + + + + + + + + + + + 4.0.0 @@ -7,9 +25,9 @@ com.att.ajsc 2.0.0 - org.onap.aai.babel + org.onap.aai babel - 2.0.0-SNAPSHOT + 1.2.0-SNAPSHOT aai-babel @@ -18,11 +36,7 @@ 2.0.0 /appl/${project.artifactId} - + /appl/${project.artifactId}/${project.version} ${basedir}/target/swm/package/nix/dist_files${distFilesRoot} @@ -31,9 +45,9 @@ aaiadmin com.att.csid.lab - + 9515 9516 @@ -43,20 +57,23 @@ /content/sites/site/org/onap/aai/babel/${project.artifactId}/${project.version} - 1.1.0 - 3.6 + 1.2.1 + 3.6 1.14 - 1.1.0 + 1.2.2 1.6.1 - 2.8.1 1.3 3.21.0-GA 1.1.9 + 0.13.2 1.10.19 1.6.2 1.1.32 - 1.18 + 1.3.3 + 2.9.4 0.7.9 + 1.6 + 1.2.1 https://nexus.onap.org ${basedir}/target @@ -112,18 +129,21 @@ commons-lang3 ${apache.lang3.version} - - - org.openecomp.sdc.common - openecomp-sdc-artifact-generator-core - ${aai.artifact.generator.version} + org.onap.sdc.sdc-tosca + sdc-tosca + ${sdc.tosca.version} com.fasterxml.jackson.core jackson-core - ${fasterxml.version} + ${fasterxml.version} + + org.onap.aai + rest-client + ${aai.rest.client.version} + @@ -165,21 +185,22 @@ org.openecomp.sdc.sdc-distribution-client sdc-distribution-client ${sdc.distribution.client.version} + test + + + xmlunit + xmlunit + ${xmlunit.version} + test - - xmlunit - xmlunit - 1.6 - test - - - ecomp-staging - ECOMP Staging Repository - ${onap.nexus.url}/content/repositories/staging/ - + + ecomp-staging + ECOMP Staging Repository + ${onap.nexus.url}/content/repositories/staging/ + @@ -201,6 +222,87 @@ + + org.apache.maven.plugins + maven-dependency-plugin + + + unpack + initialize + + unpack + + + + + org.onap.aai.aai-common + aai-schema + ${aai-schema.version} + jar + aai_schema/aai_schema_v**.xsd + target/tmp + + + + + + + + org.jvnet.jaxb2.maven2 + maven-jaxb2-plugin + ${mvn.jaxb2.version} + + + process-sources + + generate + + + + + target/tmp/aai_schema + org.onap.aai.babel.xml.generator.xsd + target/generated-sources + + aai_schema_latest.xsd + + true + + -Xannotate + + + + org.jvnet.jaxb2_commons + jaxb2-basics-annotate + 0.6.4 + + + + org.jvnet.jaxb2_commons + jaxb2-annotate-plugin-test-annox-annotations + 1.0.0 + + + + + + exec-maven-plugin + org.codehaus.mojo + + + Get latest xsd version + generate-sources + + exec + + + ${basedir}/scripts + bash + get-latest-xsd-version.sh ${basedir} + + + + org.codehaus.mojo sonar-maven-plugin @@ -216,12 +318,15 @@ - org.jacoco jacoco-maven-plugin ${jacoco.version} - + + + **/xml/generator/xsd/*.class + + prepare-agent @@ -238,7 +343,6 @@ - @@ -276,6 +380,7 @@ + org.apache.maven.plugins maven-jar-plugin @@ -305,37 +410,16 @@ ${docker.push.registry}/onap/${project.artifactId} ${docker.location} - latest + latest true - - - com.mycila - license-maven-plugin - 3.0 - -

License.txt
- - src/main/java/** - src/test/java/** - -
- - - - format - - process-sources - - - - org.apache.maven.plugins maven-deploy-plugin + 2.8.2 client @@ -362,31 +446,34 @@
- + - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.7 - true - - ${onap.nexus.url} - 176c31dfe190a - ecomp-staging - + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.7 + true + + ${onap.nexus.url} + 176c31dfe190a + ecomp-staging + + - org.apache.maven.plugins - maven-surefire-plugin - - false - 1 - + org.apache.maven.plugins + maven-surefire-plugin + + false + 1 + + . + + - - + @@ -478,13 +565,13 @@ - + context=/ port=${serverPort} diff --git a/scripts/get-latest-xsd-version.sh b/scripts/get-latest-xsd-version.sh new file mode 100644 index 0000000..0529821 --- /dev/null +++ b/scripts/get-latest-xsd-version.sh @@ -0,0 +1,7 @@ +basedir=$1 +cd ${basedir}/target/tmp/aai_schema +cp `ls -v | tail -1` ${basedir}/target/tmp/aai_schema/aai_schema_latest.xsd || exit 1 +echo "get-latest-xsd-version.sh has successfully copied aai_schema_latest.xsd to ${basedir}/target/tmp/aai_schema/latest_aai_schema" +exit 0 + + diff --git a/src/main/ajsc/babel_v1/babel/v1/conf/coreBeans.groovy b/src/main/ajsc/babel_v1/babel/v1/conf/coreBeans.groovy new file mode 100644 index 0000000..37043d8 --- /dev/null +++ b/src/main/ajsc/babel_v1/babel/v1/conf/coreBeans.groovy @@ -0,0 +1,9 @@ +beans{ + xmlns cxf: "http://camel.apache.org/schema/cxf" + xmlns jaxrs: "http://cxf.apache.org/jaxrs" + xmlns util: "http://www.springframework.org/schema/util" + + infoService(org.onap.aai.babel.service.InfoService) + + util.list(id: 'jaxrsServices') { ref(bean:'infoService') } +} diff --git a/src/main/ajsc/babel_v1/babel/v1/routes/coreServices.route b/src/main/ajsc/babel_v1/babel/v1/routes/coreServices.route new file mode 100644 index 0000000..076fb36 --- /dev/null +++ b/src/main/ajsc/babel_v1/babel/v1/routes/coreServices.route @@ -0,0 +1,4 @@ + + + + diff --git a/src/main/bin/start.sh b/src/main/bin/start.sh index 1854884..00d7dd6 100644 --- a/src/main/bin/start.sh +++ b/src/main/bin/start.sh @@ -3,8 +3,8 @@ # ============LICENSE_START======================================================= # org.onap.aai # ================================================================================ -# Copyright © 2017 AT&T Intellectual Property. All rights reserved. -# Copyright © 2017 European Software Marketing Ltd. +# Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. +# Copyright © 2017-2018 European Software Marketing Ltd. # ================================================================================ # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,8 +18,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============LICENSE_END========================================================= -# -# ECOMP is a trademark and service mark of AT&T Intellectual Property. BASEDIR="/opt/app/babel/" AJSC_HOME="$BASEDIR" @@ -29,6 +27,22 @@ if [ -z "$CONFIG_HOME" ]; then exit 1 fi +foo () { +if [ -z "$KEY_STORE_PASSWORD" ]; then + echo "KEY_STORE_PASSWORD must be set in order to start up process" + exit 1 +else + echo "KEY_STORE_PASSWORD=$KEY_STORE_PASSWORD\n" >> $AJSC_CONF_HOME/etc/sysprops/sys-props.properties +fi + +if [ -z "$KEY_MANAGER_PASSWORD" ]; then + echo "KEY_MANAGER_PASSWORD must be set in order to start up process" + exit 1 +else + echo "KEY_MANAGER_PASSWORD=$KEY_MANAGER_PASSWORD\n" >> $AJSC_CONF_HOME/etc/sysprops/sys-props.properties +fi +} + CLASSPATH="$AJSC_HOME/lib/*" CLASSPATH="$CLASSPATH:$AJSC_HOME/extJars/" CLASSPATH="$CLASSPATH:$AJSC_HOME/etc/" @@ -41,8 +55,6 @@ PROPS="$PROPS -DAJSC_SERVICE_VERSION=v1" PROPS="$PROPS -Dserver.port=9516" PROPS="$PROPS -DCONFIG_HOME=$CONFIG_HOME" PROPS="$PROPS -Dartifactgenerator.config=$CONFIG_HOME/artifact-generator.properties" -PROPS="$PROPS -DKEY_STORE_PASSWORD=$KEY_STORE_PASSWORD" -PROPS="$PROPS -DKEY_MANAGER_PASSWORD=$KEY_MANAGER_PASSWORD" JVM_MAX_HEAP=${MAX_HEAP:-1024} echo $CLASSPATH diff --git a/src/main/java/org/onap/aai/auth/AAIAuthException.java b/src/main/java/org/onap/aai/auth/AAIAuthException.java index 13593ab..a29ee98 100644 --- a/src/main/java/org/onap/aai/auth/AAIAuthException.java +++ b/src/main/java/org/onap/aai/auth/AAIAuthException.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017 European Software Marketing Ltd. + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,23 +17,18 @@ * See the License for the specific language governing permissions and * limitations under the License. * ============LICENSE_END========================================================= - * - * ECOMP is a trademark and service mark of AT&T Intellectual Property. */ package org.onap.aai.auth; public class AAIAuthException extends Exception { - /** - * - */ - private static final long serialVersionUID = 1L; + /** */ + private static final long serialVersionUID = 1L; - public AAIAuthException(String string) { - super(string); - } - - public AAIAuthException(String string, Exception e) { - super(string, e); - } + public AAIAuthException(String string) { + super(string); + } + public AAIAuthException(String string, Exception e) { + super(string, e); + } } diff --git a/src/main/java/org/onap/aai/auth/AAIMicroServiceAuth.java b/src/main/java/org/onap/aai/auth/AAIMicroServiceAuth.java index f678498..e4367f1 100644 --- a/src/main/java/org/onap/aai/auth/AAIMicroServiceAuth.java +++ b/src/main/java/org/onap/aai/auth/AAIMicroServiceAuth.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017 European Software Marketing Ltd. + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,8 +17,6 @@ * See the License for the specific language governing permissions and * limitations under the License. * ============LICENSE_END========================================================= - * - * ECOMP is a trademark and service mark of AT&T Intellectual Property. */ package org.onap.aai.auth; @@ -28,16 +26,15 @@ import javax.security.auth.x500.X500Principal; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.core.HttpHeaders; import org.onap.aai.babel.config.BabelAuthConfig; +import org.onap.aai.babel.logging.LogHelper; import org.onap.aai.cl.api.Logger; -import org.onap.aai.cl.eelf.LoggerFactory; - /** * Public class for authentication and authorization operations. Authorization is applied according to user and role */ public class AAIMicroServiceAuth { - private static Logger applicationLogger = LoggerFactory.getInstance().getLogger(AAIMicroServiceAuth.class); + private static final Logger applicationLogger = LogHelper.INSTANCE; private BabelAuthConfig babelAuthConfig; diff --git a/src/main/java/org/onap/aai/auth/AAIMicroServiceAuthCore.java b/src/main/java/org/onap/aai/auth/AAIMicroServiceAuthCore.java index b148440..6f000b8 100644 --- a/src/main/java/org/onap/aai/auth/AAIMicroServiceAuthCore.java +++ b/src/main/java/org/onap/aai/auth/AAIMicroServiceAuthCore.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017 European Software Marketing Ltd. + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,8 +17,6 @@ * See the License for the specific language governing permissions and * limitations under the License. * ============LICENSE_END========================================================= - * - * ECOMP is a trademark and service mark of AT&T Intellectual Property. */ package org.onap.aai.auth; @@ -38,16 +36,12 @@ import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.TimeUnit; import org.onap.aai.babel.logging.ApplicationMsgs; -import org.onap.aai.cl.api.Logger; -import org.onap.aai.cl.eelf.LoggerFactory; +import org.onap.aai.babel.logging.LogHelper; -/** - * Authentication and authorization by user and role. - * - */ +/** Authentication and authorization by user and role. */ public class AAIMicroServiceAuthCore { - private static Logger applicationLogger = LoggerFactory.getInstance().getLogger(AAIMicroServiceAuthCore.class); + private static LogHelper applicationLogger = LogHelper.INSTANCE; public static final String FILESEP = (System.getProperty("file.separator") == null) ? "/" : System.getProperty("file.separator"); @@ -83,7 +77,6 @@ public class AAIMicroServiceAuthCore { } public static synchronized void init(String authPolicyFile) throws AAIAuthException { - try { policyAuthFileName = AAIMicroServiceAuthCore.getConfigFile(authPolicyFile); } catch (IOException e) { @@ -237,7 +230,6 @@ public class AAIMicroServiceAuthCore { public void setUser(String myuser) { this.username = myuser; } - } public static class AAIAuthRole { diff --git a/src/main/java/org/onap/aai/auth/FileWatcher.java b/src/main/java/org/onap/aai/auth/FileWatcher.java index e3b9923..d974e66 100644 --- a/src/main/java/org/onap/aai/auth/FileWatcher.java +++ b/src/main/java/org/onap/aai/auth/FileWatcher.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017 European Software Marketing Ltd. + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,8 +17,6 @@ * See the License for the specific language governing permissions and * limitations under the License. * ============LICENSE_END========================================================= - * - * ECOMP is a trademark and service mark of AT&T Intellectual Property. */ package org.onap.aai.auth; @@ -41,15 +39,15 @@ public abstract class FileWatcher extends TimerTask { /** * runs a timer task - * + * * @see java.util.TimerTask.run */ @Override public final void run() { - long newTimeStamp = file.lastModified(); + long newTimestamp = file.lastModified(); - if ((newTimeStamp - this.timeStamp) > 500) { - this.timeStamp = newTimeStamp; + if ((newTimestamp - this.timeStamp) > 500) { + this.timeStamp = newTimestamp; onChange(file); } } diff --git a/src/main/java/org/onap/aai/babel/config/BabelAuthConfig.java b/src/main/java/org/onap/aai/babel/config/BabelAuthConfig.java index 5fdc01f..21525a1 100644 --- a/src/main/java/org/onap/aai/babel/config/BabelAuthConfig.java +++ b/src/main/java/org/onap/aai/babel/config/BabelAuthConfig.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017 European Software Marketing Ltd. + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,8 +17,6 @@ * See the License for the specific language governing permissions and * limitations under the License. * ============LICENSE_END========================================================= - * - * ECOMP is a trademark and service mark of AT&T Intellectual Property. */ package org.onap.aai.babel.config; @@ -26,26 +24,25 @@ import org.springframework.beans.factory.annotation.Value; public class BabelAuthConfig { - @Value("${auth.authentication.disable}") - private boolean authenticationDisable; - - @Value("${auth.policy.file}") - private String authPolicyFile; + @Value("${auth.authentication.disable}") + private boolean authenticationDisable; - public boolean isAuthenticationDisable() { - return authenticationDisable; - } + @Value("${auth.policy.file}") + private String authPolicyFile; - public void setAuthenticationDisable(boolean authenticationDisable) { - this.authenticationDisable = authenticationDisable; - } + public boolean isAuthenticationDisable() { + return authenticationDisable; + } - public String getAuthPolicyFile() { - return authPolicyFile; - } + public void setAuthenticationDisable(boolean authenticationDisable) { + this.authenticationDisable = authenticationDisable; + } - public void setAuthPolicyFile(String authPolicyFile) { - this.authPolicyFile = authPolicyFile; - } + public String getAuthPolicyFile() { + return authPolicyFile; + } + public void setAuthPolicyFile(String authPolicyFile) { + this.authPolicyFile = authPolicyFile; + } } diff --git a/src/main/java/org/onap/aai/babel/csar/CsarConverterException.java b/src/main/java/org/onap/aai/babel/csar/CsarConverterException.java index b9316fc..eb81014 100644 --- a/src/main/java/org/onap/aai/babel/csar/CsarConverterException.java +++ b/src/main/java/org/onap/aai/babel/csar/CsarConverterException.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017 European Software Marketing Ltd. + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,8 +17,6 @@ * See the License for the specific language governing permissions and * limitations under the License. * ============LICENSE_END========================================================= - * - * ECOMP is a trademark and service mark of AT&T Intellectual Property. */ package org.onap.aai.babel.csar; diff --git a/src/main/java/org/onap/aai/babel/csar/CsarToXmlConverter.java b/src/main/java/org/onap/aai/babel/csar/CsarToXmlConverter.java index 55cf652..1322c9c 100644 --- a/src/main/java/org/onap/aai/babel/csar/CsarToXmlConverter.java +++ b/src/main/java/org/onap/aai/babel/csar/CsarToXmlConverter.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017 European Software Marketing Ltd. + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,30 +17,26 @@ * See the License for the specific language governing permissions and * limitations under the License. * ============LICENSE_END========================================================= - * - * ECOMP is a trademark and service mark of AT&T Intellectual Property. */ package org.onap.aai.babel.csar; import java.util.List; import java.util.Objects; +import org.apache.commons.lang.time.StopWatch; import org.onap.aai.babel.csar.extractor.InvalidArchiveException; import org.onap.aai.babel.csar.extractor.YamlExtractor; import org.onap.aai.babel.logging.ApplicationMsgs; +import org.onap.aai.babel.logging.LogHelper; import org.onap.aai.babel.service.data.BabelArtifact; -import org.onap.aai.babel.xml.generator.XmlArtifactGenerationException; -import org.onap.aai.babel.xml.generator.ArtifactGenerator; import org.onap.aai.babel.xml.generator.ModelGenerator; -import org.onap.aai.cl.api.Logger; -import org.onap.aai.cl.eelf.LoggerFactory; - -import org.openecomp.sdc.generator.data.Artifact; +import org.onap.aai.babel.xml.generator.XmlArtifactGenerationException; +import org.onap.aai.babel.xml.generator.data.Artifact; /** * This class is responsible for converting content in a csar archive into one or more xml artifacts. */ public class CsarToXmlConverter { - private static Logger logger = LoggerFactory.getInstance().getLogger(CsarToXmlConverter.class); + private static final LogHelper logger = LogHelper.INSTANCE; /** * This method is responsible for extracting one or more yaml files from the given csarArtifact and then using them @@ -54,6 +50,10 @@ public class CsarToXmlConverter { */ public List generateXmlFromCsar(byte[] csarArchive, String name, String version) throws CsarConverterException { + + StopWatch stopwatch = new StopWatch(); + stopwatch.start(); + validateArguments(csarArchive, name, version); logger.info(ApplicationMsgs.DISTRIBUTION_EVENT, @@ -65,8 +65,7 @@ public class CsarToXmlConverter { List ymlFiles = YamlExtractor.extract(csarArchive, name, version); logger.debug("Calling XmlArtifactGenerator to generateXmlArtifacts"); - ArtifactGenerator modelGenerator = new ModelGenerator(); - xmlArtifacts = modelGenerator.generateArtifacts(ymlFiles); + xmlArtifacts = new ModelGenerator().generateArtifacts(csarArchive, ymlFiles); logger.debug(xmlArtifacts.size() + " xml artifacts have been generated"); } catch (InvalidArchiveException e) { @@ -75,6 +74,8 @@ public class CsarToXmlConverter { } catch (XmlArtifactGenerationException e) { throw new CsarConverterException( "An error occurred trying to generate xml files from a collection of yml files : " + e); + } finally { + logger.logMetrics(stopwatch, LogHelper.getCallerMethodName(0)); } return xmlArtifacts; diff --git a/src/main/java/org/onap/aai/babel/csar/extractor/InvalidArchiveException.java b/src/main/java/org/onap/aai/babel/csar/extractor/InvalidArchiveException.java index aac893b..97d72d4 100644 --- a/src/main/java/org/onap/aai/babel/csar/extractor/InvalidArchiveException.java +++ b/src/main/java/org/onap/aai/babel/csar/extractor/InvalidArchiveException.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017 European Software Marketing Ltd. + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,14 +17,10 @@ * See the License for the specific language governing permissions and * limitations under the License. * ============LICENSE_END========================================================= - * - * ECOMP is a trademark and service mark of AT&T Intellectual Property. */ package org.onap.aai.babel.csar.extractor; -/** - * This class represents an exception encountered when processing an archive in memory. - */ +/** This class represents an exception encountered when processing an archive in memory. */ public class InvalidArchiveException extends Exception { private static final long serialVersionUID = 1L; diff --git a/src/main/java/org/onap/aai/babel/csar/extractor/YamlExtractor.java b/src/main/java/org/onap/aai/babel/csar/extractor/YamlExtractor.java index fb51933..7700a54 100644 --- a/src/main/java/org/onap/aai/babel/csar/extractor/YamlExtractor.java +++ b/src/main/java/org/onap/aai/babel/csar/extractor/YamlExtractor.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017 European Software Marketing Ltd. + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,20 +17,13 @@ * See the License for the specific language governing permissions and * limitations under the License. * ============LICENSE_END========================================================= - * - * ECOMP is a trademark and service mark of AT&T Intellectual Property. */ package org.onap.aai.babel.csar.extractor; import java.io.IOException; import java.util.ArrayList; -import java.util.Collections; import java.util.Enumeration; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Set; import java.util.regex.Pattern; import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import org.apache.commons.compress.archivers.zip.ZipFile; @@ -38,11 +31,10 @@ import org.apache.commons.compress.utils.SeekableInMemoryByteChannel; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.onap.aai.babel.logging.ApplicationMsgs; +import org.onap.aai.babel.logging.LogHelper; import org.onap.aai.babel.xml.generator.ModelGenerator; +import org.onap.aai.babel.xml.generator.data.Artifact; import org.onap.aai.cl.api.Logger; -import org.onap.aai.cl.eelf.LoggerFactory; -import org.openecomp.sdc.generator.data.Artifact; -import org.yaml.snakeyaml.Yaml; /** * The purpose of this class is to process a .csar file in the form of a byte array and extract yaml files from it. @@ -51,19 +43,11 @@ import org.yaml.snakeyaml.Yaml; * file. */ public class YamlExtractor { - private static Logger logger = LoggerFactory.getInstance().getLogger(YamlExtractor.class); + private static Logger logger = LogHelper.INSTANCE; - private static final String TYPE = "type"; private static final Pattern YAMLFILE_EXTENSION_REGEX = Pattern.compile("(?i).*\\.ya?ml$"); - private static final Set INVALID_TYPES = new HashSet<>(); - - static { - Collections.addAll(INVALID_TYPES, "CP", "VL", "VFC", "VFCMT", "ABSTRACT"); - } - /** - * Private constructor - */ + /** Private constructor */ private YamlExtractor() { throw new IllegalAccessError("Utility class"); } @@ -91,7 +75,7 @@ public class YamlExtractor { ZipFile zipFile = new ZipFile(inMemoryByteChannel)) { for (Enumeration enumeration = zipFile.getEntries(); enumeration.hasMoreElements();) { ZipArchiveEntry entry = enumeration.nextElement(); - if (fileShouldBeExtracted(zipFile, entry)) { + if (fileShouldBeExtracted(entry)) { ymlFiles.add(ModelGenerator.createArtifact(IOUtils.toByteArray(zipFile.getInputStream(entry)), entry.getName(), version)); } @@ -120,30 +104,14 @@ public class YamlExtractor { } } - @SuppressWarnings("unchecked") - private static boolean fileShouldBeExtracted(ZipFile zipFile, ZipArchiveEntry entry) throws IOException { - logger.debug(ApplicationMsgs.DISTRIBUTION_EVENT, "Checking if " + entry.getName() + " should be extracted..."); - - boolean extractFile = false; - if (YAMLFILE_EXTENSION_REGEX.matcher(entry.getName()).matches()) { - try { - Yaml yamlParser = new Yaml(); - HashMap yaml = - (LinkedHashMap) yamlParser.load(zipFile.getInputStream(entry)); - HashMap metadata = (LinkedHashMap) yaml.get("metadata"); - - extractFile = metadata != null && metadata.containsKey(TYPE) - && !INVALID_TYPES.contains(metadata.get(TYPE).toString().toUpperCase()) - && !metadata.get(TYPE).toString().isEmpty(); - } catch (Exception e) { - logger.error(ApplicationMsgs.DISTRIBUTION_EVENT, - "Unable to verify " + entry.getName() + " contains a valid resource type: " + e.getMessage()); - } - } - - logger.debug(ApplicationMsgs.DISTRIBUTION_EVENT, "Keeping file: " + entry.getName() + "? : " + extractFile); - + /** + * @param entry + * @return + */ + private static boolean fileShouldBeExtracted(ZipArchiveEntry entry) { + boolean extractFile = YAMLFILE_EXTENSION_REGEX.matcher(entry.getName()).matches(); + logger.debug(ApplicationMsgs.DISTRIBUTION_EVENT, + "Checking if " + entry.getName() + " should be extracted... " + extractFile); return extractFile; } } - diff --git a/src/main/java/org/onap/aai/babel/csar/vnfcatalog/ConfigurationsToBabelArtifactConverter.java b/src/main/java/org/onap/aai/babel/csar/vnfcatalog/ConfigurationsToBabelArtifactConverter.java new file mode 100644 index 0000000..983c810 --- /dev/null +++ b/src/main/java/org/onap/aai/babel/csar/vnfcatalog/ConfigurationsToBabelArtifactConverter.java @@ -0,0 +1,56 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.csar.vnfcatalog; + +import com.google.gson.Gson; +import java.util.List; +import org.onap.aai.babel.service.data.BabelArtifact; +import org.onap.aai.babel.service.data.BabelArtifact.ArtifactType; + +/** + * This class is responsible for converting a collection of VendorImageConfigurations into an instance of a + * BabelArtifact. + */ +class ConfigurationsToBabelArtifactConverter { + private ConfigurationsToBabelArtifactConverter() {} + + /** + * This method converts a collection of VendorImageConfiguration objects into an instance of a BabelArtifact. + * + *

+ * The method will convert the configurations objects into JSON and this will be stored in the BabelArtifact's + * payload property. + * + *

+ * The method will return null if there are no configurations (null or empty) to process. + * + * @param configurations collection of VendorImageConfiguration objects into an instance of a BabelArtifact + * @return BabelArtifact instance representing the configurations or null if there are no configurations. + */ + static BabelArtifact convert(List configurations) { + if (configurations != null && !configurations.isEmpty()) { + String payload = new Gson().toJson(configurations, configurations.getClass()); + return new BabelArtifact("vnfVendorImageConfigurations", ArtifactType.VNFCATALOG, payload); + } else { + return null; + } + } +} diff --git a/src/main/java/org/onap/aai/babel/csar/vnfcatalog/InvalidNumberOfNodesException.java b/src/main/java/org/onap/aai/babel/csar/vnfcatalog/InvalidNumberOfNodesException.java new file mode 100644 index 0000000..425982b --- /dev/null +++ b/src/main/java/org/onap/aai/babel/csar/vnfcatalog/InvalidNumberOfNodesException.java @@ -0,0 +1,33 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.csar.vnfcatalog; + +/** + * This class represents a scenario when there are invalid number of Nodes found during processing. + */ +class InvalidNumberOfNodesException extends Exception { + /** Default ID */ + private static final long serialVersionUID = 1L; + + InvalidNumberOfNodesException(String message) { + super(message); + } +} diff --git a/src/main/java/org/onap/aai/babel/csar/vnfcatalog/ToscaToCatalogException.java b/src/main/java/org/onap/aai/babel/csar/vnfcatalog/ToscaToCatalogException.java new file mode 100644 index 0000000..2ef9dc8 --- /dev/null +++ b/src/main/java/org/onap/aai/babel/csar/vnfcatalog/ToscaToCatalogException.java @@ -0,0 +1,49 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.csar.vnfcatalog; + +/** + * This class represents an exception raised when trying to extract VNFCatalog data out of a CSAR file. + */ +public class ToscaToCatalogException extends Exception { + + /** Defaulted */ + private static final long serialVersionUID = 1L; + + /** + * Constructor with message and cause + * + * @param message Friendly information about the exception encountered + * @param cause the root cause of the exception + */ + public ToscaToCatalogException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructor with message only + * + * @param message Friendly information about the exception encountered + */ + public ToscaToCatalogException(String message) { + super(message); + } +} diff --git a/src/main/java/org/onap/aai/babel/csar/vnfcatalog/VendorImageConfiguration.java b/src/main/java/org/onap/aai/babel/csar/vnfcatalog/VendorImageConfiguration.java new file mode 100644 index 0000000..bd9aac7 --- /dev/null +++ b/src/main/java/org/onap/aai/babel/csar/vnfcatalog/VendorImageConfiguration.java @@ -0,0 +1,104 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.csar.vnfcatalog; + +import com.google.gson.annotations.SerializedName; + +/** + * This class represents Vendor Image data gleaned from tosca files within a csar file. + * + *

+ * Example: Where the value application property is 'VM00' this comes from the VNFConfiguration NodeTemplate under a + * path: + * + *

+ * node_templates:
+ *     vFW_VNF_Configuration:
+ *         type: org.openecomp.nodes.VnfConfiguration
+ *         properties:
+ *             allowed_flavors:
+ *                 ATT_part_12345_for_FortiGate-VM00: Note the value of this element is dynamic
+ *                     vendor_info:
+ *                         vendor_model: VM00
+ * 
+ * + * Where the value applicationVendor property is 'ATT (Tosca)' this comes from the VNFConfiguration NodeTemplate under a + * path: + * + *
+ * node_templates:
+ *     vFW_VNF_Configuration:
+ *         type: org.openecomp.nodes.VnfConfiguration
+ *         metadata:
+ *             resourceVendor: ATT (Tosca)
+ * 
+ * + * Where the value applicationVersion property is '3.16.9' this comes from the MultiFlavorVFC NodeTemplate under a path: + * + *
+ *  node_templates:
+ *     vWAN_VFC:
+ *         type: org.openecomp.resource.abstract.nodes.MultiFlavorVFC
+ *         properties:
+ *             images:
+ *                 3.16.1: Note the value of this element is dynamic - represents the name of each image
+ *                     software_version: 3.16.1
+ * 
+ */ +class VendorImageConfiguration { + private String application; + + @SerializedName("application-vendor") + private String applicationVendor; + + @SerializedName("application-version") + private String applicationVersion; + + VendorImageConfiguration(String application, String applicationVendor, String applicationVersion) { + this.application = application; + this.applicationVendor = applicationVendor; + this.applicationVersion = applicationVersion; + } + + public String getApplication() { + return application; + } + + public void setApplication(String application) { + this.application = application; + } + + public String getApplicationVendor() { + return applicationVendor; + } + + public void setApplicationVendor(String applicationVendor) { + this.applicationVendor = applicationVendor; + } + + public String getApplicationVersion() { + return applicationVersion; + } + + public void setApplicationVersion(String applicationVersion) { + this.applicationVersion = applicationVersion; + } +} diff --git a/src/main/java/org/onap/aai/babel/csar/vnfcatalog/VnfVendorImageExtractor.java b/src/main/java/org/onap/aai/babel/csar/vnfcatalog/VnfVendorImageExtractor.java new file mode 100644 index 0000000..e76c0c1 --- /dev/null +++ b/src/main/java/org/onap/aai/babel/csar/vnfcatalog/VnfVendorImageExtractor.java @@ -0,0 +1,265 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.csar.vnfcatalog; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang.time.StopWatch; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.Pair; +import org.onap.aai.babel.logging.ApplicationMsgs; +import org.onap.aai.babel.logging.LogHelper; +import org.onap.aai.babel.service.data.BabelArtifact; +import org.onap.sdc.tosca.parser.api.ISdcCsarHelper; +import org.onap.sdc.tosca.parser.exceptions.SdcToscaParserException; +import org.onap.sdc.tosca.parser.impl.SdcToscaParserFactory; +import org.onap.sdc.tosca.parser.impl.SdcTypes; +import org.onap.sdc.toscaparser.api.NodeTemplate; +import org.onap.sdc.toscaparser.api.SubstitutionMappings; + +/** + * This class is responsible for extracting Virtual Network Function (VNF) information from a TOSCA 1.1 CSAR package. + * + *

+ * CSAR is a compressed format that stores multiple TOSCA files. Each TOSCA file may define Topology Templates and/or + * Node Templates along with other model data. + * + *

+ * A VNF is a virtualized functional capability (e.g. a Router) that may be defined by an external Vendor. Within the + * model defined by the TOSCA files the VNF is considered to be a Resource (part of a Service). + * + *

+ * A VNF is specified by a Topology Template. Because this TOSCA construct does not allow properties to be defined + * directly, Node Templates are defined (identified by a VNF type value) storing the VNF metadata and properties. + * + *

+ * A VNF may be composed of multiple images, each running on its own Virtual Machine. The function of a deployed image + * is designated the Virtual Function Component (VFC). A VFC is a sub-component of the VNF. A VFC may have different + * "Flavors" (see the Deployment Flavors description below). + * + *

+ * An individual VNF (template) may be deployed with varying configuration values, according to + * environment/customer/business needs. For example, a VNF deployed in a testing environment would typically use fewer + * computational resources than in a production setting. + * + *

+ * A Vendor may define one or more "Deployment Flavors". A Deployment Flavor describes a set of pre-determined + * parameterised values for a specific aspect of the model. Within the TOSCA model there is a DeploymentFlavor + * definition, which has its own data types, and also an ImageInfo definition. + */ +public class VnfVendorImageExtractor { + private static LogHelper applicationLogger = LogHelper.INSTANCE; + + private static final String AN_ERROR_OCCURRED = "An error occurred trying to get the vnf catalog from a csar file."; + + // The following constants describe the expected naming conventions for TOSCA Node Templates which + // store the VNF + // configuration and the VFC data items. + + // The name of the property (for a VNF Configuration type) storing the Images Information + private static final String IMAGES = "images"; + + // Name of property key that contains the value of the software version + private static final String SOFTWARE_VERSION = "software_version"; + + // The name of the property (for a VNF Configuration type) storing the Vendor Information + private static final String VNF_CONF_TYPE_PROPERTY_VENDOR_INFO_CONTAINER = "allowed_flavors"; + + // Name of property key that contains the value of model of the vendor application + private static final String VENDOR_MODEL = "vendor_model"; + + /** + * This method is responsible for parsing the vnfConfiguration entity in the same topology_template (there's an + * assumption that there's only one per file, awaiting verification). + * + *

+ * It is possible that csar file does not contain a vnfConfiguration and this is valid. + * + *

+ * Where multiple vnfConfigurations are found an exception will be thrown. + * + *

+ * The ASDC model anticipates the following permutations of vnfConfiguration and multiflavorVFC: + * + *

+     * 
    + *
  1. Single vnfConfiguration, single multiFlavorVFC with multiple images.
  2. + *
  3. Single vnfConfiguration, multi multiFlavorVFC with single images.
  4. + *
+ *
+ * + * All ImageInfo sections found apply to all "Deployment Flavors", therefore we can apply a cross product of + * "Deployment Flavors" x "ImageInfo" - concretely + * + * @param csar compressed format that stores multiple TOSCA files and in particular a vnfConfiguration + * @return BabelArtifact VendorImageConfiguration objects created during processing represented as the Babel service + * public data structure + */ + public BabelArtifact extract(byte[] csar) throws ToscaToCatalogException { + StopWatch stopwatch = new StopWatch(); + stopwatch.start(); + + Objects.requireNonNull(csar, "A CSAR file must be supplied"); + + applicationLogger.info(ApplicationMsgs.DISTRIBUTION_EVENT, + "Starting to find and extract any vnf configuration data"); + + List vendorImageConfigurations; + Path path = null; + + try { + path = createTempFile(csar); + vendorImageConfigurations = createVendorImageConfigurations(path.toAbsolutePath().toString()); + } catch (InvalidNumberOfNodesException | IOException | SdcToscaParserException e) { + throw new ToscaToCatalogException(AN_ERROR_OCCURRED + " " + e.getLocalizedMessage(), e); + } finally { + if (path != null) { + FileUtils.deleteQuietly(path.toFile()); + } + } + + String msg = vendorImageConfigurations.isEmpty() ? "No Vnf Configuration has been found in the csar" + : "Vnf Configuration has been found in the csar"; + applicationLogger.info(ApplicationMsgs.DISTRIBUTION_EVENT, msg); + applicationLogger.logMetrics(stopwatch, LogHelper.getCallerMethodName(0)); + + return ConfigurationsToBabelArtifactConverter.convert(vendorImageConfigurations); + } + + private Path createTempFile(byte[] bytes) throws IOException { + applicationLogger.debug("Creating temp file on file system for the csar"); + Path path = Files.createTempFile("temp", ".csar"); + Files.write(path, bytes); + return path; + } + + private List createVendorImageConfigurations(String csarFilepath) + throws SdcToscaParserException, ToscaToCatalogException, InvalidNumberOfNodesException { + applicationLogger.info(ApplicationMsgs.DISTRIBUTION_EVENT, + "Getting instance of ISdcCsarHelper to use in finding vnf configuration data"); + ISdcCsarHelper csarHelper = SdcToscaParserFactory.getInstance().getSdcCsarHelper(csarFilepath); + + List vendorImageConfigurations = new ArrayList<>(); + NodeTemplate vnfConfigurationNode = findVnfConfigurationNode(csarHelper); + + if (vnfConfigurationNode != null) { + List serviceVfNodes = csarHelper.getServiceVfList(); + + for (NodeTemplate node : serviceVfNodes) { + vendorImageConfigurations.addAll(buildVendorImageConfigurations(vnfConfigurationNode, node)); + } + } + + return vendorImageConfigurations; + } + + private NodeTemplate findVnfConfigurationNode(ISdcCsarHelper csarHelper) throws InvalidNumberOfNodesException { + applicationLogger.debug("Tryng to find the vnf configuration node"); + + List configNodes = + csarHelper.getServiceNodeTemplateBySdcType(SdcTypes.VF).stream().map(serviceNodeTemplate -> { + String uuid = csarHelper.getNodeTemplateCustomizationUuid(serviceNodeTemplate); + applicationLogger.debug("Node Template Customization Uuid is " + uuid); + return csarHelper.getVnfConfig(uuid); + }).filter(Objects::nonNull).collect(Collectors.toList()); + + if (configNodes.size() > 1) { + throw new InvalidNumberOfNodesException("Only one vnf configuration node is allowed however " + + configNodes.size() + " nodes were found in the csar."); + } + + return configNodes.size() == 1 ? configNodes.get(0) : null; + } + + private List buildVendorImageConfigurations(NodeTemplate vendorInfoNode, + NodeTemplate node) throws ToscaToCatalogException { + List vendorImageConfigurations = new ArrayList<>(); + + List softwareVersions = extractSoftwareVersions(node.getSubMappingToscaTemplate()); + + Consumer> buildConfigurations = vi -> vendorImageConfigurations.addAll( + softwareVersions.stream().map(sv -> (new VendorImageConfiguration(vi.getRight(), vi.getLeft(), sv))) + .collect(Collectors.toList())); + + String resourceVendor = node.getMetaData().getValue("resourceVendor"); + buildVendorInfo(resourceVendor, vendorInfoNode).forEach(buildConfigurations); + + return vendorImageConfigurations; + } + + @SuppressWarnings("unchecked") + private List> buildVendorInfo(String resourceVendor, NodeTemplate vendorInfoNode) { + Map otherFlavorProperties = + (Map) vendorInfoNode.getPropertyValue(VNF_CONF_TYPE_PROPERTY_VENDOR_INFO_CONTAINER); + + return otherFlavorProperties.keySet().stream() + .map(key -> createVendorInfoPair((Map) otherFlavorProperties.get(key), resourceVendor)) + .collect(Collectors.toList()); + } + + private Pair createVendorInfoPair(Map otherFlavor, String resourceVendor) { + @SuppressWarnings("unchecked") + String vendorModel = otherFlavor.entrySet().stream() // + .filter(entry -> "vendor_info".equals(entry.getKey())) + .map(e -> ((Map) e.getValue()).get(VENDOR_MODEL)) // + .findFirst().orElse(null); + + applicationLogger.debug("Creating vendor info pair object for vendorModel = " + vendorModel + + " and resourceVendor = " + resourceVendor); + + return new ImmutablePair<>(resourceVendor, vendorModel); + } + + @SuppressWarnings("unchecked") + private List extractSoftwareVersions(SubstitutionMappings sm) throws ToscaToCatalogException { + applicationLogger.debug("Trying to extract the software versions for the vnf configuration"); + + List imagesNodes = sm.getNodeTemplates().stream() + .filter(nodeTemplate -> nodeTemplate.getPropertyValue(IMAGES) != null).collect(Collectors.toList()); + + if (imagesNodes != null && !imagesNodes.isEmpty()) { + applicationLogger.debug("Found NodeTemplates containing properties with a key called 'images'"); + return imagesNodes.stream() + .flatMap(imagesNode -> ((Map) imagesNode.getPropertyValue(IMAGES)) // + .entrySet().stream()) + .map(property -> findSoftwareVersion((Map) property.getValue())) + .collect(Collectors.toList()); + } else { + throw new ToscaToCatalogException("No software versions could be found for this csar file"); + } + } + + private String findSoftwareVersion(Map image) { + applicationLogger.debug("Getting the software version value from the map of image properties"); + + return (String) image.entrySet().stream().filter(entry -> SOFTWARE_VERSION.equals(entry.getKey())) + .map(Entry::getValue).findFirst().orElse(null); + } +} diff --git a/src/main/java/org/onap/aai/babel/logging/ApplicationMsgs.java b/src/main/java/org/onap/aai/babel/logging/ApplicationMsgs.java index 9b9f9d5..84b426b 100644 --- a/src/main/java/org/onap/aai/babel/logging/ApplicationMsgs.java +++ b/src/main/java/org/onap/aai/babel/logging/ApplicationMsgs.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017 European Software Marketing Ltd. + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,8 +17,6 @@ * See the License for the specific language governing permissions and * limitations under the License. * ============LICENSE_END========================================================= - * - * ECOMP is a trademark and service mark of AT&T Intellectual Property. */ package org.onap.aai.babel.logging; @@ -26,19 +24,22 @@ import com.att.eelf.i18n.EELFResourceManager; import org.onap.aai.cl.eelf.LogMessageEnum; public enum ApplicationMsgs implements LogMessageEnum { - //@formatter:off - /** - * Arguments: {0} = message. - */ - DISTRIBUTION_EVENT, - - PROCESS_REQUEST_ERROR, - - INVALID_CSAR_FILE, + /** Arguments: {0} = message. */ + // @formatter:off + DISTRIBUTION_EVENT, + MESSAGE_AUDIT, + MESSAGE_METRIC, + MISSING_REQUEST_ID, + PROCESS_REQUEST_ERROR, + INVALID_CSAR_FILE, + INVALID_REQUEST_JSON, + BABEL_REQUEST_PAYLOAD, + BABEL_RESPONSE_PAYLOAD, + LOAD_PROPERTIES, + PROCESSING_VNF_CATALOG_ERROR, + TEMP_FILE_ERROR; - INVALID_REQUEST_JSON; - - //@formatter:on + // @formatter:on /** * Static initializer to ensure the resource bundles for this class are loaded... Here this application loads diff --git a/src/main/java/org/onap/aai/babel/logging/LogHelper.java b/src/main/java/org/onap/aai/babel/logging/LogHelper.java new file mode 100644 index 0000000..54c5361 --- /dev/null +++ b/src/main/java/org/onap/aai/babel/logging/LogHelper.java @@ -0,0 +1,520 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.logging; + +import static com.att.eelf.configuration.Configuration.MDC_SERVICE_NAME; + +import ch.qos.logback.classic.AsyncAppender; +import ch.qos.logback.core.FileAppender; +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import com.att.eelf.i18n.EELFResolvableErrorEnum; +import java.io.File; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Optional; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import javax.servlet.ServletRequest; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.Response.Status; +import org.apache.commons.lang.time.StopWatch; +import org.onap.aai.babel.request.RequestHeaders; +import org.onap.aai.cl.api.LogFields; +import org.onap.aai.cl.api.Logger; +import org.onap.aai.cl.mdc.MdcContext; +import org.onap.aai.cl.mdc.MdcOverride; +import org.onap.aai.restclient.client.Headers; +import org.slf4j.MDC; + +/*- + * This Log Helper mimics the interface of a Common Logging Logger + * but adds helper methods for audit and metrics logging requirements. + * + * Messages are logged to the appropriate EELF functional logger as described below. + * + * Error Log: INFO/WARN/ERROR/FATAL + * Debug Log: DEBUG/TRACE + * Audit Log: summary view of transaction processing + * Metrics Log: detailed timings of transaction processing interactions + * + * Audit and Metrics log messages record the following fields: + * + * RequestID - an RFC4122 UUID for the transaction request + * ServiceName - the API provided by this service + * PartnerName - invoker of the API + * ClassName - name of the class creating the log record + * + * The above list is not exhaustive. + */ +public enum LogHelper implements Logger { + INSTANCE; // Joshua Bloch's singleton pattern + + @FunctionalInterface + public interface TriConsumer { + public void accept(T t, U u, V v); + } + + /** Audit log message status code values. See {@code MdcParameter.STATUS_CODE} */ + public enum StatusCode { + COMPLETE, + ERROR; + } + + /** + * Mapped Diagnostic Context parameter names. + * + *

+ * Note that MdcContext.MDC_START_TIME is used for audit messages, and indicates the start of a transaction. + * Messages in the metrics log record sub-operations of a transaction and thus use different timestamps. + */ + public enum MdcParameter { + REQUEST_ID(MdcContext.MDC_REQUEST_ID), + CLASS_NAME("ClassName"), + BEGIN_TIMESTAMP("BeginTimestamp"), + END_TIMESTAMP("EndTimestamp"), + ELAPSED_TIME("ElapsedTime"), + STATUS_CODE("StatusCode"), + RESPONSE_CODE("ResponseCode"), + RESPONSE_DESCRIPTION("ResponseDescription"), + TARGET_ENTITY("TargetEntity"), + TARGET_SERVICE_NAME("TargetServiceName"), + USER("User"); + + private final String parameterName; + + MdcParameter(String parameterName) { + this.parameterName = parameterName; + } + + /** + * Get the MDC logging context parameter name as referenced by the logback configuration + * + * @return the MDC parameter name + */ + public String value() { + return parameterName; + } + } + + /** Our externally advertised service API */ + private static final String SERVICE_NAME_VALUE = "AAI-BAS"; + + private static final EELFLogger errorLogger = EELFManager.getInstance().getErrorLogger(); + private static final EELFLogger debugLogger = EELFManager.getInstance().getDebugLogger(); + private static final EELFLogger auditLogger = EELFManager.getInstance().getAuditLogger(); + private static final EELFLogger metricsLogger = EELFManager.getInstance().getMetricsLogger(); + + /** Formatting for timestamps logged as Strings (from the MDC) */ + private DateFormat timestampFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX"); + + // Records the elapsed time (since the start of servicing a request) for audit logging + private StopWatch auditStopwatch; + + /** + * Initialises the MDC (logging context) with default values, to support any logging of messages BEFORE the + * startAudit() method is invoked. + */ + private LogHelper() { + setContextValue(MDC_SERVICE_NAME, SERVICE_NAME_VALUE); + // This value is not expected to be used in the default logging configuration + setContextValue(MdcContext.MDC_START_TIME, timestampFormat.format(new Date())); + } + + /** + * Begin recording transaction information for a new request. This data is intended for logging purposes. This + * method does not actually write any messages to the log(s). + * + *

+ * Initialise the MDC logging context for auditing and metrics, using the HTTP request headers. This information + * includes: the correlation ID, local application service name/ID, calling host details and authentication data. + * + *

+ * The request object is used to find the client details (e.g. IP address) + * + * @param headers raw HTTP headers + * @param servletRequest the request + */ + public void startAudit(final HttpHeaders headers, ServletRequest servletRequest) { + auditStopwatch = new StopWatch(); + auditStopwatch.start(); + + Optional requestId = Optional.empty(); + String serviceInstanceId = null; + Optional partnerName = Optional.empty(); + + if (headers != null) { + RequestHeaders requestHeaders = new RequestHeaders(headers); + requestId = Optional.ofNullable(requestHeaders.getCorrelationId()); + serviceInstanceId = requestHeaders.getInstanceId(); + partnerName = Optional.ofNullable(headers.getHeaderString(Headers.FROM_APP_ID)); + } + + String clientHost = null; + String clientIPAddress = null; + String user = ""; + + if (servletRequest != null) { + clientHost = servletRequest.getRemoteHost(); + clientIPAddress = servletRequest.getRemoteAddr(); + + if (!partnerName.isPresent()) { + partnerName = Optional.ofNullable(clientHost); + } + } + + // Populate standard MDC keys - note that the initialize method calls MDC.clear() + MdcContext.initialize(requestId.orElse("missing-request-id"), SERVICE_NAME_VALUE, serviceInstanceId, + partnerName.orElse(""), clientIPAddress); + + setContextValue(MdcParameter.USER, user); + setContextValue(MdcContext.MDC_REMOTE_HOST, clientHost); + } + + /** + * Store a value in the current thread's logging context. + * + * @param key non-null parameter name + * @param value the value to store against the key + */ + public void setContextValue(String key, String value) { + debug(key + "=" + value); + MDC.put(key, value); + } + + /** + * Store a value in the current thread's logging context. + * + * @param param identifier of the context parameter + * @param value the value to store for this parameter + */ + public void setContextValue(MdcParameter param, String value) { + setContextValue(param.value(), value); + } + + /** + * Set a value in the current thread's logging context, only if this is not already set. + * + * @param key non-null parameter name + * @param value the value to store against the key (only if the current value is null) + */ + public void setDefaultContextValue(String key, String value) { + if (MDC.get(key) == null) { + setContextValue(key, value); + } + } + + /** + * Set a value in the current thread's logging context, only if this is not already set. + * + * @param param identifier of the context parameter + * @param value the value to store for this parameter (only if the current value is null) + */ + public void setDefaultContextValue(MdcParameter param, String value) { + setContextValue(param.value(), value); + } + + /** Clear all logging context values. This should be called at start-up only. */ + public void clearContext() { + debug("Clearing MDC context"); + MDC.clear(); + } + + /** + * Log an audit message to the audit logger. This method is expected to be called when a response is returned to the + * caller and/or when the processing of the request completes. + * + * @param status status of the service request: COMPLETE/ERROR + * @param responseCode optional application error code + * @param responseDescription human-readable description of the response code + * @param args the argument(s) required to populate the Audit Message log content + */ + public void logAudit(StatusCode status, String responseCode, String responseDescription, final String... args) { + if (auditStopwatch == null) { + debug("Unexpected program state: audit stopwatch not started"); + auditStopwatch = new StopWatch(); + auditStopwatch.start(); + } + + if (auditLogger.isInfoEnabled()) { + setMdcElapsedTime(auditStopwatch); + setContextValue(MdcParameter.STATUS_CODE, status.toString()); + setContextValue(MdcParameter.RESPONSE_CODE, responseCode); + setContextValue(MdcParameter.RESPONSE_DESCRIPTION, responseDescription); + invokeLogger(auditLogger::info, ApplicationMsgs.MESSAGE_AUDIT, args); + } + } + + /** + * Log an audit message to the audit logger representing a non-specific processing success message. + * + * @param msg + */ + public void logAuditSuccess(String msg) { + Status status = Status.OK; + logAudit(StatusCode.COMPLETE, Integer.toString(status.getStatusCode()), status.getReasonPhrase(), msg); + } + + /** + * Log an audit message to the audit logger representing an internal error (e.g. for an exception thrown by the + * implementation). This method is expected to be called when a generic error response is returned to the caller to + * indicate a processing failure. + * + * @param e Exception + */ + public void logAuditError(Exception e) { + Status status = Status.INTERNAL_SERVER_ERROR; + logAudit(StatusCode.ERROR, Integer.toString(status.getStatusCode()), status.getReasonPhrase(), e.getMessage()); + } + + /** + * Log a message to the metrics log. + * + * @param error the log code + * @param args the info messages + */ + public void logMetrics(final String... args) { + if (metricsLogger.isInfoEnabled()) { + invokeLogger(metricsLogger::info, ApplicationMsgs.MESSAGE_METRIC, args); + } + } + + /** + * @param stopwatch + * @param args + */ + public void logMetrics(final StopWatch stopwatch, String... args) { + setMdcElapsedTime(stopwatch); + logMetrics(args); + } + + @Override + public String formatMsg(@SuppressWarnings("rawtypes") Enum arg0, String... args) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isDebugEnabled() { + return debugLogger != null && debugLogger.isDebugEnabled(); + } + + @Override + public boolean isErrorEnabled() { + return errorLogger.isErrorEnabled(); + } + + @Override + public boolean isInfoEnabled() { + return errorLogger.isInfoEnabled(); + } + + @Override + public boolean isTraceEnabled() { + return debugLogger.isTraceEnabled(); + } + + @Override + public boolean isWarnEnabled() { + return errorLogger.isWarnEnabled(); + } + + /** + * Log a DEBUG level message to the debug logger. + * + * @param message the debug message + */ + @Override + public void debug(String message) { + if (isDebugEnabled()) { + invokeLogger(debugLogger::debug, message); + } + } + + @Override + public void debug(@SuppressWarnings("rawtypes") Enum errorCode, String... args) { + if (isDebugEnabled()) { + invokeErrorCodeLogger(debugLogger::debug, (EELFResolvableErrorEnum) errorCode, args); + } + } + + @Override + public void debug(@SuppressWarnings("rawtypes") Enum errorCode, LogFields arg1, String... args) { + throw new UnsupportedOperationException(); + } + + @Override + public void error(@SuppressWarnings("rawtypes") Enum errorCode, String... args) { + if (isErrorEnabled()) { + invokeErrorCodeLogger(errorLogger::error, (EELFResolvableErrorEnum) errorCode, args); + } + } + + @Override + public void error(@SuppressWarnings("rawtypes") Enum errorCode, Throwable t, String... args) { + if (isErrorEnabled()) { + invokeErrorCodeLogger(errorLogger::error, (EELFResolvableErrorEnum) errorCode, t, args); + } + } + + @Override + public void error(@SuppressWarnings("rawtypes") Enum errorCode, LogFields arg1, String... args) { + throw new UnsupportedOperationException(); + } + + @Override + public void error(@SuppressWarnings("rawtypes") Enum errorCode, LogFields arg1, Throwable arg2, String... args) { + throw new UnsupportedOperationException(); + } + + @Override + public void info(@SuppressWarnings("rawtypes") Enum errorCode, String... args) { + if (isInfoEnabled()) { + invokeErrorCodeLogger(errorLogger::info, (EELFResolvableErrorEnum) errorCode, args); + } + } + + @Override + public void info(@SuppressWarnings("rawtypes") Enum arg0, LogFields arg1, String... args) { + throw new UnsupportedOperationException(); + } + + @Override + public void info(@SuppressWarnings("rawtypes") Enum arg0, LogFields arg1, MdcOverride arg2, String... args) { + throw new UnsupportedOperationException(); + } + + @Override + public void trace(@SuppressWarnings("rawtypes") Enum errorCode, String... args) { + if (isTraceEnabled()) { + invokeErrorCodeLogger(debugLogger::trace, (EELFResolvableErrorEnum) errorCode, args); + } + } + + @Override + public void trace(@SuppressWarnings("rawtypes") Enum arg0, LogFields arg1, String... args) { + throw new UnsupportedOperationException(); + } + + @Override + public void warn(@SuppressWarnings("rawtypes") Enum errorCode, String... args) { + if (isWarnEnabled()) { + invokeErrorCodeLogger(errorLogger::warn, (EELFResolvableErrorEnum) errorCode, args); + } + } + + @Override + public void warn(@SuppressWarnings("rawtypes") Enum arg0, LogFields arg1, String... args) { + throw new UnsupportedOperationException(); + } + + /** + * Get the method name for a calling method (from the current stack trace) + * + * @param level number of levels for the caller (not including this method) + * @return the class and name of the calling method in the form "class#method" + */ + public static String getCallerMethodName(int level) { + StackTraceElement callingMethod = Thread.currentThread().getStackTrace()[level + 2]; + return callingMethod.getClassName() + "#" + callingMethod.getMethodName(); + } + + /** + * Convenience method to be used only for testing purposes. + * + * @return the directory storing the log files + */ + public static String getLogDirectory() { + ch.qos.logback.classic.Logger logger = + (ch.qos.logback.classic.Logger) org.slf4j.LoggerFactory.getLogger("com.att.eelf"); + AsyncAppender appender = (AsyncAppender) logger.getAppender("asyncEELF"); + FileAppender fileAppender = (FileAppender) appender.getAppender("EELF"); + return new File(fileAppender.getFile()).getParent(); + } + + private void setMdcClassName() { + MDC.put(MdcParameter.CLASS_NAME.value(), getCallerMethodName(3)); + } + + private void unsetMdcClassName() { + MDC.put(MdcParameter.CLASS_NAME.value(), null); + } + + /** + * Set the Begin, End, and Elapsed time values in the MDC context. + * + * @param stopwatch + */ + private void setMdcElapsedTime(final StopWatch stopwatch) { + long startTime = stopwatch.getStartTime(); + long elapsedTime = stopwatch.getTime(); + + setContextValue(MdcParameter.BEGIN_TIMESTAMP, timestampFormat.format(startTime)); + setContextValue(MdcParameter.END_TIMESTAMP, timestampFormat.format(startTime + elapsedTime)); + setContextValue(MdcParameter.ELAPSED_TIME, Long.toString(elapsedTime)); // Milliseconds + } + + /** + * @param logMethod the logger method to invoke + * @param message + */ + private void invokeLogger(Consumer logMethod, String message) { + setMdcClassName(); + logMethod.accept(message); + unsetMdcClassName(); + } + + /** + * @param logMethod + * @param msg + * @param args + */ + private void invokeLogger(BiConsumer logMethod, ApplicationMsgs msg, String[] args) { + setMdcClassName(); + logMethod.accept(msg, args); + unsetMdcClassName(); + } + + /** + * @param logMethod + * @param errorEnum + * @param args + */ + private void invokeErrorCodeLogger(BiConsumer logMethod, + EELFResolvableErrorEnum errorEnum, String[] args) { + setMdcClassName(); + logMethod.accept(errorEnum, args); + unsetMdcClassName(); + } + + /** + * @param logMethod + * @param errorEnum + * @param t a Throwable + * @param args + */ + private void invokeErrorCodeLogger(TriConsumer logMethod, + EELFResolvableErrorEnum errorEnum, Throwable t, String[] args) { + setMdcClassName(); + logMethod.accept(errorEnum, t, args); + unsetMdcClassName(); + } +} diff --git a/src/main/java/org/onap/aai/babel/parser/ArtifactGeneratorToscaParser.java b/src/main/java/org/onap/aai/babel/parser/ArtifactGeneratorToscaParser.java new file mode 100644 index 0000000..3bccebe --- /dev/null +++ b/src/main/java/org/onap/aai/babel/parser/ArtifactGeneratorToscaParser.java @@ -0,0 +1,302 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.parser; + +import static org.onap.aai.babel.xml.generator.data.GeneratorConstants.GENERATOR_AAI_CONFIGFILE_NOT_FOUND; +import static org.onap.aai.babel.xml.generator.data.GeneratorConstants.GENERATOR_AAI_CONFIGLOCATION_NOT_FOUND; +import static org.onap.aai.babel.xml.generator.data.GeneratorConstants.GENERATOR_AAI_PROVIDING_SERVICE_METADATA_MISSING; +import static org.onap.aai.babel.xml.generator.data.GeneratorConstants.GENERATOR_AAI_PROVIDING_SERVICE_MISSING; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.stream.Collectors; +import org.onap.aai.babel.logging.LogHelper; +import org.onap.aai.babel.xml.generator.data.GeneratorConstants; +import org.onap.aai.babel.xml.generator.data.WidgetConfigurationUtil; +import org.onap.aai.babel.xml.generator.model.AllotedResource; +import org.onap.aai.babel.xml.generator.model.L3NetworkWidget; +import org.onap.aai.babel.xml.generator.model.Model; +import org.onap.aai.babel.xml.generator.model.ProvidingService; +import org.onap.aai.babel.xml.generator.model.Resource; +import org.onap.aai.babel.xml.generator.model.Service; +import org.onap.aai.babel.xml.generator.model.TunnelXconnectWidget; +import org.onap.aai.babel.xml.generator.model.VfModule; +import org.onap.aai.babel.xml.generator.model.Widget; +import org.onap.aai.babel.xml.generator.types.ModelType; +import org.onap.aai.cl.api.Logger; +import org.onap.sdc.tosca.parser.api.ISdcCsarHelper; +import org.onap.sdc.toscaparser.api.Group; +import org.onap.sdc.toscaparser.api.NodeTemplate; +import org.onap.sdc.toscaparser.api.Property; + +public class ArtifactGeneratorToscaParser { + + public static final String GENERATOR_AAI_ERROR_INVALID_ID = + "Invalid value for mandatory attribute <%s> in Artifact: <%s>"; + private static final String ALLOTTED_RESOURCE = "Allotted Resource"; + private static final String TUNNEL_XCONNECT = "Tunnel XConnect"; + private static Logger log = LogHelper.INSTANCE; + + private ISdcCsarHelper csarHelper; + + /** + * Constructs using csarHelper + * + * @param csarHelper The csar helper + */ + public ArtifactGeneratorToscaParser(ISdcCsarHelper csarHelper) { + this.csarHelper = csarHelper; + } + + /** + * Returns the artifact description + * + * @param model the artifact model + * @return the artifact model's description + */ + public static String getArtifactDescription(Model model) { + String artifactDesc = model.getModelDescription(); + if (model.getModelType().equals(ModelType.SERVICE)) { + artifactDesc = "AAI Service Model"; + } else if (model.getModelType().equals(ModelType.RESOURCE)) { + artifactDesc = "AAI Resource Model"; + } + return artifactDesc; + } + + /** + * Initialises the widget configuration. + * + * @throws IOException + */ + public static void initWidgetConfiguration() throws IOException { + log.debug("Getting Widget Configuration"); + String configLocation = System.getProperty(GeneratorConstants.PROPERTY_ARTIFACT_GENERATOR_CONFIG_FILE); + if (configLocation != null) { + File file = new File(configLocation); + if (file.exists()) { + Properties properties = new Properties(); + properties.load(new FileInputStream(file)); + WidgetConfigurationUtil.setConfig(properties); + } else { + throw new IllegalArgumentException(String.format(GENERATOR_AAI_CONFIGFILE_NOT_FOUND, configLocation)); + } + } else { + throw new IllegalArgumentException(GENERATOR_AAI_CONFIGLOCATION_NOT_FOUND); + } + } + + /** + * Generates a Resource List using input Service Node Templates + * + * @param serviceNodes input Service Node Templates + * @param idTypeStore ID->Type mapping + * @return the processed resource models + */ + public List processResourceToscas(List serviceNodes, Map idTypeStore) { + List resources = new LinkedList<>(); + for (NodeTemplate serviceNode : serviceNodes) { + List resourceNodes = csarHelper.getNodeTemplateChildren(serviceNode); + + String resourceUuId = serviceNode.getMetaData().getValue("UUID"); + String mapValue = idTypeStore.get(resourceUuId); + if (mapValue != null) { + Model model = Model.getModelFor(idTypeStore.get(serviceNode.getMetaData().getValue("UUID"))); + + log.debug("Inside Resource artifact generation for resource"); + Map serviceMetadata = serviceNode.getMetaData().getAllProperties(); + model.populateModelIdentificationInformation(serviceMetadata); + + // Found model from the type store so removing the same + idTypeStore.remove(model.getModelNameVersionId()); + processVfTosca(idTypeStore, model, resourceNodes); + + // Process group information from tosca for vfModules + if (csarHelper.getServiceVfList() != null) { + processVfModules(resources, model, serviceNode); + } + + if (hasSubCategoryTunnelXConnect(serviceMetadata) && hasAllottedResource(serviceMetadata)) { + model.addWidget(new TunnelXconnectWidget()); + } + resources.add((Resource) model); + } + } + return resources; + } + + private void processVfModules(List resources, Model model, NodeTemplate serviceNode) { + // Get the customisation UUID for each VF node and use it to get its Groups + String uuid = csarHelper.getNodeTemplateCustomizationUuid(serviceNode); + + // Populate a Map of Group against NodeTemplates that are members of the Group + List serviceGroups = csarHelper.getVfModulesByVf(uuid); + + // Process each VF Group + for (Group serviceGroup : serviceGroups) { + Model groupModel = Model.getModelFor(serviceGroup.getType()); + if (groupModel instanceof VfModule) { + processVfModule(resources, model, serviceGroup, serviceNode, (VfModule) groupModel); + } + } + + } + + private void processVfModule(List resources, Model model, Group groupDefinition, NodeTemplate serviceNode, + VfModule groupModel) { + // Populate group with metadata properties + groupModel.populateModelIdentificationInformation(groupDefinition.getMetadata().getAllProperties()); + // Populate group with non-metadata properties + Map groupProperties = groupDefinition.getProperties(); + Map properties = populateStringProperties(groupProperties); + groupModel.populateModelIdentificationInformation(properties); + processVfModuleGroup(resources, model, groupDefinition, serviceNode, groupModel); + } + + private void processVfModuleGroup(List resources, Model model, Group groupDefinition, + NodeTemplate serviceNode, VfModule groupModel) { + // Get names of the members of the service group + List members = csarHelper.getMembersOfVfModule(serviceNode, groupDefinition); + if (members != null && !members.isEmpty()) { + List memberNames = members.stream().map(NodeTemplate::getName).collect(Collectors.toList()); + groupModel.setMembers(memberNames); + for (NodeTemplate nodeTemplate : members) { + processNodeTemplate(groupModel, nodeTemplate); + } + } + + model.addResource(groupModel); // Added group (VfModule) to the (VF) model + // Check if we have already encountered the same VfModule across all the artifacts + if (!resources.contains(groupModel)) { + resources.add(groupModel); + } + } + + private static void processNodeTemplate(Model group, NodeTemplate nodeTemplate) { + Model resourceNode; + // L3-network inside vf-module to be generated as Widget a special handling. + if (nodeTemplate.getType().contains("org.openecomp.resource.vl")) { + resourceNode = new L3NetworkWidget(); + } else { + resourceNode = Model.getModelFor(nodeTemplate.getType()); + } + if (resourceNode != null && !(resourceNode instanceof Resource)) { + Widget widget = (Widget) resourceNode; + widget.addKey(nodeTemplate.getName()); + // Add the widget element encountered to the Group model + group.addWidget(widget); + } + } + + /** + * Process the service tosca + * + * @param service model of the service artifact + * @param idTypeStore ID->Type mapping + * @param nodeTemplates a list of service nodes + * + */ + public void processServiceTosca(Service service, Map idTypeStore, + List nodeTemplates) { + log.debug("Inside Service Tosca "); + // Get the resource/widgets in the service according to the node-template types + for (NodeTemplate node : nodeTemplates) { + Model model = Model.getModelFor(correctNodeType(node)); + if (model != null) { + model.populateModelIdentificationInformation(node.getMetaData().getAllProperties()); + if (model instanceof Resource) { + // Keeping track of resource types and + // their uuid for identification during resource tosca processing + idTypeStore.put(model.getModelNameVersionId(), correctNodeType(node)); + service.addResource((Resource) model); + } else { + service.addWidget((Widget) model); + } + } + } + } + + private String correctNodeType(NodeTemplate nodeType) { + String correctedNodeType = nodeType.getType(); + if (hasAllottedResource(nodeType.getMetaData().getAllProperties())) { + if (nodeType.getType().contains("org.openecomp.resource.vf.")) { + correctedNodeType = "org.openecomp.resource.vf.allottedResource"; + } + if (nodeType.getType().contains("org.openecomp.resource.vfc.")) { + correctedNodeType = "org.openecomp.resource.vfc.AllottedResource"; + } + } + return correctedNodeType; + } + + private boolean hasAllottedResource(Map metadata) { + return ALLOTTED_RESOURCE.equals(metadata.get(GeneratorConstants.CATEGORY)); + } + + private boolean hasSubCategoryTunnelXConnect(Map metadata) { + return TUNNEL_XCONNECT.equals(metadata.get(GeneratorConstants.SUBCATEGORY)); + } + + /** + * Create a Map of property name against String property value from the input Map + * + * @param inputMap The input Map + * @return Map of property name against String property value + */ + private Map populateStringProperties(Map inputMap) { + return inputMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, + e -> e.getValue().getValue() == null ? "" : e.getValue().getValue().toString())); + } + + private void processVfTosca(Map idTypeStore, Model model, List resourceNodes) { + boolean flag = false; + + for (NodeTemplate resourceNodeTemplate : resourceNodes) { + Model resourceNode = Model.getModelFor(correctNodeType(resourceNodeTemplate)); + if (resourceNode instanceof ProvidingService) { + flag = true; + Map nodeProperties = resourceNodeTemplate.getProperties(); + if (nodeProperties.get("providing_service_uuid") == null + || nodeProperties.get("providing_service_invariant_uuid") == null) { + throw new IllegalArgumentException( + String.format(GENERATOR_AAI_PROVIDING_SERVICE_METADATA_MISSING, model.getModelId())); + } + Map properties = populateStringProperties(nodeProperties); + properties.put(GeneratorConstants.VERSION, "1.0"); + resourceNode.populateModelIdentificationInformation(properties); + model.addResource((Resource) resourceNode); + } else if (resourceNode instanceof Resource && !(resourceNode.getWidgetType().equals(Widget.Type.L3_NET))) { + idTypeStore.put(resourceNode.getModelNameVersionId(), correctNodeType(resourceNodeTemplate)); + model.addResource((Resource) resourceNode); + } + } + + if (model instanceof AllotedResource && !flag) { + throw new IllegalArgumentException( + String.format(GENERATOR_AAI_PROVIDING_SERVICE_MISSING, model.getModelId())); + } + } +} diff --git a/src/main/java/org/onap/aai/babel/request/RequestHeaders.java b/src/main/java/org/onap/aai/babel/request/RequestHeaders.java new file mode 100644 index 0000000..f0d960c --- /dev/null +++ b/src/main/java/org/onap/aai/babel/request/RequestHeaders.java @@ -0,0 +1,84 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.request; + +import java.util.Optional; +import javax.ws.rs.core.HttpHeaders; + +/** Bean to represent the ECOMP request/transaction IDs required for EELF logging. */ +public class RequestHeaders { + + // ECOMP request ID a.k.a. transaction ID or correlation ID + public static final String HEADER_REQUEST_ID = "X-ECOMP-RequestID"; + public static final String HEADER_SERVICE_INSTANCE_ID = "X-ECOMP-ServiceInstanceID"; + // This value should match with org.openecomp.restclient.client.Headers.TRANSACTION_ID + private static final String HEADER_X_TRANSACTION_ID = "X-TransactionId"; + + private String requestId; + private String instanceId; + private String transactionId; + + public RequestHeaders(HttpHeaders headers) { + requestId = headers.getHeaderString(RequestHeaders.HEADER_REQUEST_ID); + instanceId = headers.getHeaderString(RequestHeaders.HEADER_SERVICE_INSTANCE_ID); + transactionId = headers.getHeaderString(RequestHeaders.HEADER_X_TRANSACTION_ID); + } + + public String getRequestId() { + return requestId; + } + + public String getInstanceId() { + return instanceId; + } + + public String getTransactionId() { + return transactionId; + } + + /** + * Get the global request ID from the HTTP headers. The value will be taken from the header "X-ECOMP-RequestID" if + * this is set, or else the value of "X-TransactionId" (which may be null). + * + *

+ * If the correlation ID contains the symbol : then this character and any trailing characters are removed. This + * allows for an incrementing numeric sequence where there are multiple HTTP requests for a single transaction. + * + * @return the normalsed UUID used for correlating transactions across components, or else null (if no ID is set) + */ + public String getCorrelationId() { + // If the request ID is missing, use the transaction ID (if present) + String uuid = Optional.ofNullable(getRequestId()).orElse(getTransactionId()); + + // Normalize the correlation ID by removing any suffix + if (uuid != null && uuid.contains(":")) { + uuid = uuid.split(":")[0]; + } + + return uuid; + } + + @Override + public String toString() { + return "RequestHeaders [requestId=" + requestId + ", instanceId=" + instanceId + ", transactionId=" + + transactionId + "]"; + } +} diff --git a/src/main/java/org/onap/aai/babel/service/GenerateArtifactsService.java b/src/main/java/org/onap/aai/babel/service/GenerateArtifactsService.java index bb7b721..7034149 100644 --- a/src/main/java/org/onap/aai/babel/service/GenerateArtifactsService.java +++ b/src/main/java/org/onap/aai/babel/service/GenerateArtifactsService.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017 European Software Marketing Ltd. + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,8 +17,6 @@ * See the License for the specific language governing permissions and * limitations under the License. * ============LICENSE_END========================================================= - * - * ECOMP is a trademark and service mark of AT&T Intellectual Property. */ package org.onap.aai.babel.service; @@ -34,9 +32,7 @@ import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; import org.onap.aai.auth.AAIAuthException; -/** - * Generate artifacts from the specified request content - */ +/** Generate artifacts from the specified request content */ @Path("/") @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) @@ -46,6 +42,4 @@ public interface GenerateArtifactsService { @Path("/generateArtifacts") Response generateArtifacts(@Context UriInfo uriInfo, @Context HttpHeaders headers, @Context HttpServletRequest servletRequest, String request) throws AAIAuthException; - - } diff --git a/src/main/java/org/onap/aai/babel/service/GenerateArtifactsServiceImpl.java b/src/main/java/org/onap/aai/babel/service/GenerateArtifactsServiceImpl.java index bdb45b5..eef7476 100644 --- a/src/main/java/org/onap/aai/babel/service/GenerateArtifactsServiceImpl.java +++ b/src/main/java/org/onap/aai/babel/service/GenerateArtifactsServiceImpl.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017 European Software Marketing Ltd. + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,16 +17,15 @@ * See the License for the specific language governing permissions and * limitations under the License. * ============LICENSE_END========================================================= - * - * ECOMP is a trademark and service mark of AT&T Intellectual Property. */ package org.onap.aai.babel.service; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonSyntaxException; +import com.att.aft.dme2.internal.gson.Gson; +import com.att.aft.dme2.internal.gson.GsonBuilder; +import com.att.aft.dme2.internal.gson.JsonSyntaxException; import java.util.Base64; import java.util.List; +import java.util.UUID; import javax.inject.Inject; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.core.HttpHeaders; @@ -34,25 +33,26 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.UriInfo; -import org.onap.aai.auth.AAIAuthException; +import org.apache.commons.lang.time.StopWatch; import org.onap.aai.auth.AAIMicroServiceAuth; import org.onap.aai.auth.AAIMicroServiceAuthCore; import org.onap.aai.babel.csar.CsarConverterException; import org.onap.aai.babel.csar.CsarToXmlConverter; +import org.onap.aai.babel.csar.vnfcatalog.ToscaToCatalogException; +import org.onap.aai.babel.csar.vnfcatalog.VnfVendorImageExtractor; import org.onap.aai.babel.logging.ApplicationMsgs; +import org.onap.aai.babel.logging.LogHelper; +import org.onap.aai.babel.logging.LogHelper.MdcParameter; +import org.onap.aai.babel.logging.LogHelper.StatusCode; +import org.onap.aai.babel.request.RequestHeaders; import org.onap.aai.babel.service.data.BabelArtifact; import org.onap.aai.babel.service.data.BabelRequest; import org.onap.aai.babel.util.RequestValidationException; import org.onap.aai.babel.util.RequestValidator; -import org.onap.aai.cl.api.Logger; -import org.onap.aai.cl.eelf.LoggerFactory; - -/** - * Generate SDC Artifacts by passing in a CSAR payload, Artifact Name and Artifact version - */ +/** Generate SDC Artifacts by passing in a CSAR payload, Artifact Name and Artifact version */ public class GenerateArtifactsServiceImpl implements GenerateArtifactsService { - private static Logger applicationLogger = LoggerFactory.getInstance().getLogger(GenerateArtifactsServiceImpl.class); + private static final LogHelper applicationLogger = LogHelper.INSTANCE; private AAIMicroServiceAuth aaiMicroServiceAuth; @@ -66,14 +66,35 @@ public class GenerateArtifactsServiceImpl implements GenerateArtifactsService { /* * (non-Javadoc) - * + * * @see org.onap.aai.babel.service.GenerateArtifactsService#generateArtifacts(javax.ws.rs.core.UriInfo, * javax.ws.rs.core.HttpHeaders, javax.servlet.http.HttpServletRequest, java.lang.String) */ @Override public Response generateArtifacts(UriInfo uriInfo, HttpHeaders headers, HttpServletRequest servletRequest, - String requestBody) throws AAIAuthException { - applicationLogger.debug("Received request: " + requestBody); + String requestBody) { + applicationLogger.startAudit(headers, servletRequest); + applicationLogger.info(ApplicationMsgs.BABEL_REQUEST_PAYLOAD, + "Received request: " + headers.getRequestHeaders() + requestBody); + applicationLogger.debug(String.format( + "Received request. UriInfo \"%s\", HttpHeaders \"%s\", ServletRequest \"%s\", Request \"%s\"", uriInfo, + headers, servletRequest, requestBody)); + + // Additional name/value pairs according to EELF guidelines + applicationLogger.setContextValue("Protocol", "https"); + applicationLogger.setContextValue("Method", "POST"); + applicationLogger.setContextValue("Path", uriInfo.getPath()); + applicationLogger.setContextValue("Query", uriInfo.getPathParameters().toString()); + + RequestHeaders requestHeaders = new RequestHeaders(headers); + applicationLogger.info(ApplicationMsgs.BABEL_REQUEST_PAYLOAD, requestHeaders.toString()); + + String requestId = requestHeaders.getCorrelationId(); + if (requestId == null) { + requestId = UUID.randomUUID().toString(); + applicationLogger.info(ApplicationMsgs.MISSING_REQUEST_ID, requestId); + applicationLogger.setContextValue(MdcParameter.REQUEST_ID, requestId); + } Response response; try { @@ -82,23 +103,36 @@ public class GenerateArtifactsServiceImpl implements GenerateArtifactsService { response = authorized ? generateArtifacts(requestBody) : buildResponse(Status.UNAUTHORIZED, "User not authorized to perform the operation."); - } catch (AAIAuthException e) { + } catch (Exception e) { applicationLogger.error(ApplicationMsgs.PROCESS_REQUEST_ERROR, e); - throw e; + applicationLogger.logAuditError(e); + return buildResponse(Status.INTERNAL_SERVER_ERROR, + "Error while processing request. Please check the babel service logs for more details.\n"); + } + + StatusCode statusDescription; + int statusCode = response.getStatus(); + if (statusCode / 100 == 2) { + statusDescription = StatusCode.COMPLETE; + } else { + statusDescription = StatusCode.ERROR; } + applicationLogger.logAudit(statusDescription, Integer.toString(statusCode), + Response.Status.fromStatusCode(statusCode).getReasonPhrase(), response.getEntity().toString()); - applicationLogger.debug("Sending response: " + response.getStatus() + " " + response.getEntity().toString()); return response; } - /** * Generate XML model artifacts from request body. - * + * * @param requestBody the request body in JSON format * @return response object containing the generated XML models */ protected Response generateArtifacts(String requestBody) { + StopWatch stopwatch = new StopWatch(); + stopwatch.start(); + Response response; try { @@ -107,42 +141,53 @@ public class GenerateArtifactsServiceImpl implements GenerateArtifactsService { BabelRequest babelRequest = gson.fromJson(requestBody, BabelRequest.class); RequestValidator.validateRequest(babelRequest); byte[] csarFile = Base64.getDecoder().decode(babelRequest.getCsar()); - List xmlArtifacts = new CsarToXmlConverter().generateXmlFromCsar(csarFile, + + List babelArtifacts = new CsarToXmlConverter().generateXmlFromCsar(csarFile, babelRequest.getArtifactName(), babelRequest.getArtifactVersion()); - response = buildResponse(Status.OK, gson.toJson(xmlArtifacts)); + BabelArtifact vendorImageConfiguration = new VnfVendorImageExtractor().extract(csarFile); + if (vendorImageConfiguration != null) { + babelArtifacts.add(vendorImageConfiguration); + } + + response = buildResponse(Status.OK, gson.toJson(babelArtifacts)); } catch (JsonSyntaxException e) { - applicationLogger.error(ApplicationMsgs.INVALID_REQUEST_JSON, e); - response = buildResponse(Status.BAD_REQUEST, "Malformed request."); + response = processError(ApplicationMsgs.INVALID_REQUEST_JSON, Status.BAD_REQUEST, e, "Malformed request."); } catch (CsarConverterException e) { - applicationLogger.error(ApplicationMsgs.INVALID_CSAR_FILE, e); - response = buildResponse(Status.INTERNAL_SERVER_ERROR, "Error converting CSAR artifact to XML model."); + response = processError(ApplicationMsgs.INVALID_CSAR_FILE, Status.INTERNAL_SERVER_ERROR, e, + "Error converting CSAR artifact to XML model."); + } catch (ToscaToCatalogException e) { + response = processError(ApplicationMsgs.PROCESSING_VNF_CATALOG_ERROR, Status.INTERNAL_SERVER_ERROR, e, + "Error converting CSAR artifact to VNF catalog."); } catch (RequestValidationException e) { - applicationLogger.error(ApplicationMsgs.PROCESS_REQUEST_ERROR, e); - response = buildResponse(Status.BAD_REQUEST, e.getLocalizedMessage()); + response = + processError(ApplicationMsgs.PROCESS_REQUEST_ERROR, Status.BAD_REQUEST, e, e.getLocalizedMessage()); } catch (Exception e) { - applicationLogger.error(ApplicationMsgs.PROCESS_REQUEST_ERROR, e); - response = buildResponse(Status.INTERNAL_SERVER_ERROR, + response = processError(ApplicationMsgs.PROCESS_REQUEST_ERROR, Status.INTERNAL_SERVER_ERROR, e, "Error while processing request. Please check the babel service logs for more details.\n"); + } finally { + applicationLogger.logMetrics(stopwatch, LogHelper.getCallerMethodName(0)); } return response; } + private Response processError(ApplicationMsgs applicationMsgs, Status responseStatus, Exception e, String message) { + applicationLogger.error(applicationMsgs, e); + + return buildResponse(responseStatus, message); + } + /** * Helper method to create a REST response object. - * + * * @param status response status code * @param entity response payload * @return */ private Response buildResponse(Status status, String entity) { - //@formatter:off - return Response - .status(status) - .entity(entity) - .type(MediaType.TEXT_PLAIN) - .build(); - //@formatter:on + // @formatter:off + return Response.status(status).entity(entity).type(MediaType.TEXT_PLAIN).build(); + // @formatter:on } } diff --git a/src/main/java/org/onap/aai/babel/service/InfoService.java b/src/main/java/org/onap/aai/babel/service/InfoService.java new file mode 100644 index 0000000..107c6d1 --- /dev/null +++ b/src/main/java/org/onap/aai/babel/service/InfoService.java @@ -0,0 +1,80 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.service; + +import java.time.Clock; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.time.temporal.Temporal; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; + +/** + * Information service for the micro-service. Return status details to the caller. + * + * @exclude + */ +@Path("/core-service") +public class InfoService { + + private Clock clock = Clock.systemDefaultZone(); + private LocalDateTime startTime = LocalDateTime.now(clock); + private long infoCount = 0L; + + /** + * @param format is an optional setting - html requests an HTML format + * @return a formatted status report + */ + @GET + @Path("/info") + @Produces("text/plain") + public String getInfo(@DefaultValue("text") @QueryParam("format") String format) { + return "Status: Up\n" + statusReport(clock) + "\n"; + } + + /** @return a status report showing the up time for the service */ + public String statusReport(Clock clock) { + Temporal reportTime = LocalDateTime.now(clock); + long upTime = ChronoUnit.SECONDS.between(startTime, reportTime); + long upTimeDays = ChronoUnit.DAYS.between(startTime, reportTime); + + StringBuilder sb = new StringBuilder("Started at "); + sb.append(startTime).append('\n').append("Up time "); + if (upTimeDays > 0) { + sb.append(upTimeDays).append(" day"); + if (upTimeDays > 1) { + sb.append("s"); + } + sb.append(" "); + } + sb.append(LocalTime.MIDNIGHT.plusSeconds(upTime).format(DateTimeFormatter.ofPattern("HH:mm:ss"))).append('\n'); + + sb.append('\n').append("Info Service").append('\n'); + sb.append("total=").append(++infoCount).append('\n'); + + return sb.toString(); + } +} diff --git a/src/main/java/org/onap/aai/babel/service/data/BabelArtifact.java b/src/main/java/org/onap/aai/babel/service/data/BabelArtifact.java index 986aed9..81c237e 100644 --- a/src/main/java/org/onap/aai/babel/service/data/BabelArtifact.java +++ b/src/main/java/org/onap/aai/babel/service/data/BabelArtifact.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017 European Software Marketing Ltd. + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,21 +17,22 @@ * See the License for the specific language governing permissions and * limitations under the License. * ============LICENSE_END========================================================= - * - * ECOMP is a trademark and service mark of AT&T Intellectual Property. */ package org.onap.aai.babel.service.data; -/** - * Bean representing the return artifacts of the Babel microservice. - */ +/** Bean representing the return artifacts of the Babel microservice. */ public class BabelArtifact { + + public enum ArtifactType { + MODEL, + VNFCATALOG; + } + String name; - String type; - byte[] payload; + ArtifactType type; + String payload; - public BabelArtifact(String name, String type, byte[] payload) { - super(); + public BabelArtifact(String name, ArtifactType type, String payload) { this.name = name; this.type = type; this.payload = payload; @@ -45,19 +46,19 @@ public class BabelArtifact { this.name = name; } - public String getType() { + public ArtifactType getType() { return type; } public void setType(String type) { - this.type = type; + this.type = ArtifactType.valueOf(type); } - public byte[] getPayload() { + public String getPayload() { return payload; } - public void setPayload(byte[] payload) { + public void setPayload(String payload) { this.payload = payload; } } diff --git a/src/main/java/org/onap/aai/babel/service/data/BabelRequest.java b/src/main/java/org/onap/aai/babel/service/data/BabelRequest.java index 20a101f..47241b4 100644 --- a/src/main/java/org/onap/aai/babel/service/data/BabelRequest.java +++ b/src/main/java/org/onap/aai/babel/service/data/BabelRequest.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017 European Software Marketing Ltd. + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,14 +17,12 @@ * See the License for the specific language governing permissions and * limitations under the License. * ============LICENSE_END========================================================= - * - * ECOMP is a trademark and service mark of AT&T Intellectual Property. */ package org.onap.aai.babel.service.data; public class BabelRequest { private String csar; - private String artifactVersion; + private String artifactVersion; private String artifactName; public String getCsar() { @@ -34,20 +32,20 @@ public class BabelRequest { public void setCsar(String csar) { this.csar = csar; } - + public String getArtifactVersion() { - return artifactVersion; - } + return artifactVersion; + } - public void setArtifactVersion(String artifactVersion) { - this.artifactVersion = artifactVersion; - } + public void setArtifactVersion(String artifactVersion) { + this.artifactVersion = artifactVersion; + } - public String getArtifactName() { - return artifactName; - } + public String getArtifactName() { + return artifactName; + } - public void setArtifactName(String artifactName) { - this.artifactName = artifactName; - } + public void setArtifactName(String artifactName) { + this.artifactName = artifactName; + } } diff --git a/src/main/java/org/onap/aai/babel/util/RequestValidationException.java b/src/main/java/org/onap/aai/babel/util/RequestValidationException.java index 5e3be5e..cf5322a 100644 --- a/src/main/java/org/onap/aai/babel/util/RequestValidationException.java +++ b/src/main/java/org/onap/aai/babel/util/RequestValidationException.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017 European Software Marketing Ltd. + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,14 +17,10 @@ * See the License for the specific language governing permissions and * limitations under the License. * ============LICENSE_END========================================================= - * - * ECOMP is a trademark and service mark of AT&T Intellectual Property. */ package org.onap.aai.babel.util; -/** - * This exception is thrown when the request fails validation. - */ +/** This exception is thrown when the request fails validation. */ public class RequestValidationException extends Exception { private static final long serialVersionUID = 1L; @@ -38,5 +34,3 @@ public class RequestValidationException extends Exception { super(message); } } - - diff --git a/src/main/java/org/onap/aai/babel/util/RequestValidator.java b/src/main/java/org/onap/aai/babel/util/RequestValidator.java index ecc9d2b..499a5c8 100644 --- a/src/main/java/org/onap/aai/babel/util/RequestValidator.java +++ b/src/main/java/org/onap/aai/babel/util/RequestValidator.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017 European Software Marketing Ltd. + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,21 +17,22 @@ * See the License for the specific language governing permissions and * limitations under the License. * ============LICENSE_END========================================================= - * - * ECOMP is a trademark and service mark of AT&T Intellectual Property. */ package org.onap.aai.babel.util; import org.onap.aai.babel.service.data.BabelRequest; +/** Utility class */ public class RequestValidator { - private RequestValidator() {} - + /** Empty constructor */ + private RequestValidator() { + // Prevent instantiation + } /** * Validates that the request body contains the required attributes - * + * * @param request the request body to validate */ public static void validateRequest(BabelRequest request) throws RequestValidationException { diff --git a/src/main/java/org/onap/aai/babel/xml/generator/ArtifactGenerator.java b/src/main/java/org/onap/aai/babel/xml/generator/ArtifactGenerator.java index 4fd51aa..46d5ea4 100644 --- a/src/main/java/org/onap/aai/babel/xml/generator/ArtifactGenerator.java +++ b/src/main/java/org/onap/aai/babel/xml/generator/ArtifactGenerator.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017 European Software Marketing Ltd. + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,23 +17,23 @@ * See the License for the specific language governing permissions and * limitations under the License. * ============LICENSE_END========================================================= - * - * ECOMP is a trademark and service mark of AT&T Intellectual Property. */ package org.onap.aai.babel.xml.generator; import java.util.List; import org.onap.aai.babel.service.data.BabelArtifact; -import org.openecomp.sdc.generator.data.Artifact; +import org.onap.aai.babel.xml.generator.data.Artifact; public interface ArtifactGenerator { /** * Generate a {@link List} of {@link BabelArtifact}s from the Artifacts obtained from the CSAR - * - * @param csarArtifacts artifacts obtained from the CSAR file + * + * @param csarArchive original CSAR file (zip format) + * @param csarArtifacts YAML artifacts extracted from the CSAR file * @return generated {@link BabelArtifact}s + * @throws XmlArtifactGenerationException */ - List generateArtifacts(List csarArtifacts) throws XmlArtifactGenerationException; - + List generateArtifacts(byte[] csarArchive, List csarArtifacts) + throws XmlArtifactGenerationException; } diff --git a/src/main/java/org/onap/aai/babel/xml/generator/ModelGenerator.java b/src/main/java/org/onap/aai/babel/xml/generator/ModelGenerator.java index c6def3d..65e0ada 100644 --- a/src/main/java/org/onap/aai/babel/xml/generator/ModelGenerator.java +++ b/src/main/java/org/onap/aai/babel/xml/generator/ModelGenerator.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017 European Software Marketing Ltd. + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,36 +17,33 @@ * See the License for the specific language governing permissions and * limitations under the License. * ============LICENSE_END========================================================= - * - * ECOMP is a trademark and service mark of AT&T Intellectual Property. */ package org.onap.aai.babel.xml.generator; +import java.util.Base64; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.regex.Pattern; import java.util.stream.Collectors; import org.onap.aai.babel.logging.ApplicationMsgs; +import org.onap.aai.babel.logging.LogHelper; import org.onap.aai.babel.service.data.BabelArtifact; +import org.onap.aai.babel.service.data.BabelArtifact.ArtifactType; +import org.onap.aai.babel.xml.generator.api.AaiArtifactGenerator; +import org.onap.aai.babel.xml.generator.data.AdditionalParams; +import org.onap.aai.babel.xml.generator.data.Artifact; +import org.onap.aai.babel.xml.generator.data.GenerationData; +import org.onap.aai.babel.xml.generator.data.GeneratorUtil; +import org.onap.aai.babel.xml.generator.data.GroupType; import org.onap.aai.cl.api.Logger; -import org.onap.aai.cl.eelf.LoggerFactory; -import org.openecomp.sdc.generator.data.AdditionalParams; -import org.openecomp.sdc.generator.data.Artifact; -import org.openecomp.sdc.generator.data.GenerationData; -import org.openecomp.sdc.generator.data.GeneratorUtil; -import org.openecomp.sdc.generator.data.GroupType; -import org.openecomp.sdc.generator.service.ArtifactGenerationService; /** * This class is responsible for generating xml model artifacts from a collection of csar file artifacts */ public class ModelGenerator implements ArtifactGenerator { - private static Logger logger = LoggerFactory.getInstance().getLogger(ModelGenerator.class); + private static final Logger logger = LogHelper.INSTANCE; - private static final String GENERATORCONFIG = "{\"artifactTypes\": [\"AAI\"]}"; - private static final Pattern UUID_NORMATIVE_NEW_VERSION = Pattern.compile("^\\d{1,}.0"); private static final String VERSION_DELIMITER = "."; private static final String VERSION_DELIMITER_REGEXP = "\\" + VERSION_DELIMITER; private static final String DEFAULT_SERVICE_VERSION = "1.0"; @@ -54,12 +51,14 @@ public class ModelGenerator implements ArtifactGenerator { /** * Invokes the TOSCA artifact generator API with the input artifacts. * + * @param csarArchive * @param csarArtifacts the input artifacts * @return {@link List} of output artifacts * @throws XmlArtifactGenerationException if there is an error trying to generate xml artifacts */ @Override - public List generateArtifacts(List csarArtifacts) throws XmlArtifactGenerationException { + public List generateArtifacts(byte[] csarArchive, List csarArtifacts) + throws XmlArtifactGenerationException { logger.info(ApplicationMsgs.DISTRIBUTION_EVENT, "Generating XML for " + csarArtifacts.size() + " CSAR artifacts."); @@ -70,19 +69,19 @@ public class ModelGenerator implements ArtifactGenerator { String serviceVersion = getServiceVersion(toscaVersion); logger.debug("The service version is " + serviceVersion); Map additionalParams = new HashMap<>(); - additionalParams.put(AdditionalParams.ServiceVersion.getName(), serviceVersion); + additionalParams.put(AdditionalParams.SERVICE_VERSION.getName(), serviceVersion); // Call ArtifactGenerator API logger.debug("Obtaining instance of ArtifactGenerationService"); - ArtifactGenerationService generationService = ArtifactGenerationService.lookup(); + org.onap.aai.babel.xml.generator.api.ArtifactGenerator generator = new AaiArtifactGenerator(); logger.debug("About to call generationService.generateArtifact()"); - GenerationData data = generationService.generateArtifact(csarArtifacts, GENERATORCONFIG, additionalParams); + GenerationData data = generator.generateArtifact(csarArchive, csarArtifacts, additionalParams); logger.debug("Call generationService.generateArtifact() has finished"); // Convert results into BabelArtifacts if (data.getErrorData().isEmpty()) { - return data.getResultData().stream().map(a -> new BabelArtifact(a.getName(), a.getType(), a.getPayload())) - .collect(Collectors.toList()); + return data.getResultData().stream().map(a -> new BabelArtifact(a.getName(), ArtifactType.MODEL, + new String(Base64.getDecoder().decode(a.getPayload())))).collect(Collectors.toList()); } else { throw new XmlArtifactGenerationException( "Error occurred during artifact generation: " + data.getErrorData().toString()); @@ -115,14 +114,10 @@ public class ModelGenerator implements ArtifactGenerator { String serviceVersion; try { - if (UUID_NORMATIVE_NEW_VERSION.matcher(artifactVersion).matches()) { - serviceVersion = artifactVersion; - } else { - String[] versionParts = artifactVersion.split(VERSION_DELIMITER_REGEXP); - Integer majorVersion = Integer.parseInt(versionParts[0]); + String[] versionParts = artifactVersion.split(VERSION_DELIMITER_REGEXP); + Integer majorVersion = Integer.parseInt(versionParts[0]); - serviceVersion = (majorVersion + 1) + VERSION_DELIMITER + "0"; - } + serviceVersion = majorVersion + VERSION_DELIMITER + "0"; } catch (Exception e) { logger.warn(ApplicationMsgs.DISTRIBUTION_EVENT, "Error generating service version from artifact version: " + artifactVersion diff --git a/src/main/java/org/onap/aai/babel/xml/generator/XmlArtifactGenerationException.java b/src/main/java/org/onap/aai/babel/xml/generator/XmlArtifactGenerationException.java index bff6ab3..5e5304b 100644 --- a/src/main/java/org/onap/aai/babel/xml/generator/XmlArtifactGenerationException.java +++ b/src/main/java/org/onap/aai/babel/xml/generator/XmlArtifactGenerationException.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017 European Software Marketing Ltd. + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,14 +17,10 @@ * See the License for the specific language governing permissions and * limitations under the License. * ============LICENSE_END========================================================= - * - * ECOMP is a trademark and service mark of AT&T Intellectual Property. */ package org.onap.aai.babel.xml.generator; -/** - * This class represents an exception encountered when generating an Artifact. - */ +/** This class represents an exception encountered when generating an Artifact. */ public class XmlArtifactGenerationException extends Exception { private static final long serialVersionUID = 1L; diff --git a/src/main/java/org/onap/aai/babel/xml/generator/api/AaiArtifactGenerator.java b/src/main/java/org/onap/aai/babel/xml/generator/api/AaiArtifactGenerator.java new file mode 100644 index 0000000..bdcd71c --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/api/AaiArtifactGenerator.java @@ -0,0 +1,239 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.api; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.commons.io.FileUtils; +import org.onap.aai.babel.logging.ApplicationMsgs; +import org.onap.aai.babel.logging.LogHelper; +import org.onap.aai.babel.parser.ArtifactGeneratorToscaParser; +import org.onap.aai.babel.xml.generator.data.AdditionalParams; +import org.onap.aai.babel.xml.generator.data.Artifact; +import org.onap.aai.babel.xml.generator.data.ArtifactType; +import org.onap.aai.babel.xml.generator.data.GenerationData; +import org.onap.aai.babel.xml.generator.data.GeneratorConstants; +import org.onap.aai.babel.xml.generator.data.GeneratorUtil; +import org.onap.aai.babel.xml.generator.data.GroupType; +import org.onap.aai.babel.xml.generator.model.Model; +import org.onap.aai.babel.xml.generator.model.Resource; +import org.onap.aai.babel.xml.generator.model.Service; +import org.onap.aai.cl.api.Logger; +import org.onap.sdc.tosca.parser.api.ISdcCsarHelper; +import org.onap.sdc.tosca.parser.impl.SdcToscaParserFactory; +import org.onap.sdc.toscaparser.api.NodeTemplate; +import org.slf4j.MDC; + +public class AaiArtifactGenerator implements ArtifactGenerator { + + private static final String ARTIFACT_MODEL_INFO = "ARTIFACT_MODEL_INFO"; + + private static Logger log = LogHelper.INSTANCE; + + @Override + public GenerationData generateArtifact(byte[] csarArchive, List input, + Map additionalParams) { + Path path = null; + + try { + ArtifactGeneratorToscaParser.initWidgetConfiguration(); + String serviceVersion = validateServiceVersion(additionalParams); + GenerationData generationData = new GenerationData(); + + path = createTempFile(csarArchive); + if (path != null) { + ISdcCsarHelper csarHelper = + SdcToscaParserFactory.getInstance().getSdcCsarHelper(path.toAbsolutePath().toString()); + + List serviceNodes = + csarHelper.getServiceNodeTemplates(); + Map serviceMetaData = csarHelper.getServiceMetadataAllProperties(); + + if (serviceNodes == null) { + throw new IllegalArgumentException(GeneratorConstants.GENERATOR_AAI_ERROR_MISSING_SERVICE_TOSCA); + } + + // Populate basic service model metadata + Service serviceModel = new Service(); + serviceModel.populateModelIdentificationInformation(serviceMetaData); + serviceModel.setModelVersion(serviceVersion); + + Map idTypeStore = new HashMap<>(); + + ArtifactGeneratorToscaParser parser = new ArtifactGeneratorToscaParser(csarHelper); + if (!serviceNodes.isEmpty()) { + parser.processServiceTosca(serviceModel, idTypeStore, serviceNodes); + } + + // Process the resource TOSCA files + List resources = parser.processResourceToscas(serviceNodes, idTypeStore); + + // Generate AAI XML service model + AaiModelGenerator modelGenerator = AaiModelGenerator.getInstance(); + MDC.put(ARTIFACT_MODEL_INFO, serviceModel.getModelName() + "," + getArtifactLabel(serviceModel)); + String aaiServiceModel = modelGenerator.generateModelFor(serviceModel); + generationData.add(getServiceArtifact(serviceModel, aaiServiceModel)); + + // Generate AAI XML resource model + for (Resource res : resources) { + MDC.put(ARTIFACT_MODEL_INFO, res.getModelName() + "," + getArtifactLabel(res)); + String aaiResourceModel = modelGenerator.generateModelFor(res); + generationData.add(getResourceArtifact(res, aaiResourceModel)); + + } + } + return generationData; + } catch (Exception e) { + log.error(ApplicationMsgs.INVALID_CSAR_FILE, e); + GenerationData generationData = new GenerationData(); + generationData.add(ArtifactType.AAI.name(), e.getMessage()); + return generationData; + + } finally { + if (path != null) { + FileUtils.deleteQuietly(path.toFile()); + } + } + } + + private Path createTempFile(byte[] bytes) { + Path path = null; + try { + log.debug("Creating temp file on file system for the csar"); + path = Files.createTempFile("temp", ".csar"); + Files.write(path, bytes); + } catch (IOException e) { + log.error(ApplicationMsgs.TEMP_FILE_ERROR, e); + } + return path; + } + + /** + * Method to generate the artifact label for AAI model + * + * @param model + * @return the artifact label as String + */ + public String getArtifactLabel(Model model) { + StringBuilder artifactName = new StringBuilder(ArtifactType.AAI.name()); + artifactName.append("-"); + artifactName.append(model.getModelType().name().toLowerCase()); + artifactName.append("-"); + artifactName.append(hashCodeUuId(model.getModelNameVersionId())); + return (artifactName.toString()).replaceAll("[^a-zA-Z0-9 +]+", "-"); + } + + /** + * Method to generate the artifact name for an AAI model. + * + * @param model AAI artifact model + * @return Model artifact name + */ + private String getArtifactName(Model model) { + StringBuilder artifactName = new StringBuilder(ArtifactType.AAI.name()); + artifactName.append("-"); + + String truncatedArtifactName = truncateName(model.getModelName()); + artifactName.append(truncatedArtifactName); + + artifactName.append("-"); + artifactName.append(model.getModelType().name().toLowerCase()); + artifactName.append("-"); + artifactName.append(model.getModelVersion()); + + artifactName.append("."); + artifactName.append(GeneratorConstants.GENERATOR_AAI_GENERATED_ARTIFACT_EXTENSION); + return artifactName.toString(); + } + + /** + * Create Resource artifact model from the AAI xml model string. + * + * @param resourceModel Model of the resource artifact + * @param aaiResourceModel AAI model as string + * @return Generated {@link Artifact} model for the resource + */ + private Artifact getResourceArtifact(Model resourceModel, String aaiResourceModel) { + Artifact artifact = new Artifact(ArtifactType.MODEL_INVENTORY_PROFILE.name(), GroupType.DEPLOYMENT.name(), + GeneratorUtil.checkSum(aaiResourceModel.getBytes()), GeneratorUtil.encode(aaiResourceModel.getBytes())); + String resourceArtifactName = getArtifactName(resourceModel); + String resourceArtifactLabel = getArtifactLabel(resourceModel); + artifact.setName(resourceArtifactName); + artifact.setLabel(resourceArtifactLabel); + String description = ArtifactGeneratorToscaParser.getArtifactDescription(resourceModel); + artifact.setDescription(description); + return artifact; + } + + /** + * Create Service artifact model from the AAI xml model string. + * + * @param serviceModel Model of the service artifact + * @param aaiServiceModel AAI model as string + * @return Generated {@link Artifact} model for the service + */ + private Artifact getServiceArtifact(Service serviceModel, String aaiServiceModel) { + Artifact artifact = new Artifact(ArtifactType.MODEL_INVENTORY_PROFILE.name(), GroupType.DEPLOYMENT.name(), + GeneratorUtil.checkSum(aaiServiceModel.getBytes()), GeneratorUtil.encode(aaiServiceModel.getBytes())); + String serviceArtifactName = getArtifactName(serviceModel); + String serviceArtifactLabel = getArtifactLabel(serviceModel); + artifact.setName(serviceArtifactName); + artifact.setLabel(serviceArtifactLabel); + String description = ArtifactGeneratorToscaParser.getArtifactDescription(serviceModel); + artifact.setDescription(description); + return artifact; + } + + private int hashCodeUuId(String uuId) { + int hashcode = 0; + for (int i = 0; i < uuId.length(); i++) { + hashcode = 31 * hashcode + uuId.charAt(i); + } + return hashcode; + } + + private String truncateName(String name) { + String truncatedName = name; + if (name.length() >= 200) { + truncatedName = name.substring(0, 199); + } + return truncatedName; + } + + private String validateServiceVersion(Map additionalParams) { + String serviceVersion; + serviceVersion = additionalParams.get(AdditionalParams.SERVICE_VERSION.getName()); + if (serviceVersion == null) { + throw new IllegalArgumentException(GeneratorConstants.GENERATOR_AAI_ERROR_MISSING_SERVICE_VERSION); + } else { + String versionRegex = "^[1-9]\\d*(\\.0)$"; + if (!(serviceVersion.matches(versionRegex))) { + throw new IllegalArgumentException( + String.format(GeneratorConstants.GENERATOR_AAI_INVALID_SERVICE_VERSION)); + } + } + return serviceVersion; + } +} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/api/AaiModelGenerator.java b/src/main/java/org/onap/aai/babel/xml/generator/api/AaiModelGenerator.java new file mode 100644 index 0000000..f8571f3 --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/api/AaiModelGenerator.java @@ -0,0 +1,64 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.api; + +import static org.onap.aai.babel.xml.generator.data.GeneratorConstants.ERROR_CATEGORY; +import static org.onap.aai.babel.xml.generator.data.GeneratorConstants.ERROR_CODE; +import static org.onap.aai.babel.xml.generator.data.GeneratorConstants.ERROR_DESCRIPTION; +import static org.onap.aai.babel.xml.generator.data.GeneratorConstants.GENERATOR_ERROR_CODE; +import static org.onap.aai.babel.xml.generator.data.GeneratorConstants.GENERATOR_ERROR_SERVICE_INSTANTIATION_FAILED; +import static org.onap.aai.babel.xml.generator.data.GeneratorConstants.GENERATOR_PARTNER_NAME; +import static org.onap.aai.babel.xml.generator.data.GeneratorConstants.PARTNER_NAME; + +import org.onap.aai.babel.logging.ApplicationMsgs; +import org.onap.aai.babel.logging.LogHelper; +import org.onap.aai.babel.xml.generator.logging.CategoryLogLevel; +import org.onap.aai.babel.xml.generator.model.Resource; +import org.onap.aai.babel.xml.generator.model.Service; +import org.onap.aai.cl.api.Logger; +import org.slf4j.MDC; + +public interface AaiModelGenerator { + + /** + * Gets instance. + * + * @return the instance + */ + public static AaiModelGenerator getInstance() { + Logger log = LogHelper.INSTANCE; + try { + return AaiModelGenerator.class + .cast(Class.forName("org.onap.aai.babel.xml.generator.api.AaiModelGeneratorImpl").newInstance()); + } catch (Exception exception) { + MDC.put(PARTNER_NAME, GENERATOR_PARTNER_NAME); + MDC.put(ERROR_CATEGORY, CategoryLogLevel.ERROR.name()); + MDC.put(ERROR_CODE, GENERATOR_ERROR_CODE); + MDC.put(ERROR_DESCRIPTION, GENERATOR_ERROR_SERVICE_INSTANTIATION_FAILED); + log.error(ApplicationMsgs.PROCESS_REQUEST_ERROR, exception); + } + return null; + } + + public String generateModelFor(Service service); + + public String generateModelFor(Resource resource); +} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/api/AaiModelGeneratorImpl.java b/src/main/java/org/onap/aai/babel/xml/generator/api/AaiModelGeneratorImpl.java new file mode 100644 index 0000000..488faae --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/api/AaiModelGeneratorImpl.java @@ -0,0 +1,260 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.api; + +import java.io.StringWriter; +import java.util.List; +import java.util.Set; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import org.onap.aai.babel.logging.ApplicationMsgs; +import org.onap.aai.babel.logging.LogHelper; +import org.onap.aai.babel.xml.generator.model.Resource; +import org.onap.aai.babel.xml.generator.model.Service; +import org.onap.aai.babel.xml.generator.model.Widget; +import org.onap.aai.babel.xml.generator.xsd.Model; +import org.onap.aai.babel.xml.generator.xsd.ModelElement; +import org.onap.aai.babel.xml.generator.xsd.ModelElements; +import org.onap.aai.babel.xml.generator.xsd.ModelVer; +import org.onap.aai.babel.xml.generator.xsd.ModelVers; +import org.onap.aai.babel.xml.generator.xsd.Relationship; +import org.onap.aai.babel.xml.generator.xsd.RelationshipData; +import org.onap.aai.babel.xml.generator.xsd.RelationshipList; +import org.onap.aai.cl.api.Logger; +import org.w3c.dom.DOMException; + +/** + * Implementation of the {@link AaiModelGenerator} which generates the XML models from the Service/Resource/Widget java + * models. + */ +public class AaiModelGeneratorImpl implements AaiModelGenerator { + private static Logger log = LogHelper.INSTANCE; + + /** + * Method to generate the AAI model for a Service. + * + * @param service Java object model representing an AAI {@link Service} model + * @return XML representation of the service model in String format + */ + @Override + public String generateModelFor(Service service) { + // Create a JAXB Model for AAI service model + Model aaiServiceModel = new Model(); + log.debug("Generating Model for Service with ModelName: " + service.getModelName()); + // after new model + aaiServiceModel.setModelInvariantId(service.getModelId()); + aaiServiceModel.setModelVers(new ModelVers()); + ModelVer modelVer = new ModelVer(); + modelVer.setModelDescription(service.getModelDescription()); + modelVer.setModelName(service.getModelName()); + modelVer.setModelVersion(service.getModelVersion()); + modelVer.setModelVersionId(service.getModelNameVersionId()); + modelVer.setModelElements(new ModelElements()); + ModelElements modelElements = modelVer.getModelElements(); + // Populate basic model details + aaiServiceModel.setModelType(service.getModelType().name().toLowerCase()); // Using enum name as model type + List modelElementList = modelElements.getModelElement(); + + // Add service base widget model element + ModelElement serviceWidgetModelRelationshipElement = createRelationshipModelElement( + getNewDataDelFlagValue(service.getDeleteFlag()), service.getWidgetId(), service.getWidgetInvariantId()); + modelElementList.add(serviceWidgetModelRelationshipElement); + + // Add the resource model elements + ModelElements serviceModelElements = serviceWidgetModelRelationshipElement.getModelElements(); + List serviceModelElementList = serviceModelElements.getModelElement(); + Set serviceResources = service.getResources(); + if (serviceResources != null && !serviceResources.isEmpty()) { + for (Resource resourceModel : serviceResources) { + ModelElement aaiResourceModelElement = + createRelationshipModelElement(getNewDataDelFlagValue(resourceModel.getDeleteFlag()), + resourceModel.getModelNameVersionId(), resourceModel.getModelId()); + serviceModelElementList.add(aaiResourceModelElement); + } + } + + // Add the widget model elements + Set serviceWidgets = service.getWidgets(); + if (serviceWidgets != null && !serviceWidgets.isEmpty()) { + for (Widget widgetModel : serviceWidgets) { + ModelElement widgetModelElement = + createRelationshipModelElement(getNewDataDelFlagValue(widgetModel.getDeleteFlag()), + widgetModel.getId(), widgetModel.getWidgetId()); + serviceModelElementList.add(widgetModelElement); + } + } + ModelVers modelVers = aaiServiceModel.getModelVers(); + List modelVerList = modelVers.getModelVer(); + modelVerList.add(modelVer); + return getModelAsString(aaiServiceModel); + } + + /** + * Method to generate the AAI model for a Resource. + * + * @param resource Java object model representing an AAI {@link Resource} model + * @return XML representation of the resource model in String format + */ + @Override + public String generateModelFor(Resource resource) { + // Create a JAXB Model for AAI Resource model + Model aaiResourceModel = new Model(); + log.debug("Generating Model for Resource with ModelName: " + resource.getModelName()); + aaiResourceModel.setModelInvariantId(resource.getModelId()); + aaiResourceModel.setModelVers(new ModelVers()); + ModelVer modelVer = new ModelVer(); + modelVer.setModelDescription(resource.getModelDescription()); + modelVer.setModelName(resource.getModelName()); + modelVer.setModelVersion(resource.getModelVersion()); + modelVer.setModelVersionId(resource.getModelNameVersionId()); + modelVer.setModelElements(new ModelElements()); + ModelElements modelElements = modelVer.getModelElements(); + aaiResourceModel.setModelType(resource.getModelType().name().toLowerCase()); // Using enum name as model type + List modelElementList = modelElements.getModelElement(); + + // Add resource base widget model element + ModelElement resourceWidgetModelRelationshipElement = + createRelationshipModelElement(getNewDataDelFlagValue(resource.getDeleteFlag()), resource.getWidgetId(), + resource.getWidgetInvariantId()); + modelElementList.add(resourceWidgetModelRelationshipElement); + + // Add the child resources to the base widget model element list + ModelElements baseResourceWidgetModelElements = resourceWidgetModelRelationshipElement.getModelElements(); + List baseResourceWidgetModelElementList = baseResourceWidgetModelElements.getModelElement(); + Set childResources = resource.getResources(); + if (childResources != null && !childResources.isEmpty()) { + for (Resource childResourceModel : childResources) { + ModelElement aaiChildResourceModelElement = + createRelationshipModelElement(getNewDataDelFlagValue(childResourceModel.getDeleteFlag()), + childResourceModel.getModelNameVersionId(), childResourceModel.getModelId()); + baseResourceWidgetModelElementList.add(aaiChildResourceModelElement); + } + } + // Add resource widgets/resources to the resource widget model relationship element + Set resourceWidgets = resource.getWidgets(); + if (resourceWidgets != null && !resourceWidgets.isEmpty()) { + generateWidgetChildren(resourceWidgetModelRelationshipElement, resourceWidgets); + } + + ModelVers modelVers = aaiResourceModel.getModelVers(); + List modelVerList = modelVers.getModelVer(); + modelVerList.add(modelVer); + return getModelAsString(aaiResourceModel); + } + + /** + * Method to create the holding the relationship value for a resource/widget model. + * + * @param newDataDelFlag Value of the attribute for a widget/resource in the + * model xml + * @param relationshipValue Value of the attribute for the widget/resource + * in the model xml + * @return Java object representation for the holding the relationship + */ + private ModelElement createRelationshipModelElement(String newDataDelFlag, String modelVersionId, + String modelInvariantId) { + ModelElement relationshipModelElement = new ModelElement(); + relationshipModelElement.setNewDataDelFlag(newDataDelFlag); // Set new-data-del-flag value + relationshipModelElement.setCardinality("unbounded"); + RelationshipList relationShipList = new RelationshipList(); + final List relationships = relationShipList.getRelationship(); + Relationship relationship = new Relationship(); + relationship.setRelatedTo("model-ver"); + List relationshipDataList = relationship.getRelationshipData(); + + RelationshipData modelVersionRelationshipData = new RelationshipData(); + modelVersionRelationshipData.setRelationshipKey("model-ver.model-version-id"); + modelVersionRelationshipData.setRelationshipValue(modelVersionId); // Set the widget/resource name-version-uuid + // as value + relationshipDataList.add(modelVersionRelationshipData); + RelationshipData modelInvariantRelationshipData = new RelationshipData(); + modelInvariantRelationshipData.setRelationshipKey("model.model-invariant-id"); + modelInvariantRelationshipData.setRelationshipValue(modelInvariantId); + relationshipDataList.add(modelInvariantRelationshipData); + relationships.add(relationship); + relationshipModelElement.setRelationshipList(relationShipList); + relationshipModelElement.setModelElements(new ModelElements()); + return relationshipModelElement; + } + + /** + * Method to create the child model elements of the widget. Handles the generation of recursive child widget + * elements (if any) + * + * @param parent Reference to the parent widget model element + * @param widgetChildrenSet Set of children obtained from the tosca/widget definition + */ + private void generateWidgetChildren(ModelElement parent, Set widgetChildrenSet) { + for (Widget widget : widgetChildrenSet) { + Set widgetSubChildren = widget.getWidgets(); + if (widgetSubChildren != null && !widgetSubChildren.isEmpty()) { + ModelElement widgetChildRelationshipElement = createRelationshipModelElement( + getNewDataDelFlagValue(widget.getDeleteFlag()), widget.getId(), widget.getWidgetId()); + // Recursive call for getting the children of widgets (if any) + generateWidgetChildren(widgetChildRelationshipElement, widgetSubChildren); + parent.getModelElements().getModelElement().add(widgetChildRelationshipElement); + } else { + ModelElement widgetChildRelationshipElement = createRelationshipModelElement( + getNewDataDelFlagValue(widget.getDeleteFlag()), widget.getId(), widget.getWidgetId()); + parent.getModelElements().getModelElement().add(widgetChildRelationshipElement); + } + } + } + + /** + * Converts the data delete flag value from boolean to String as per AAI model. + * + * @param delFlag Boolean value as true/false from the annotation + * @return Converted value to a flag as per AAI model + */ + private String getNewDataDelFlagValue(boolean delFlag) { + if (delFlag) { + return "T"; + } else { + return "F"; + } + } + + /** + * JAXB marshalling helper method to convert the Java object model to XML String. + * + * @param model Java Object model of a service/widget/resource + * @return XML representation of the Java model in String format + */ + private String getModelAsString(Model model) { + JAXBContext jaxbContext; + StringWriter modelStringWriter = new StringWriter(); + try { + jaxbContext = JAXBContext.newInstance(Model.class); + Marshaller jaxbMarshaller = jaxbContext.createMarshaller(); + jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); + jaxbMarshaller.setProperty(Marshaller.JAXB_ENCODING, "US-ASCII"); + jaxbMarshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE); + jaxbMarshaller.marshal(model, modelStringWriter); + } catch (JAXBException jaxbException) { + log.error(ApplicationMsgs.INVALID_CSAR_FILE, jaxbException); + throw new DOMException(DOMException.SYNTAX_ERR, jaxbException.getMessage()); + } + + return modelStringWriter.toString(); + } +} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/api/ArtifactGenerator.java b/src/main/java/org/onap/aai/babel/xml/generator/api/ArtifactGenerator.java new file mode 100644 index 0000000..b5c8f5b --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/api/ArtifactGenerator.java @@ -0,0 +1,41 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.api; + +import java.util.List; +import java.util.Map; +import org.onap.aai.babel.xml.generator.data.Artifact; +import org.onap.aai.babel.xml.generator.data.GenerationData; + +/** Artifact Generation. Note that there is only one implementation of this interface currently. */ +public interface ArtifactGenerator { + + /** + * Implementation of the method to generate AAI artifacts. + * + * @param csarArchive original CSAR (zip format) + * @param input List of input tosca files + * @param additionalParams + * @return Translated/Error data as a {@link GenerationData} object + */ + public GenerationData generateArtifact(byte[] csarArchive, List input, + Map additionalParams); +} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/data/AdditionalParams.java b/src/main/java/org/onap/aai/babel/xml/generator/data/AdditionalParams.java new file mode 100644 index 0000000..00b276c --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/data/AdditionalParams.java @@ -0,0 +1,35 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.data; + +public enum AdditionalParams { + SERVICE_VERSION("serviceVersion"); + + private String name; + + AdditionalParams(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/data/Artifact.java b/src/main/java/org/onap/aai/babel/xml/generator/data/Artifact.java new file mode 100644 index 0000000..50dfc89 --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/data/Artifact.java @@ -0,0 +1,96 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.data; + +public class Artifact { + + String name; + String type; + String groupType; + String description; + String label; + String version; + String checksum; + byte[] payload; + + /** + * Instantiates a new Artifact. + * + * @param type the type + * @param groupType the group type + * @param checksum the checksum + * @param payload the payload + */ + public Artifact(String type, String groupType, String checksum, byte[] payload) { + this.type = type; + this.groupType = groupType; + this.checksum = checksum; + this.payload = payload; + } + + public byte[] getPayload() { + return payload; + } + + public String getChecksum() { + return checksum; + } + + public String getType() { + return type; + } + + public String getGroupType() { + return groupType; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } +} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/data/ArtifactType.java b/src/main/java/org/onap/aai/babel/xml/generator/data/ArtifactType.java new file mode 100644 index 0000000..572342a --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/data/ArtifactType.java @@ -0,0 +1,27 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.data; + +public enum ArtifactType { + OTHER, + AAI, + MODEL_INVENTORY_PROFILE +} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/data/GenerationData.java b/src/main/java/org/onap/aai/babel/xml/generator/data/GenerationData.java new file mode 100644 index 0000000..f59cb66 --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/data/GenerationData.java @@ -0,0 +1,65 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.data; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class GenerationData { + + List resultData = new ArrayList<>(); + Map> errorData = new HashMap<>(); + + public void add(List resultData, Map> errorData) { + this.resultData.addAll(resultData); + this.errorData.putAll(errorData); + } + + public void add(Artifact generatedArtifact) { + resultData.add(generatedArtifact); + } + + /** + * Add the error code to the list of error codes for the given ID + * + * @param generatorId the generator id + * @param errorCode the error code + */ + public void add(String generatorId, String errorCode) { + errorData.computeIfAbsent(generatorId, k -> new ArrayList<>()); + errorData.get(generatorId).add(errorCode); + } + + public void add(GenerationData generationData) { + this.resultData.addAll(generationData.resultData); + this.errorData.putAll(generationData.errorData); + } + + public List getResultData() { + return resultData; + } + + public Map> getErrorData() { + return errorData; + } +} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/data/GeneratorConstants.java b/src/main/java/org/onap/aai/babel/xml/generator/data/GeneratorConstants.java new file mode 100644 index 0000000..40e8eb9 --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/data/GeneratorConstants.java @@ -0,0 +1,92 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.data; + +public class GeneratorConstants { + + /* + * Private constructor to prevent instantiation + */ + private GeneratorConstants() { + throw new UnsupportedOperationException("This static class should not be instantiated!"); + } + + public static final String PROPERTY_ARTIFACT_GENERATOR_CONFIG_FILE = "artifactgenerator.config"; + + public static final String VERSION = "version"; + public static final String CATEGORY = "category"; + public static final String SUBCATEGORY = "subcategory"; + public static final int ID_LENGTH = 36; + + public static final String GENERATOR_AAI_GENERATED_ARTIFACT_EXTENSION = "xml"; + + // Error codes + public static final String GENERATOR_INVOCATION_ERROR_CODE = "ARTIFACT_GENERATOR_INVOCATION_ERROR"; + + // Error Constants + public static final String GENERATOR_ERROR_INVALID_CLIENT_CONFIGURATION = "Invalid Client Configuration"; + public static final String GENERATOR_ERROR_ARTIFACT_GENERATION_FAILED = + "Unable to generate artifacts for the provided input"; + public static final String GENERATOR_ERROR_SERVICE_INSTANTIATION_FAILED = + "Artifact Generation Service Instantiation failed"; + + // AAI Generator Error Messages + public static final String GENERATOR_AAI_ERROR_CHECKSUM_MISMATCH = "Checksum Mismatch for file : %s"; + public static final String GENERATOR_AAI_ERROR_INVALID_TOSCA = "Invalid format for Tosca YML : %s"; + public static final String GENERATOR_AAI_ERROR_UNSUPPORTED_WIDGET_OPERATION = "Operation Not Supported for Widgets"; + public static final String GENERATOR_AAI_ERROR_MISSING_SERVICE_TOSCA = + "Service tosca missing from list of input artifacts"; + public static final String GENERATOR_AAI_ERROR_NULL_RESOURCE_VERSION_IN_SERVICE_TOSCA = + "Invalid Service definition mandatory attribute version missing for resource with UUID: <%s>"; + + public static final String GENERATOR_AAI_ERROR_INVALID_RESOURCE_VERSION_IN_SERVICE_TOSCA = + "Cannot generate artifacts. Invalid Resource version in Service tosca for resource with " + "UUID: " + + "<%s>"; + public static final String GENERATOR_AAI_ERROR_MISSING_RESOURCE_TOSCA = + "Cannot generate artifacts. Resource Tosca missing for resource with UUID: <%s>"; + + public static final String GENERATOR_AAI_ERROR_MISSING_SERVICE_VERSION = + "Cannot generate artifacts. Service version is not specified"; + + public static final String GENERATOR_AAI_INVALID_SERVICE_VERSION = + "Cannot generate artifacts. Service version is incorrect"; + + // Logging constants + public static final String PARTNER_NAME = "userId"; + public static final String ERROR_CATEGORY = "ErrorCategory"; + public static final String ERROR_CODE = "ErrorCode"; + public static final String ERROR_DESCRIPTION = "ErrorDescription"; + + public static final String GENERATOR_ERROR_CODE = "300F"; + public static final String GENERATOR_PARTNER_NAME = "SDC Catalog"; + + // AAI Generator Error Messages for Logging + public static final String GENERATOR_AAI_CONFIGFILE_NOT_FOUND = + "Cannot generate artifacts. Artifact Generator Configuration file not found at %s"; + public static final String GENERATOR_AAI_CONFIGLOCATION_NOT_FOUND = + "Cannot generate artifacts. artifactgenerator.config system property not configured"; + public static final String GENERATOR_AAI_CONFIGLPROP_NOT_FOUND = + "Cannot generate artifacts. Widget configuration not found for %s"; + public static final String GENERATOR_AAI_PROVIDING_SERVICE_MISSING = + "Cannot generate artifacts. Providing Service is missing for allotted resource %s"; + public static final String GENERATOR_AAI_PROVIDING_SERVICE_METADATA_MISSING = + "Cannot generate artifacts. Providing Service Metadata is missing for allotted resource %s"; +} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/data/GeneratorUtil.java b/src/main/java/org/onap/aai/babel/xml/generator/data/GeneratorUtil.java new file mode 100644 index 0000000..905c859 --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/data/GeneratorUtil.java @@ -0,0 +1,65 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.data; + +import java.util.Base64; +import org.apache.commons.codec.digest.DigestUtils; + +/** Utility method class for artifact generation. */ +public class GeneratorUtil { + + /* + * Private constructor to prevent instantiation + */ + private GeneratorUtil() { + throw new UnsupportedOperationException("This static class should not be instantiated!"); + } + + /** + * Decodes Base64 encode byte array input. + * + * @param input Base64 encoded byte array + * @return Decoded byte array + */ + public static byte[] decoder(byte[] input) { + return input != null ? Base64.getDecoder().decode(input) : new byte[0]; + } + + /** + * Encode a byte array input using Base64 encoding. + * + * @param input Input byte array to be encoded + * @return Base64 encoded byte array + */ + public static byte[] encode(byte[] input) { + return input != null ? Base64.getEncoder().encode(input) : new byte[0]; + } + + /** + * Calculate the checksum for a given input. + * + * @param input Byte array for which the checksum has to be calculated + * @return Calculated checksum of the input byte array + */ + public static String checkSum(byte[] input) { + return input != null ? DigestUtils.md5Hex(input).toUpperCase() : null; + } +} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/data/GroupType.java b/src/main/java/org/onap/aai/babel/xml/generator/data/GroupType.java new file mode 100644 index 0000000..b2bc3f9 --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/data/GroupType.java @@ -0,0 +1,26 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.data; + +public enum GroupType { + DEPLOYMENT, + OTHER +} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/data/WidgetConfigurationUtil.java b/src/main/java/org/onap/aai/babel/xml/generator/data/WidgetConfigurationUtil.java new file mode 100644 index 0000000..d36982d --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/data/WidgetConfigurationUtil.java @@ -0,0 +1,43 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.data; + +import java.util.Properties; + +public class WidgetConfigurationUtil { + + private static Properties config; + + /* + * Private constructor to prevent instantiation + */ + private WidgetConfigurationUtil() { + throw new UnsupportedOperationException("This static class should not be instantiated!"); + } + + public static Properties getConfig() { + return config; + } + + public static void setConfig(Properties config) { + WidgetConfigurationUtil.config = config; + } +} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/error/IllegalAccessException.java b/src/main/java/org/onap/aai/babel/xml/generator/error/IllegalAccessException.java new file mode 100644 index 0000000..3c907cb --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/error/IllegalAccessException.java @@ -0,0 +1,30 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.error; + +public class IllegalAccessException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public IllegalAccessException(String message) { + super(message); + } +} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/logging/CategoryLogLevel.java b/src/main/java/org/onap/aai/babel/xml/generator/logging/CategoryLogLevel.java new file mode 100644 index 0000000..b6ca2d3 --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/logging/CategoryLogLevel.java @@ -0,0 +1,29 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.logging; + +public enum CategoryLogLevel { + INFO, + WARN, + DEBUG, + ERROR, + FATAL +} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/model/AllotedResource.java b/src/main/java/org/onap/aai/babel/xml/generator/model/AllotedResource.java new file mode 100644 index 0000000..cecf7bd --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/model/AllotedResource.java @@ -0,0 +1,28 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.model; + +import org.onap.aai.babel.xml.generator.types.Cardinality; +import org.onap.aai.babel.xml.generator.types.Model; + +@Model(widget = Widget.Type.ALLOTTED_RESOURCE, cardinality = Cardinality.UNBOUNDED, dataDeleteFlag = true) +public class AllotedResource extends Resource { +} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/model/AllotedResourceWidget.java b/src/main/java/org/onap/aai/babel/xml/generator/model/AllotedResourceWidget.java new file mode 100644 index 0000000..740ca62 --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/model/AllotedResourceWidget.java @@ -0,0 +1,31 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.model; + +import org.onap.aai.babel.xml.generator.types.Cardinality; +import org.onap.aai.babel.xml.generator.types.ModelType; +import org.onap.aai.babel.xml.generator.types.ModelWidget; + +@org.onap.aai.babel.xml.generator.types.Model(widget = Widget.Type.ALLOTTED_RESOURCE, + cardinality = Cardinality.UNBOUNDED, dataDeleteFlag = true) +@ModelWidget(type = ModelType.WIDGET, name = "allotted-resource") +public class AllotedResourceWidget extends ResourceWidget { +} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/model/FlavorWidget.java b/src/main/java/org/onap/aai/babel/xml/generator/model/FlavorWidget.java new file mode 100644 index 0000000..26dde9b --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/model/FlavorWidget.java @@ -0,0 +1,31 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.model; + +import org.onap.aai.babel.xml.generator.types.Cardinality; +import org.onap.aai.babel.xml.generator.types.Model; +import org.onap.aai.babel.xml.generator.types.ModelType; +import org.onap.aai.babel.xml.generator.types.ModelWidget; + +@Model(widget = Widget.Type.FLAVOR, cardinality = Cardinality.UNBOUNDED, dataDeleteFlag = false) +@ModelWidget(type = ModelType.WIDGET, name = "flavor") +public class FlavorWidget extends ResourceWidget { +} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/model/ImageWidget.java b/src/main/java/org/onap/aai/babel/xml/generator/model/ImageWidget.java new file mode 100644 index 0000000..60656b2 --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/model/ImageWidget.java @@ -0,0 +1,31 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.model; + +import org.onap.aai.babel.xml.generator.types.Cardinality; +import org.onap.aai.babel.xml.generator.types.Model; +import org.onap.aai.babel.xml.generator.types.ModelType; +import org.onap.aai.babel.xml.generator.types.ModelWidget; + +@Model(widget = Widget.Type.IMAGE, cardinality = Cardinality.UNBOUNDED, dataDeleteFlag = false) +@ModelWidget(type = ModelType.WIDGET, name = "image") +public class ImageWidget extends ResourceWidget { +} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/model/L3Network.java b/src/main/java/org/onap/aai/babel/xml/generator/model/L3Network.java new file mode 100644 index 0000000..105c3a1 --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/model/L3Network.java @@ -0,0 +1,28 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.model; + +import org.onap.aai.babel.xml.generator.types.Cardinality; + +@org.onap.aai.babel.xml.generator.types.Model(widget = Widget.Type.L3_NET, cardinality = Cardinality.UNBOUNDED, + dataDeleteFlag = false) +public class L3Network extends Resource { +} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/model/L3NetworkWidget.java b/src/main/java/org/onap/aai/babel/xml/generator/model/L3NetworkWidget.java new file mode 100644 index 0000000..f737b82 --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/model/L3NetworkWidget.java @@ -0,0 +1,31 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.model; + +import org.onap.aai.babel.xml.generator.types.Cardinality; +import org.onap.aai.babel.xml.generator.types.ModelType; +import org.onap.aai.babel.xml.generator.types.ModelWidget; + +@org.onap.aai.babel.xml.generator.types.Model(widget = Widget.Type.L3_NET, cardinality = Cardinality.UNBOUNDED, + dataDeleteFlag = true) +@ModelWidget(type = ModelType.WIDGET, name = "l3-network") +public class L3NetworkWidget extends Widget { +} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/model/LIntfWidget.java b/src/main/java/org/onap/aai/babel/xml/generator/model/LIntfWidget.java new file mode 100644 index 0000000..fb07ef6 --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/model/LIntfWidget.java @@ -0,0 +1,31 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.model; + +import org.onap.aai.babel.xml.generator.types.Cardinality; +import org.onap.aai.babel.xml.generator.types.Model; +import org.onap.aai.babel.xml.generator.types.ModelType; +import org.onap.aai.babel.xml.generator.types.ModelWidget; + +@Model(widget = Widget.Type.LINT, cardinality = Cardinality.UNBOUNDED, dataDeleteFlag = true) +@ModelWidget(type = ModelType.WIDGET, name = "l-interface") +public class LIntfWidget extends ResourceWidget { +} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/model/Model.java b/src/main/java/org/onap/aai/babel/xml/generator/model/Model.java new file mode 100644 index 0000000..262d29a --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/model/Model.java @@ -0,0 +1,248 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.model; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import org.onap.aai.babel.xml.generator.data.GeneratorConstants; +import org.onap.aai.babel.xml.generator.error.IllegalAccessException; +import org.onap.aai.babel.xml.generator.types.Cardinality; +import org.onap.aai.babel.xml.generator.types.ModelType; + +public abstract class Model { + + protected Set resources = new HashSet<>(); + protected Set widgets = new HashSet<>(); + private String modelId; + private String modelName; + private String modelVersion; + private String modelNameVersionId; + private String modelDescription; + + /** + * Gets the object (model) corresponding to the supplied TOSCA type. + * + * @param toscaType the tosca type + * @return the model for the type, or null + */ + public static Model getModelFor(String toscaType) { + Model modelToBeReturned = null; + String typePrefix = toscaType; + while (modelToBeReturned == null && typePrefix != null && typePrefix.lastIndexOf('.') != -1) { + switch (typePrefix) { + case "org.openecomp.resource.vf.allottedResource": + modelToBeReturned = new AllotedResource(); + break; + case "org.openecomp.resource.vfc.AllottedResource": + modelToBeReturned = new ProvidingService(); + break; + case "org.openecomp.resource.vfc": + modelToBeReturned = new VServerWidget(); + break; + case "org.openecomp.resource.cp": + case "org.openecomp.cp": + modelToBeReturned = new LIntfWidget(); + break; + case "org.openecomp.resource.vl": + modelToBeReturned = new L3Network(); + break; + case "org.openecomp.resource.vf": + modelToBeReturned = new VirtualFunction(); + break; + case "org.openecomp.groups.vfmodule": + case "org.openecomp.groups.VfModule": + modelToBeReturned = new VfModule(); + break; + case "org.openecomp.resource.vfc.nodes.heat.cinder": + modelToBeReturned = new VolumeWidget(); + break; + default: + modelToBeReturned = null; + break; + } + typePrefix = typePrefix.substring(0, typePrefix.lastIndexOf('.')); + } + + return modelToBeReturned; + } + + public abstract boolean addResource(Resource resource); + + public abstract boolean addWidget(Widget resource); + + /** + * Gets widget version id. + * + * @return the widget version id + */ + public String getWidgetId() { + org.onap.aai.babel.xml.generator.types.Model model = + this.getClass().getAnnotation(org.onap.aai.babel.xml.generator.types.Model.class); + return Widget.getWidget(model.widget()).getId(); + } + + /** + * Gets invariant id. + * + * @return the invariant id + */ + public String getWidgetInvariantId() { + org.onap.aai.babel.xml.generator.types.Model model = + this.getClass().getAnnotation(org.onap.aai.babel.xml.generator.types.Model.class); + return Widget.getWidget(model.widget()).getWidgetId(); + } + + /** + * Gets delete flag. + * + * @return the delete flag + */ + public boolean getDeleteFlag() { + org.onap.aai.babel.xml.generator.types.Model model = + this.getClass().getAnnotation(org.onap.aai.babel.xml.generator.types.Model.class); + return model.dataDeleteFlag(); + } + + /** + * Gets cardinality. + * + * @return the cardinality + */ + public Cardinality getCardinality() { + org.onap.aai.babel.xml.generator.types.Model model = + this.getClass().getAnnotation(org.onap.aai.babel.xml.generator.types.Model.class); + return model.cardinality(); + } + + public abstract Widget.Type getWidgetType(); + + public String getModelId() { + checkSupported(); + return modelId; + } + + /** + * Gets model type. + * + * @return the model type + */ + public ModelType getModelType() { + if (this instanceof Service) { + return ModelType.SERVICE; + } else if (this instanceof Resource) { + return ModelType.RESOURCE; + } else if (this instanceof Widget) { + return ModelType.WIDGET; + } else { + return null; + } + } + + public String getModelName() { + return modelName; + } + + public String getModelVersion() { + return modelVersion; + } + + public String getModelNameVersionId() { + checkSupported(); + return modelNameVersionId; + } + + public String getModelDescription() { + return modelDescription; + } + + /** + * Populate model identification information. + * + * @param modelIdentInfo the model ident info + */ + public void populateModelIdentificationInformation(Map modelIdentInfo) { + Iterator iter = modelIdentInfo.keySet().iterator(); + String property; + while (iter.hasNext()) { + property = iter.next(); + switch (property) { + case "vfModuleModelInvariantUUID": + case "serviceInvariantUUID": + case "resourceInvariantUUID": + case "invariantUUID": + case "providing_service_invariant_uuid": + modelId = modelIdentInfo.get(property); + break; + case "vfModuleModelUUID": + case "resourceUUID": + case "serviceUUID": + case "UUID": + case "providing_service_uuid": + modelNameVersionId = modelIdentInfo.get(property); + break; + case "vfModuleModelVersion": + case "serviceVersion": + case "resourceversion": + case "version": + modelVersion = modelIdentInfo.get(property); + break; + case "vfModuleModelName": + case "serviceName": + case "resourceName": + case "name": + modelName = modelIdentInfo.get(property); + break; + case "serviceDescription": + case "resourceDescription": + case "vf_module_description": + case "description": + modelDescription = modelIdentInfo.get(property); + break; + case "providing_service_name": + modelName = modelIdentInfo.get(property); + modelDescription = modelIdentInfo.get(property); + break; + default: + break; + } + } + } + + public void setModelVersion(String modelVersion) { + this.modelVersion = modelVersion; + } + + public Set getResources() { + return resources; + } + + public Set getWidgets() { + return widgets; + } + + private void checkSupported() { + if (this instanceof Widget) { + throw new IllegalAccessException(GeneratorConstants.GENERATOR_AAI_ERROR_UNSUPPORTED_WIDGET_OPERATION); + } + } +} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/model/OamNetwork.java b/src/main/java/org/onap/aai/babel/xml/generator/model/OamNetwork.java new file mode 100644 index 0000000..e9076a9 --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/model/OamNetwork.java @@ -0,0 +1,31 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.model; + +import org.onap.aai.babel.xml.generator.types.Cardinality; +import org.onap.aai.babel.xml.generator.types.Model; +import org.onap.aai.babel.xml.generator.types.ModelType; +import org.onap.aai.babel.xml.generator.types.ModelWidget; + +@Model(widget = Widget.Type.L3_NET, cardinality = Cardinality.UNBOUNDED, dataDeleteFlag = true) +@ModelWidget(type = ModelType.WIDGET, name = "oam-network") +public class OamNetwork extends Widget { +} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/model/ProvidingService.java b/src/main/java/org/onap/aai/babel/xml/generator/model/ProvidingService.java new file mode 100644 index 0000000..e25274e --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/model/ProvidingService.java @@ -0,0 +1,28 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.model; + +import org.onap.aai.babel.xml.generator.types.Cardinality; + +@org.onap.aai.babel.xml.generator.types.Model(widget = Widget.Type.ALLOTTED_RESOURCE, + cardinality = Cardinality.UNBOUNDED, dataDeleteFlag = false) +public class ProvidingService extends Resource { +} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/model/Resource.java b/src/main/java/org/onap/aai/babel/xml/generator/model/Resource.java new file mode 100644 index 0000000..9d4feab --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/model/Resource.java @@ -0,0 +1,54 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.model; + +public class Resource extends Model { + + @Override + public int hashCode() { + return getModelNameVersionId().hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Resource) { + return getModelNameVersionId().equals(((Resource) obj).getModelNameVersionId()); + } + return false; + } + + @Override + public boolean addResource(Resource resource) { + return resources.add(resource); + } + + @Override + public boolean addWidget(Widget widget) { + return widgets.add(widget); + } + + @Override + public Widget.Type getWidgetType() { + org.onap.aai.babel.xml.generator.types.Model model = + this.getClass().getAnnotation(org.onap.aai.babel.xml.generator.types.Model.class); + return model.widget(); + } +} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/model/ResourceWidget.java b/src/main/java/org/onap/aai/babel/xml/generator/model/ResourceWidget.java new file mode 100644 index 0000000..a0f84c7 --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/model/ResourceWidget.java @@ -0,0 +1,24 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.model; + +public class ResourceWidget extends Widget { +} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/model/Service.java b/src/main/java/org/onap/aai/babel/xml/generator/model/Service.java new file mode 100644 index 0000000..9d22e41 --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/model/Service.java @@ -0,0 +1,43 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.model; + +import org.onap.aai.babel.xml.generator.types.Cardinality; + +@org.onap.aai.babel.xml.generator.types.Model(widget = Widget.Type.SERVICE, cardinality = Cardinality.UNBOUNDED, + dataDeleteFlag = true) +public class Service extends Model { + + @Override + public boolean addResource(Resource resource) { + return resources.add(resource); + } + + @Override + public boolean addWidget(Widget widget) { + return widgets.add(widget); + } + + @Override + public Widget.Type getWidgetType() { + return null; + } +} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/model/ServiceWidget.java b/src/main/java/org/onap/aai/babel/xml/generator/model/ServiceWidget.java new file mode 100644 index 0000000..7c2229d --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/model/ServiceWidget.java @@ -0,0 +1,28 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.model; + +import org.onap.aai.babel.xml.generator.types.ModelType; +import org.onap.aai.babel.xml.generator.types.ModelWidget; + +@ModelWidget(type = ModelType.WIDGET, name = "service-instance") +public class ServiceWidget extends Widget { +} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/model/TenantWidget.java b/src/main/java/org/onap/aai/babel/xml/generator/model/TenantWidget.java new file mode 100644 index 0000000..a0a4392 --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/model/TenantWidget.java @@ -0,0 +1,31 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.model; + +import org.onap.aai.babel.xml.generator.types.Cardinality; +import org.onap.aai.babel.xml.generator.types.Model; +import org.onap.aai.babel.xml.generator.types.ModelType; +import org.onap.aai.babel.xml.generator.types.ModelWidget; + +@Model(widget = Widget.Type.TENANT, cardinality = Cardinality.UNBOUNDED, dataDeleteFlag = false) +@ModelWidget(type = ModelType.WIDGET, name = "tenant") +public class TenantWidget extends Widget { +} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/model/TunnelXconnectWidget.java b/src/main/java/org/onap/aai/babel/xml/generator/model/TunnelXconnectWidget.java new file mode 100644 index 0000000..548cd6f --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/model/TunnelXconnectWidget.java @@ -0,0 +1,31 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.model; + +import org.onap.aai.babel.xml.generator.types.Cardinality; +import org.onap.aai.babel.xml.generator.types.ModelType; +import org.onap.aai.babel.xml.generator.types.ModelWidget; + +@org.onap.aai.babel.xml.generator.types.Model(widget = Widget.Type.TUNNEL_XCONNECT, cardinality = Cardinality.UNBOUNDED, + dataDeleteFlag = true) +@ModelWidget(type = ModelType.WIDGET, name = "tunnel-xconnect") +public class TunnelXconnectWidget extends Widget { +} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/model/VServerWidget.java b/src/main/java/org/onap/aai/babel/xml/generator/model/VServerWidget.java new file mode 100644 index 0000000..7de0f5b --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/model/VServerWidget.java @@ -0,0 +1,44 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.model; + +import org.onap.aai.babel.xml.generator.types.Cardinality; +import org.onap.aai.babel.xml.generator.types.Model; +import org.onap.aai.babel.xml.generator.types.ModelType; +import org.onap.aai.babel.xml.generator.types.ModelWidget; + +@Model(widget = Widget.Type.VSERVER, cardinality = Cardinality.UNBOUNDED, dataDeleteFlag = true) +@ModelWidget(type = ModelType.WIDGET, name = "vserver") +public class VServerWidget extends Widget { + + /** Instantiates a new V server widget. */ + public VServerWidget() { + addWidget(new FlavorWidget()); + addWidget(new ImageWidget()); + addWidget(new TenantWidget()); + addWidget(new VfcWidget()); + } + + @Override + public boolean addWidget(Widget widget) { + return widgets.add(widget); + } +} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/model/VfModule.java b/src/main/java/org/onap/aai/babel/xml/generator/model/VfModule.java new file mode 100644 index 0000000..8b8913d --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/model/VfModule.java @@ -0,0 +1,109 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.model; + +import java.util.List; +import org.onap.aai.babel.xml.generator.types.Cardinality; +import org.onap.aai.babel.xml.generator.types.Model; + +@Model(widget = Widget.Type.VFMODULE, cardinality = Cardinality.UNBOUNDED, dataDeleteFlag = true) +public class VfModule extends Resource { + + Widget vserver = null; + boolean addlintf = false; + boolean addvolume = false; + + List members; + + public void setMembers(List members) { + this.members = members; + } + + /** + * Adds Widget. + * + * @param widget the widget + * @return the boolean + */ + @Override + public boolean addWidget(Widget widget) { + if (widget.memberOf(members)) { + if (vserver == null && widget.getId().equals(new VServerWidget().getId())) { + addVserverWidget(widget); + } else if (widget.getId().equals(new LIntfWidget().getId())) { + return addLIntfWidget(widget); + } else if (widget.getId().equals(new VolumeWidget().getId())) { + addVolumeWidget(widget); + return true; + } + if (widget.getId().equals(new OamNetwork().getId())) { + return false; + } + return widgets.add(widget); + } + return false; + } + + private void addVolumeWidget(Widget widget) { + if (vserver != null) { + vserver.addWidget(widget); + } else { + addvolume = true; + } + } + + /** + * @param widget + * @return + */ + private boolean addLIntfWidget(Widget widget) { + if (vserver != null) { + vserver.addWidget(widget); + return true; + } else { + addlintf = true; + return false; + } + } + + private void addVserverWidget(Widget widget) { + vserver = widget; + if (addlintf) { + vserver.addWidget(new LIntfWidget()); + } + if (addvolume) { + vserver.addWidget(new VolumeWidget()); + } + } + + @Override + public int hashCode() { + return getModelNameVersionId().hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Resource) { + return getModelNameVersionId().equals(((Resource) obj).getModelNameVersionId()); + } + return false; + } +} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/model/VfModuleWidget.java b/src/main/java/org/onap/aai/babel/xml/generator/model/VfModuleWidget.java new file mode 100644 index 0000000..cc27ca9 --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/model/VfModuleWidget.java @@ -0,0 +1,31 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.model; + +import org.onap.aai.babel.xml.generator.types.Cardinality; +import org.onap.aai.babel.xml.generator.types.ModelType; +import org.onap.aai.babel.xml.generator.types.ModelWidget; + +@org.onap.aai.babel.xml.generator.types.Model(widget = Widget.Type.VFMODULE, cardinality = Cardinality.UNBOUNDED, + dataDeleteFlag = true) +@ModelWidget(type = ModelType.WIDGET, name = "vf-module") +public class VfModuleWidget extends Widget { +} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/model/VfWidget.java b/src/main/java/org/onap/aai/babel/xml/generator/model/VfWidget.java new file mode 100644 index 0000000..614244e --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/model/VfWidget.java @@ -0,0 +1,31 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.model; + +import org.onap.aai.babel.xml.generator.types.Cardinality; +import org.onap.aai.babel.xml.generator.types.ModelType; +import org.onap.aai.babel.xml.generator.types.ModelWidget; + +@org.onap.aai.babel.xml.generator.types.Model(widget = Widget.Type.VF, cardinality = Cardinality.UNBOUNDED, + dataDeleteFlag = true) +@ModelWidget(type = ModelType.WIDGET, name = "generic-vnf") +public class VfWidget extends ResourceWidget { +} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/model/VfcWidget.java b/src/main/java/org/onap/aai/babel/xml/generator/model/VfcWidget.java new file mode 100644 index 0000000..abfa49f --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/model/VfcWidget.java @@ -0,0 +1,31 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.model; + +import org.onap.aai.babel.xml.generator.types.Cardinality; +import org.onap.aai.babel.xml.generator.types.ModelType; +import org.onap.aai.babel.xml.generator.types.ModelWidget; + +@org.onap.aai.babel.xml.generator.types.Model(widget = Widget.Type.VFC, cardinality = Cardinality.UNBOUNDED, + dataDeleteFlag = true) +@ModelWidget(type = ModelType.WIDGET, name = "vnfc") +public class VfcWidget extends ResourceWidget { +} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/model/VirtualFunction.java b/src/main/java/org/onap/aai/babel/xml/generator/model/VirtualFunction.java new file mode 100644 index 0000000..be75d65 --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/model/VirtualFunction.java @@ -0,0 +1,28 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.model; + +import org.onap.aai.babel.xml.generator.types.Cardinality; +import org.onap.aai.babel.xml.generator.types.Model; + +@Model(widget = Widget.Type.VF, cardinality = Cardinality.UNBOUNDED, dataDeleteFlag = true) +public class VirtualFunction extends Resource { +} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/model/VolumeGroupWidget.java b/src/main/java/org/onap/aai/babel/xml/generator/model/VolumeGroupWidget.java new file mode 100644 index 0000000..3a0aa37 --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/model/VolumeGroupWidget.java @@ -0,0 +1,31 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.model; + +import org.onap.aai.babel.xml.generator.types.Cardinality; +import org.onap.aai.babel.xml.generator.types.ModelType; +import org.onap.aai.babel.xml.generator.types.ModelWidget; + +@org.onap.aai.babel.xml.generator.types.Model(widget = Widget.Type.VOLUME_GROUP, cardinality = Cardinality.UNBOUNDED, + dataDeleteFlag = true) +@ModelWidget(type = ModelType.WIDGET, name = "volume-group") +public class VolumeGroupWidget extends Widget { +} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/model/VolumeWidget.java b/src/main/java/org/onap/aai/babel/xml/generator/model/VolumeWidget.java new file mode 100644 index 0000000..d18a723 --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/model/VolumeWidget.java @@ -0,0 +1,31 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.model; + +import org.onap.aai.babel.xml.generator.types.Cardinality; +import org.onap.aai.babel.xml.generator.types.Model; +import org.onap.aai.babel.xml.generator.types.ModelType; +import org.onap.aai.babel.xml.generator.types.ModelWidget; + +@Model(widget = Widget.Type.VOLUME, cardinality = Cardinality.UNBOUNDED, dataDeleteFlag = true) +@ModelWidget(type = ModelType.WIDGET, name = "volume") +public class VolumeWidget extends ResourceWidget { +} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/model/Widget.java b/src/main/java/org/onap/aai/babel/xml/generator/model/Widget.java new file mode 100644 index 0000000..545ad79 --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/model/Widget.java @@ -0,0 +1,211 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.model; + +import static org.onap.aai.babel.xml.generator.data.GeneratorConstants.GENERATOR_AAI_CONFIGLPROP_NOT_FOUND; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Properties; +import java.util.Set; +import org.onap.aai.babel.xml.generator.data.ArtifactType; +import org.onap.aai.babel.xml.generator.data.GeneratorConstants; +import org.onap.aai.babel.xml.generator.data.WidgetConfigurationUtil; +import org.onap.aai.babel.xml.generator.error.IllegalAccessException; +import org.onap.aai.babel.xml.generator.types.ModelType; +import org.onap.aai.babel.xml.generator.types.ModelWidget; + +public abstract class Widget extends Model { + + private Set keys = new HashSet<>(); + + /** + * Gets widget. + * + * @param type the type + * @return the widget + */ + public static Widget getWidget(Type type) { + + switch (type) { + case SERVICE: + return new ServiceWidget(); + case VF: + return new VfWidget(); + case VFC: + return new VfcWidget(); + case VSERVER: + return new VServerWidget(); + case VOLUME: + return new VolumeWidget(); + case FLAVOR: + return new FlavorWidget(); + case TENANT: + return new TenantWidget(); + case VOLUME_GROUP: + return new VolumeGroupWidget(); + case LINT: + return new LIntfWidget(); + case L3_NET: + return new L3NetworkWidget(); + case VFMODULE: + return new VfModuleWidget(); + case IMAGE: + return new ImageWidget(); + case OAM_NETWORK: + return new OamNetwork(); + case ALLOTTED_RESOURCE: + return new AllotedResourceWidget(); + case TUNNEL_XCONNECT: + return new TunnelXconnectWidget(); + default: + return null; + } + } + + /** + * Gets id. + * + * @return the id + */ + public String getId() { + Properties properties = WidgetConfigurationUtil.getConfig(); + String id = properties.getProperty(ArtifactType.AAI.name() + ".model-version-id." + getName()); + if (id == null) { + throw new IllegalArgumentException(String.format(GENERATOR_AAI_CONFIGLPROP_NOT_FOUND, + ArtifactType.AAI.name() + ".model-version-id." + getName())); + } + return id; + } + + public ModelType getType() { + ModelWidget widgetModel = this.getClass().getAnnotation(ModelWidget.class); + return widgetModel.type(); + } + + public String getName() { + ModelWidget widgetModel = this.getClass().getAnnotation(ModelWidget.class); + return widgetModel.name(); + } + + /** + * Get Widget Id from properties file. + * + * @return - Widget Id + */ + @Override + public String getWidgetId() { + Properties properties = WidgetConfigurationUtil.getConfig(); + String id = properties.getProperty(ArtifactType.AAI.name() + ".model-invariant-id." + getName()); + if (id == null) { + throw new IllegalArgumentException(String.format(GENERATOR_AAI_CONFIGLPROP_NOT_FOUND, + ArtifactType.AAI.name() + ".model-invariant-id." + getName())); + } + return id; + } + + @Override + public int hashCode() { + return getId().hashCode(); + } + + @Override + public Type getWidgetType() { + return null; + } + + /** + * Equals. + * + * @param obj Object + * @return the boolean + */ + @Override + public boolean equals(Object obj) { + if (obj instanceof Widget) { + if (getId().equals(((Widget) obj).getId())) { + ((Widget) obj).keys.addAll(this.keys); + return true; + } + return false; + } else { + return false; + } + } + + public void addKey(String key) { + this.keys.add(key); + } + + /** + * Member of boolean. + * + * @param keys the keys + * @return the boolean + */ + public boolean memberOf(List keys) { + if (keys == null) { + return false; + } + return !Collections.disjoint(this.keys, keys); + } + + /** + * All instances used boolean. + * + * @param collection the collection + * @return the boolean + */ + public boolean allInstancesUsed(Set collection) { + Set keyCopy = new HashSet<>(keys); + keyCopy.removeAll(collection); + return keyCopy.isEmpty(); + } + + @Override + public boolean addResource(Resource resource) { + throw new IllegalAccessException(GeneratorConstants.GENERATOR_AAI_ERROR_UNSUPPORTED_WIDGET_OPERATION); + } + + @Override + public boolean addWidget(Widget widget) { + return true; + } + + public enum Type { + SERVICE, + VF, + VFC, + VSERVER, + VOLUME, + FLAVOR, + TENANT, + VOLUME_GROUP, + LINT, + L3_NET, + VFMODULE, + IMAGE, + OAM_NETWORK, + ALLOTTED_RESOURCE, + TUNNEL_XCONNECT + } +} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/types/Cardinality.java b/src/main/java/org/onap/aai/babel/xml/generator/types/Cardinality.java new file mode 100644 index 0000000..9cdb93b --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/types/Cardinality.java @@ -0,0 +1,25 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.types; + +public enum Cardinality { + UNBOUNDED +} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/types/Model.java b/src/main/java/org/onap/aai/babel/xml/generator/types/Model.java new file mode 100644 index 0000000..f69c3ea --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/types/Model.java @@ -0,0 +1,53 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.types; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.onap.aai.babel.xml.generator.model.Widget; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface Model { + + /** + * Widget widget . type. + * + * @return the widget . type + */ + public Widget.Type widget(); + + /** + * Data delete flag boolean. + * + * @return the boolean + */ + public boolean dataDeleteFlag(); + + /** + * Cardinality cardinality. + * + * @return the cardinality + */ + public Cardinality cardinality(); +} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/types/ModelType.java b/src/main/java/org/onap/aai/babel/xml/generator/types/ModelType.java new file mode 100644 index 0000000..7dd86c6 --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/types/ModelType.java @@ -0,0 +1,27 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.types; + +public enum ModelType { + SERVICE, + RESOURCE, + WIDGET; +} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/types/ModelWidget.java b/src/main/java/org/onap/aai/babel/xml/generator/types/ModelWidget.java new file mode 100644 index 0000000..307524e --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/types/ModelWidget.java @@ -0,0 +1,45 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.types; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ModelWidget { + + /** + * Type model type. + * + * @return the model type + */ + public ModelType type(); + + /** + * Name string. + * + * @return the string + */ + public String name(); +} diff --git a/src/main/resources/babel-logging-resources.properties b/src/main/resources/babel-logging-resources.properties index 3cd23ab..dba72c8 100644 --- a/src/main/resources/babel-logging-resources.properties +++ b/src/main/resources/babel-logging-resources.properties @@ -46,14 +46,42 @@ DISTRIBUTION_EVENT=\ Distribution event: {0}|\ A distribution event was received from ASDC|\ +MESSAGE_AUDIT=\ + BABEL0002I|\ + {0}|\ + |\ + +MESSAGE_METRIC=\ + BABEL0003I|\ + {0}|\ + |\ + PROCESS_REQUEST_ERROR=\ - BABEL0002E|\ + BABEL0004E|\ Error while processing REST request.|\ INVALID_REQUEST_JSON=\ - BABEL0003E|\ + BABEL0005E|\ Error while processing JSON in request body.|\ INVALID_CSAR_FILE=\ - BABEL0004E|\ - Error while processing CSAR file.|\ \ No newline at end of file + BABEL0006E|\ + Error while processing CSAR file.|\ + +PROCESSING_VNF_CATALOG_ERROR=\ + BABEL0007E|\ + Error while processing VNF Catalog.|\ + +BABEL_REQUEST_PAYLOAD=\ + BABEL0008I|\ + Babel request payload: {0}|\ + +BABEL_RESPONSE_PAYLOAD=\ + BABEL0009I|\ + Babel response payload: {0}|\ + +MISSING_REQUEST_ID=\ + BABEL0010I|\ + Missing request ID. Assigned {0}|\ + |\ + \ No newline at end of file diff --git a/src/test/java/org/onap/aai/babel/MicroServiceAuthTest.java b/src/test/java/org/onap/aai/babel/MicroServiceAuthTest.java index f24cbf1..6912d90 100644 --- a/src/test/java/org/onap/aai/babel/MicroServiceAuthTest.java +++ b/src/test/java/org/onap/aai/babel/MicroServiceAuthTest.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017 European Software Marketing Ltd. + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,8 +17,6 @@ * See the License for the specific language governing permissions and * limitations under the License. * ============LICENSE_END========================================================= - * - * ECOMP is a trademark and service mark of AT&T Intellectual Property. */ package org.onap.aai.babel; @@ -49,14 +47,13 @@ public class MicroServiceAuthTest { private static final String authPolicyFile = "auth_policy.json"; static { - System.setProperty("CONFIG_HOME", - System.getProperty("user.dir") + File.separator + "src/test/resources"); + System.setProperty("CONFIG_HOME", System.getProperty("user.dir") + File.separator + "src/test/resources"); } /** * Temporarily invalidate the default policy file and then try to initialise the authorisation class using the name * of a policy file that does not exist. - * + * * @throws AAIAuthException * @throws IOException */ @@ -65,9 +62,9 @@ public class MicroServiceAuthTest { String defaultFile = AAIMicroServiceAuthCore.getDefaultAuthFileName(); try { AAIMicroServiceAuthCore.setDefaultAuthFileName("invalid.default.file"); - BabelAuthConfig gapServiceAuthConfig = new BabelAuthConfig(); - gapServiceAuthConfig.setAuthPolicyFile("invalid.file.name"); - new AAIMicroServiceAuth(gapServiceAuthConfig); + BabelAuthConfig babelServiceAuthConfig = new BabelAuthConfig(); + babelServiceAuthConfig.setAuthPolicyFile("invalid.file.name"); + new AAIMicroServiceAuth(babelServiceAuthConfig); } finally { AAIMicroServiceAuthCore.setDefaultAuthFileName(defaultFile); } @@ -75,7 +72,7 @@ public class MicroServiceAuthTest { /** * Test loading of a temporary file created with the specified roles - * + * * @throws AAIAuthException * @throws IOException * @throws JSONException @@ -90,21 +87,21 @@ public class MicroServiceAuthTest { /** * Test that the default policy file is loaded when a non-existent file is passed to the authorisation clas. - * + * * @throws AAIAuthException */ @Test public void createAuthFromDefaultFile() throws AAIAuthException { - BabelAuthConfig gapServiceAuthConfig = new BabelAuthConfig(); - gapServiceAuthConfig.setAuthPolicyFile("non-existent-file"); - AAIMicroServiceAuth auth = new AAIMicroServiceAuth(gapServiceAuthConfig); + BabelAuthConfig babelServiceAuthConfig = new BabelAuthConfig(); + babelServiceAuthConfig.setAuthPolicyFile("non-existent-file"); + AAIMicroServiceAuth auth = new AAIMicroServiceAuth(babelServiceAuthConfig); // The default policy will have been loaded assertAdminUserAuthorisation(auth, VALID_ADMIN_USER); } /** * Test loading of the policy file relative to CONFIG_HOME - * + * * @throws AAIAuthException */ @Test @@ -125,13 +122,13 @@ public class MicroServiceAuthTest { @Test public void testValidateRequest() throws AAIAuthException { AAIMicroServiceAuth auth = createStandardAuth(); - assertThat(auth.validateRequest(null, new MockHttpServletRequest(), null, "app/v1/gap"), is(false)); + assertThat(auth.validateRequest(null, new MockHttpServletRequest(), null, "app/v1/babel"), is(false)); } private AAIMicroServiceAuth createStandardAuth() throws AAIAuthException { - BabelAuthConfig gapServiceAuthConfig = new BabelAuthConfig(); - gapServiceAuthConfig.setAuthPolicyFile(authPolicyFile); - return new AAIMicroServiceAuth(gapServiceAuthConfig); + BabelAuthConfig babelServiceAuthConfig = new BabelAuthConfig(); + babelServiceAuthConfig.setAuthPolicyFile(authPolicyFile); + return new AAIMicroServiceAuth(babelServiceAuthConfig); } /** @@ -155,7 +152,7 @@ public class MicroServiceAuthTest { /** * Assert authorisation results for an admin user based on the test policy file - * + * * @param auth * @param adminUser * @throws AAIAuthException diff --git a/src/test/java/org/onap/aai/babel/csar/extractor/YamlExtractorTest.java b/src/test/java/org/onap/aai/babel/csar/extractor/YamlExtractorTest.java index 54f4c65..9024efa 100644 --- a/src/test/java/org/onap/aai/babel/csar/extractor/YamlExtractorTest.java +++ b/src/test/java/org/onap/aai/babel/csar/extractor/YamlExtractorTest.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017 European Software Marketing Ltd. + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,8 +17,6 @@ * See the License for the specific language governing permissions and * limitations under the License. * ============LICENSE_END========================================================= - * - * ECOMP is a trademark and service mark of AT&T Intellectual Property. */ package org.onap.aai.babel.csar.extractor; @@ -32,7 +30,7 @@ import java.util.List; import org.apache.commons.io.IOUtils; import org.junit.Test; import org.onap.aai.babel.util.ArtifactTestUtils; -import org.openecomp.sdc.generator.data.Artifact; +import org.onap.aai.babel.xml.generator.data.Artifact; /** * Tests @see YamlExtractor @@ -124,7 +122,7 @@ public class YamlExtractorTest { } @Test - public void extract_archiveContainsThreeRelevantYmlFilesFromSdWanService() + public void extract_archiveContainsOnlyTheExpectedYmlFilesFromSdWanService() throws IOException, InvalidArchiveException { List ymlFiles = YamlExtractor.extract(loadResource("compressedArtifacts/service-SdWanServiceTest-csar.csar"), @@ -134,6 +132,8 @@ public class YamlExtractorTest { payloads.add("ymlFiles/resource-SdWanTestVsp-template.yml"); payloads.add("ymlFiles/resource-TunnelXconntest-template.yml"); payloads.add("ymlFiles/service-SdWanServiceTest-template.yml"); + payloads.add("ymlFiles/artifacts.yml"); + payloads.add("ymlFiles/data.yml"); new ArtifactTestUtils().performYmlAsserts(ymlFiles, payloads); } diff --git a/src/test/java/org/onap/aai/babel/csar/fixture/ArtifactInfoBuilder.java b/src/test/java/org/onap/aai/babel/csar/fixture/ArtifactInfoBuilder.java index 0ff8fa1..20c8254 100644 --- a/src/test/java/org/onap/aai/babel/csar/fixture/ArtifactInfoBuilder.java +++ b/src/test/java/org/onap/aai/babel/csar/fixture/ArtifactInfoBuilder.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017 European Software Marketing Ltd. + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,8 +17,6 @@ * See the License for the specific language governing permissions and * limitations under the License. * ============LICENSE_END========================================================= - * - * ECOMP is a trademark and service mark of AT&T Intellectual Property. */ package org.onap.aai.babel.csar.fixture; diff --git a/src/test/java/org/onap/aai/babel/csar/fixture/TestArtifactInfoImpl.java b/src/test/java/org/onap/aai/babel/csar/fixture/TestArtifactInfoImpl.java index bbf4a43..dfca951 100644 --- a/src/test/java/org/onap/aai/babel/csar/fixture/TestArtifactInfoImpl.java +++ b/src/test/java/org/onap/aai/babel/csar/fixture/TestArtifactInfoImpl.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017 European Software Marketing Ltd. + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,11 +17,11 @@ * See the License for the specific language governing permissions and * limitations under the License. * ============LICENSE_END========================================================= - * - * ECOMP is a trademark and service mark of AT&T Intellectual Property. */ package org.onap.aai.babel.csar.fixture; +import java.util.Objects; +import org.apache.commons.lang3.builder.EqualsBuilder; import org.openecomp.sdc.api.notification.IArtifactInfo; /** @@ -101,35 +101,24 @@ public class TestArtifactInfoImpl implements IArtifactInfo { } @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - TestArtifactInfoImpl that = (TestArtifactInfoImpl) o; - - if (artifactName != null ? !artifactName.equals(that.artifactName) : that.artifactName != null) { - return false; - } - if (artifactType != null ? !artifactType.equals(that.artifactType) : that.artifactType != null) { - return false; - } - if (artifactDescription != null ? !artifactDescription.equals(that.artifactDescription) - : that.artifactDescription != null) { + public boolean equals(Object obj) { + if (!(obj instanceof TestArtifactInfoImpl)) { return false; + } else if (obj == this) { + return true; } - return artifactVersion != null ? artifactVersion.equals(that.artifactVersion) : that.artifactVersion == null; + TestArtifactInfoImpl rhs = (TestArtifactInfoImpl) obj; + // @formatter:off + return new EqualsBuilder() + .append(artifactType, rhs.artifactType) + .append(artifactDescription, rhs.artifactDescription) + .append(artifactVersion, rhs.artifactVersion) + .isEquals(); + // @formatter:on } @Override public int hashCode() { - int result = artifactName != null ? artifactName.hashCode() : 0; - result = 31 * result + (artifactType != null ? artifactType.hashCode() : 0); - result = 31 * result + (artifactDescription != null ? artifactDescription.hashCode() : 0); - result = 31 * result + (artifactVersion != null ? artifactVersion.hashCode() : 0); - return result; + return Objects.hash(this.artifactType, this.artifactDescription, this.artifactVersion); } } diff --git a/src/test/java/org/onap/aai/babel/logging/LogReader.java b/src/test/java/org/onap/aai/babel/logging/LogReader.java new file mode 100644 index 0000000..c2a8e64 --- /dev/null +++ b/src/test/java/org/onap/aai/babel/logging/LogReader.java @@ -0,0 +1,102 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.logging; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.TimeUnit; +import org.apache.commons.lang.time.StopWatch; +import org.junit.Assert; + +public class LogReader { + + private Map cachedLogMap = new HashMap<>(); + private Map readersMap = new HashMap<>(); + private BufferedReader cachedReader; + + public LogReader(String logDirectory, String logFilePrefix) throws IOException { + cachedReader = getReader(logDirectory, logFilePrefix); + } + + private BufferedReader getReader(String logDirectory, String logFilePrefix) throws IOException { + BufferedReader reader = readersMap.get(logFilePrefix); + if (reader == null) { + reader = new BufferedReader(new FileReader(getLogFile(logDirectory, logFilePrefix))); + while (reader.readLine() != null) { + // Consume all lines + } + readersMap.put(logFilePrefix, reader); + } + return reader; + } + + /** + * @param logDirectory + * @return the most recently created log file. + * @throws IOException + */ + public File getLogFile(String logDirectory, String filenamePrefix) throws IOException { + Path cachedLog = cachedLogMap.get(filenamePrefix); + + if (cachedLog == null) { + Optional latestFilePath = Files.list(Paths.get(logDirectory)) + .filter(f -> Files.isDirectory(f) == false && f.getFileName().toString().startsWith(filenamePrefix)) + .max((f1, f2) -> (int) (f1.toFile().lastModified() - f2.toFile().lastModified())); + if (latestFilePath.isPresent()) { + cachedLog = latestFilePath.get(); + } else { + throw new IOException("No validation log files were found!"); + } + } + return cachedLog.toFile(); + } + + /** + * @return new lines appended to the log file + * @throws IOException + */ + public String getNewLines() throws IOException { + StopWatch stopwatch = new StopWatch(); + stopwatch.start(); + + while (!cachedReader.ready()) { + if (stopwatch.getTime() > TimeUnit.SECONDS.toMillis(30)) { + Assert.fail("Test took too long"); + } + // else keep waiting + } + + StringBuilder lines = new StringBuilder(); + String line; + while ((line = cachedReader.readLine()) != null) { + lines.append(line).append(System.lineSeparator()); + } + return lines.toString(); + } +} diff --git a/src/test/java/org/onap/aai/babel/logging/TestApplicationLogger.java b/src/test/java/org/onap/aai/babel/logging/TestApplicationLogger.java new file mode 100644 index 0000000..8a038b2 --- /dev/null +++ b/src/test/java/org/onap/aai/babel/logging/TestApplicationLogger.java @@ -0,0 +1,235 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.logging; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; + +import java.io.IOException; +import java.util.Arrays; +import javax.ws.rs.core.HttpHeaders; +import org.apache.commons.lang.time.StopWatch; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mockito.Mockito; +import org.onap.aai.babel.logging.LogHelper.TriConsumer; +import org.onap.aai.cl.api.LogFields; +import org.onap.aai.cl.api.Logger; +import org.onap.aai.cl.mdc.MdcOverride; + +/** + * Simple test to log each of the validation messages in turn. + * + * This version tests only the error logger at INFO level. + * + */ +public class TestApplicationLogger { + + @BeforeClass + public static void setupClass() { + System.setProperty("AJSC_HOME", "."); + } + + /** + * Check that each message can be logged and that (by implication of successful logging) there is a corresponding + * resource (message format). + * + * @throws IOException + */ + @Test + public void logAllMessages() throws IOException { + Logger logger = LogHelper.INSTANCE; + LogReader errorReader = new LogReader(LogHelper.getLogDirectory(), "error"); + LogReader debugReader = new LogReader(LogHelper.getLogDirectory(), "debug"); + String[] args = {"1", "2", "3", "4"}; + for (ApplicationMsgs msg : Arrays.asList(ApplicationMsgs.values())) { + if (msg.name().endsWith("ERROR")) { + logger.error(msg, args); + validateLoggedMessage(msg, errorReader, "ERROR"); + + logger.error(msg, new RuntimeException("fred"), args); + validateLoggedMessage(msg, errorReader, "fred"); + } else { + logger.info(msg, args); + validateLoggedMessage(msg, errorReader, "INFO"); + + logger.warn(msg, args); + validateLoggedMessage(msg, errorReader, "WARN"); + } + + logger.debug(msg, args); + validateLoggedMessage(msg, debugReader, "DEBUG"); + + // The trace level is not enabled + logger.trace(msg, args); + } + } + + /** + * Check that each message can be logged and that (by implication of successful logging) there is a corresponding + * resource (message format). + * + * @throws IOException + */ + @Test + public void logDebugMessages() throws IOException { + LogReader reader = new LogReader(LogHelper.getLogDirectory(), "debug"); + LogHelper.INSTANCE.debug("a message"); + String s = reader.getNewLines(); + assertThat(s, is(notNullValue())); + } + + /** + * Check logAudit with HTTP headers + * + * @throws IOException + */ + @Test + public void logAuditMessage() throws IOException { + LogHelper logger = LogHelper.INSTANCE; + LogReader reader = new LogReader(LogHelper.getLogDirectory(), "audit"); + + HttpHeaders headers = Mockito.mock(HttpHeaders.class); + Mockito.when(headers.getHeaderString("X-ECOMP-RequestID")).thenReturn("ecomp-request-id"); + Mockito.when(headers.getHeaderString("X-FromAppId")).thenReturn("app-id"); + + // Call logAudit without first calling startAudit + logger.logAuditSuccess("first call: bob"); + String s = reader.getNewLines(); + assertThat(s, is(notNullValue())); + assertThat("audit message log level", s, containsString("INFO")); + assertThat("audit message content", s, containsString("bob")); + + // This time call the start method + logger.startAudit(headers, null); + logger.logAuditSuccess("second call: foo"); + s = reader.getNewLines(); + assertThat(s, is(notNullValue())); + assertThat("audit message log level", s, containsString("INFO")); + assertThat("audit message content", s, containsString("foo")); + assertThat("audit message content", s, containsString("ecomp-request-id")); + assertThat("audit message content", s, containsString("app-id")); + } + + /** + * Check logAudit with no HTTP headers + * + * @throws IOException + */ + @Test + public void logAuditMessageWithoutHeaders() throws IOException { + LogHelper logger = LogHelper.INSTANCE; + LogReader reader = new LogReader(LogHelper.getLogDirectory(), "audit"); + logger.startAudit(null, null); + logger.logAuditSuccess("foo"); + String s = reader.getNewLines(); + assertThat(s, is(notNullValue())); + assertThat("audit message log level", s, containsString("INFO")); + assertThat("audit message content", s, containsString("foo")); + } + + /** + * Check logMetrics + * + * @throws IOException + */ + @Test + public void logMetricsMessage() throws IOException { + LogReader reader = new LogReader(LogHelper.getLogDirectory(), "metrics"); + LogHelper logger = LogHelper.INSTANCE; + logger.logMetrics("metrics: fred"); + String s = reader.getNewLines(); + assertThat(s, is(notNullValue())); + assertThat("metrics message log level", s, containsString("INFO")); + assertThat("metrics message content", s, containsString("fred")); + } + + @Test + public void logMetricsMessageWithStopwatch() throws IOException { + LogReader reader = new LogReader(LogHelper.getLogDirectory(), "metrics"); + LogHelper logger = LogHelper.INSTANCE; + StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + logger.logMetrics(stopWatch, "joe", "bloggs"); + String logLine = reader.getNewLines(); + assertThat(logLine, is(notNullValue())); + assertThat("metrics message log level", logLine, containsString("INFO")); + assertThat("metrics message content", logLine, containsString("joe")); + } + + @Test + public void callUnsupportedMethods() throws IOException { + LogHelper logger = LogHelper.INSTANCE; + ApplicationMsgs dummyMsg = ApplicationMsgs.LOAD_PROPERTIES; + callUnsupportedOperationMethod(logger::error, dummyMsg); + callUnsupportedOperationMethod(logger::info, dummyMsg); + callUnsupportedOperationMethod(logger::warn, dummyMsg); + callUnsupportedOperationMethod(logger::debug, dummyMsg); + callUnsupportedOperationMethod(logger::trace, dummyMsg); + try { + logger.error(dummyMsg, new LogFields(), new RuntimeException("test"), ""); + } catch (UnsupportedOperationException e) { + // Expected to reach here + } + try { + logger.info(dummyMsg, new LogFields(), new MdcOverride(), ""); + } catch (UnsupportedOperationException e) { + // Expected to reach here + } + try { + logger.formatMsg(dummyMsg, ""); + } catch (UnsupportedOperationException e) { + // Expected to reach here + } + } + + /** + * Call a logger method which is expected to throw an UnsupportedOperationException + * + * @param logMethod + * @param dummyMsg + */ + private void callUnsupportedOperationMethod(TriConsumer, LogFields, String[]> logMethod, + ApplicationMsgs dummyMsg) { + try { + logMethod.accept(dummyMsg, new LogFields(), new String[] {""}); + org.junit.Assert.fail("method should have thrown execption"); // NOSONAR as code not reached + } catch (UnsupportedOperationException e) { + // Expected to reach here + } + } + + /** + * Assert that a log message was logged to the expected log file at the expected severity + * + * @param msg + * @param reader + * @param severity + * @throws IOException + */ + private void validateLoggedMessage(ApplicationMsgs msg, LogReader reader, String severity) throws IOException { + String s = reader.getNewLines(); + assertThat(s, is(notNullValue())); + assertThat(msg.toString() + " log level", s, containsString(severity)); + } +} diff --git a/src/test/java/org/onap/aai/babel/parser/TestToscaParser.java b/src/test/java/org/onap/aai/babel/parser/TestToscaParser.java new file mode 100644 index 0000000..b1f2c01 --- /dev/null +++ b/src/test/java/org/onap/aai/babel/parser/TestToscaParser.java @@ -0,0 +1,88 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.parser; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import org.apache.commons.io.IOUtils; +import org.junit.Before; +import org.junit.Test; +import org.onap.aai.babel.csar.extractor.InvalidArchiveException; +import org.onap.aai.babel.csar.extractor.YamlExtractor; +import org.onap.aai.babel.xml.generator.api.AaiArtifactGenerator; +import org.onap.aai.babel.xml.generator.data.AdditionalParams; +import org.onap.aai.babel.xml.generator.data.Artifact; +import org.onap.aai.babel.xml.generator.data.GenerationData; +import org.onap.aai.babel.xml.generator.data.GeneratorConstants; +import org.onap.aai.babel.xml.generator.data.WidgetConfigurationUtil; + +/** + * Direct tests of the Model so as to improve code coverage + */ +public class TestToscaParser { + + static { + if (System.getProperty("AJSC_HOME") == null) { + System.setProperty("AJSC_HOME", "."); + } + } + + @Before + public void setup() throws FileNotFoundException, IOException { + URL url = TestToscaParser.class.getClassLoader().getResource("artifact-generator.properties"); + System.setProperty(GeneratorConstants.PROPERTY_ARTIFACT_GENERATOR_CONFIG_FILE, url.getPath()); + + InputStream in = TestToscaParser.class.getClassLoader().getResourceAsStream("artifact-generator.properties"); + Properties properties = new Properties(); + properties.load(in); + in.close(); + WidgetConfigurationUtil.setConfig(properties); + } + + @Test + public void testParserWithCsarFile() throws IOException, InvalidArchiveException { + byte[] csar = loadResource("compressedArtifacts/catalog_csar.csar"); + List ymlFiles = YamlExtractor.extract(csar, "catalog_csar.csar", "1.0"); + + Map additionalParams = new HashMap<>(); + additionalParams.put(AdditionalParams.SERVICE_VERSION.getName(), "1.0"); + + AaiArtifactGenerator generator = new AaiArtifactGenerator(); + GenerationData data = generator.generateArtifact(csar, ymlFiles, additionalParams); + + assertThat(data.getErrorData().size(), is(equalTo(0))); + assertThat(data.getResultData().size(), is(equalTo(2))); + } + + private byte[] loadResource(String resourceName) throws IOException { + return IOUtils.toByteArray(TestToscaParser.class.getClassLoader().getResource(resourceName)); + } +} diff --git a/src/test/java/org/onap/aai/babel/service/CsarToXmlConverterTest.java b/src/test/java/org/onap/aai/babel/service/CsarToXmlConverterTest.java index 5c1e213..c8080e9 100644 --- a/src/test/java/org/onap/aai/babel/service/CsarToXmlConverterTest.java +++ b/src/test/java/org/onap/aai/babel/service/CsarToXmlConverterTest.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017 European Software Marketing Ltd. + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,58 +17,61 @@ * See the License for the specific language governing permissions and * limitations under the License. * ============LICENSE_END========================================================= - * - * ECOMP is a trademark and service mark of AT&T Intellectual Property. */ package org.onap.aai.babel.service; -import java.io.BufferedReader; -import java.io.ByteArrayInputStream; +import static org.junit.Assert.assertThat; + +import java.io.File; import java.io.IOException; -import java.io.InputStreamReader; -import java.net.URL; -import java.util.Base64; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; -import org.custommonkey.xmlunit.XMLTestCase; +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; +import org.hamcrest.Matcher; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -import org.junit.runner.RunWith; import org.onap.aai.babel.csar.CsarConverterException; import org.onap.aai.babel.csar.CsarToXmlConverter; import org.onap.aai.babel.service.data.BabelArtifact; import org.onap.aai.babel.util.ArtifactTestUtils; -import org.onap.aai.babel.xml.generator.ModelGenerator; import org.onap.aai.babel.xml.generator.XmlArtifactGenerationException; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; -import org.xml.sax.SAXException; +import org.onap.aai.babel.xml.generator.data.GeneratorConstants; /** * Tests {@link CsarToXmlConverter} */ -@RunWith(PowerMockRunner.class) -@PrepareForTest(ModelGenerator.class) -public class CsarToXmlConverterTest extends XMLTestCase { +public class CsarToXmlConverterTest { - private static final String NAME = "the_name_of_the_csar_file.csar"; - private static final String VERSION = "v1"; + private static final String ARTIFACT_GENERATOR_CONFIG = "artifact-generator.properties"; + private static final String CSAR_FOLDER = "compressedArtifacts"; + private static final String VALID_CSAR_FILE = "service-SdWanServiceTest-csar.csar"; + private static final String INCORRECT_CSAR_NAME = "the_name_of_the_csar_file.csar"; + private static final String SERVICE_VERSION = "1.0"; private CsarToXmlConverter converter; + static { + if (System.getProperty("AJSC_HOME") == null) { + System.setProperty("AJSC_HOME", "."); + } + } + @Rule public ExpectedException exception = ExpectedException.none(); + private ArtifactTestUtils artifactTestUtils; @Before - public void setUp() { + public void setup() { + System.setProperty(GeneratorConstants.PROPERTY_ARTIFACT_GENERATOR_CONFIG_FILE, + CsarToXmlConverterTest.class.getClassLoader().getResource(ARTIFACT_GENERATOR_CONFIG).getPath()); converter = new CsarToXmlConverter(); - URL url = CsarToXmlConverterTest.class.getClassLoader().getResource("artifact-generator.properties"); - System.setProperty("artifactgenerator.config", url.getPath()); + artifactTestUtils = new ArtifactTestUtils(); } @After @@ -83,25 +86,22 @@ public class CsarToXmlConverterTest extends XMLTestCase { @Test(expected = NullPointerException.class) public void generateXmlFromCsar_missingName() throws CsarConverterException, IOException { - byte[] csarArchive = new ArtifactTestUtils().loadResource("compressedArtifacts/service-VscpaasTest-csar.csar"); - converter.generateXmlFromCsar(csarArchive, null, null); + converter.generateXmlFromCsar(getCsar(VALID_CSAR_FILE), null, null); } @Test(expected = NullPointerException.class) public void generateXmlFromCsar_missingVersion() throws CsarConverterException, IOException { - byte[] csarArchive = new ArtifactTestUtils().loadResource("compressedArtifacts/service-VscpaasTest-csar.csar"); - converter.generateXmlFromCsar(csarArchive, NAME, null); + converter.generateXmlFromCsar(getCsar(VALID_CSAR_FILE), INCORRECT_CSAR_NAME, null); } @Test(expected = CsarConverterException.class) public void generateXmlFromCsar_noPayloadExists() throws CsarConverterException { - converter.generateXmlFromCsar(new byte[0], NAME, VERSION); + converter.generateXmlFromCsar(new byte[0], INCORRECT_CSAR_NAME, SERVICE_VERSION); } @Test(expected = CsarConverterException.class) public void generateXmlFromCsar_csarFileHasNoYmlFiles() throws CsarConverterException, IOException { - byte[] csarArchive = new ArtifactTestUtils().loadResource("compressedArtifacts/noYmlFilesArchive.zip"); - converter.generateXmlFromCsar(csarArchive, "noYmlFilesArchive.zip", VERSION); + converter.generateXmlFromCsar(getCsar("noYmlFilesArchive.zip"), "noYmlFilesArchive.zip", SERVICE_VERSION); } @Test @@ -110,61 +110,55 @@ public class CsarToXmlConverterTest extends XMLTestCase { exception.expect(CsarConverterException.class); exception.expectMessage("Cannot generate artifacts. artifactgenerator.config system property not configured"); - byte[] csarArchive = - new ArtifactTestUtils().loadResource("compressedArtifacts/service-SdWanServiceTest-csar.csar"); - // Unset the required system property - System.clearProperty("artifactgenerator.config"); - converter.generateXmlFromCsar(csarArchive, VERSION, "service-SdWanServiceTest-csar.csar"); + System.clearProperty(GeneratorConstants.PROPERTY_ARTIFACT_GENERATOR_CONFIG_FILE); + converter.generateXmlFromCsar(getCsar(VALID_CSAR_FILE), VALID_CSAR_FILE, SERVICE_VERSION); } @Test public void generateXmlFromCsar() throws CsarConverterException, IOException, XmlArtifactGenerationException { - byte[] csarArchive = - new ArtifactTestUtils().loadResource("compressedArtifacts/service-SdWanServiceTest-csar.csar"); - Map expectedXmlFiles = createExpectedXmlFiles(); List generatedArtifacts = - converter.generateXmlFromCsar(csarArchive, VERSION, "service-SdWanServiceTest-csar.csar"); - - generatedArtifacts.forEach(ga -> { - try { + converter.generateXmlFromCsar(getCsar(VALID_CSAR_FILE), VALID_CSAR_FILE, SERVICE_VERSION); - String x1 = expectedXmlFiles.get(ga.getName()); + generatedArtifacts + .forEach(ga -> assertThat("The content of " + ga.getName() + " must match the expected content", + ga.getPayload(), matches(expectedXmlFiles.get(ga.getName())))); + } - String x2 = bytesToString(ga.getPayload()); + public Matcher matches(final String expected) { + return new BaseMatcher() { + protected String theExpected = expected; - assertXMLEqual("The content of " + ga.getName() + " must match the expected content", x1, x2); + @Override + public boolean matches(Object o) { + return artifactTestUtils.compareXMLStrings((String) o, theExpected); + } - } catch (SAXException | IOException e) { - fail("There was an Exception parsing the XML: "+e.getMessage()); + @Override + public void describeTo(Description description) { + description.appendText(theExpected.toString()); } - }); + }; } - public String bytesToString(byte[] source) { - ByteArrayInputStream bis = new ByteArrayInputStream(Base64.getDecoder().decode(source)); - - String result = new BufferedReader(new InputStreamReader(bis)).lines().collect(Collectors.joining("\n")); - - return result; - + private byte[] getCsar(String csarFileName) throws IOException { + return artifactTestUtils.loadResource(CSAR_FOLDER + File.separator + csarFileName); } private Map createExpectedXmlFiles() throws IOException { - Map xml = new HashMap<>(); - - ArtifactTestUtils utils = new ArtifactTestUtils(); - - String[] filesToLoad = - {"AAI-SD-WAN-Service-Test-service-1.0.xml", "AAI-SdWanTestVsp..DUMMY..module-0-resource-2.xml", - "AAI-Tunnel_XConnTest-resource-2.0.xml", "AAI-SD-WAN-Test-VSP-resource-1.0.xml"}; + Map xmlMap = new HashMap<>(); - for (String s : filesToLoad) { - xml.put(s, utils.loadResourceAsString("generatedXml/" + s)); + List filesToLoad = new ArrayList<>(); + filesToLoad.add("AAI-SD-WAN-Service-Test-service-1.0.xml"); + filesToLoad.add("AAI-SdWanTestVsp..DUMMY..module-0-resource-2.xml"); + filesToLoad.add("AAI-Tunnel_XConnTest-resource-2.0.xml"); + filesToLoad.add("AAI-SD-WAN-Test-VSP-resource-1.0.xml"); + for (String filename : filesToLoad) { + xmlMap.put(filename, artifactTestUtils.loadResourceAsString("generatedXml" + File.separator + filename)); } - return xml; + return xmlMap; } } diff --git a/src/test/java/org/onap/aai/babel/service/TestGenerateArtifactsServiceImpl.java b/src/test/java/org/onap/aai/babel/service/TestGenerateArtifactsServiceImpl.java index 5d2309f..b1423b7 100644 --- a/src/test/java/org/onap/aai/babel/service/TestGenerateArtifactsServiceImpl.java +++ b/src/test/java/org/onap/aai/babel/service/TestGenerateArtifactsServiceImpl.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017 European Software Marketing Ltd. + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,8 +17,6 @@ * See the License for the specific language governing permissions and * limitations under the License. * ============LICENSE_END========================================================= - * - * ECOMP is a trademark and service mark of AT&T Intellectual Property. */ package org.onap.aai.babel.service; @@ -26,60 +24,79 @@ import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; import java.io.IOException; +import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.nio.file.Files; import java.nio.file.Paths; +import java.security.cert.X509Certificate; +import java.util.Collections; +import java.util.List; +import java.util.Map.Entry; import java.util.stream.Collectors; +import javax.inject.Inject; +import javax.security.auth.x500.X500Principal; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MultivaluedHashMap; import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; import org.junit.BeforeClass; import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.onap.aai.auth.AAIMicroServiceAuth; +import org.onap.aai.babel.xml.generator.data.GeneratorConstants; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** * Direct invocation of the generate artifacts service implementation * */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = {"classpath:/babel-beans.xml"}) public class TestGenerateArtifactsServiceImpl { - + + static { + if (System.getProperty("AJSC_HOME") == null) { + System.setProperty("AJSC_HOME", "."); + } + } + + @Inject + private AAIMicroServiceAuth auth; + @BeforeClass public static void setup() { URL url = TestGenerateArtifactsServiceImpl.class.getClassLoader().getResource("artifact-generator.properties"); - System.setProperty("artifactgenerator.config", url.getPath()); + System.setProperty(GeneratorConstants.PROPERTY_ARTIFACT_GENERATOR_CONFIG_FILE, url.getPath()); } - + @Test - public void testGenerateArtifacts() throws Exception { - String jsonRequest = readstringFromFile("jsonFiles/success_request.json"); + public void testInvalidCsarFile() throws URISyntaxException, IOException { + String jsonRequest = readstringFromFile("jsonFiles/invalid_csar_request.json"); Response response = processJsonRequest(jsonRequest); - assertThat(response.getStatus(), is(Response.Status.OK.getStatusCode())); - assertThat(response.getEntity(), is(readstringFromFile("response/response.json"))); + assertThat(response.getStatus(), is(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode())); + assertThat(response.getEntity(), is("Error converting CSAR artifact to XML model.")); } - @Test - public void testInvalidCsarFile() throws URISyntaxException, IOException{ - String jsonRequest = readstringFromFile("jsonFiles/invalid_csar_request.json"); - Response response = processJsonRequest(jsonRequest); - assertThat(response.getStatus(), is(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode())); - assertThat(response.getEntity(), is("Error converting CSAR artifact to XML model.")); - } - - @Test - public void testInvalidJsonFile() throws URISyntaxException, IOException{ - String jsonRequest = readstringFromFile("jsonFiles/invalid_json_request.json"); - Response response = processJsonRequest(jsonRequest); - assertThat(response.getStatus(), is(Response.Status.BAD_REQUEST.getStatusCode())); - assertThat(response.getEntity(), is("Malformed request.")); + public void testInvalidJsonFile() throws URISyntaxException, IOException { + String jsonRequest = readstringFromFile("jsonFiles/invalid_json_request.json"); + Response response = processJsonRequest(jsonRequest); + assertThat(response.getStatus(), is(Response.Status.BAD_REQUEST.getStatusCode())); + assertThat(response.getEntity(), is("Malformed request.")); } - + @Test public void testMissingArtifactName() throws Exception { String jsonRequest = readstringFromFile("jsonFiles/missing_artifact_name_request.json"); Response response = processJsonRequest(jsonRequest); assertThat(response.getStatus(), is(Response.Status.BAD_REQUEST.getStatusCode())); - assertThat(response.getEntity(), is("No artifact name attribute found in the request body." )); + assertThat(response.getEntity(), is("No artifact name attribute found in the request body.")); } - + @Test public void testMissingArtifactVersion() throws Exception { String jsonRequest = readstringFromFile("jsonFiles/missing_artifact_version_request.json"); @@ -87,7 +104,7 @@ public class TestGenerateArtifactsServiceImpl { assertThat(response.getStatus(), is(Response.Status.BAD_REQUEST.getStatusCode())); assertThat(response.getEntity(), is("No artifact version attribute found in the request body.")); } - + @Test public void testMissingCsarFile() throws Exception { String jsonRequest = readstringFromFile("jsonFiles/missing_csar_request.json"); @@ -95,17 +112,59 @@ public class TestGenerateArtifactsServiceImpl { assertThat(response.getStatus(), is(Response.Status.BAD_REQUEST.getStatusCode())); assertThat(response.getEntity(), is("No csar attribute found in the request body.")); } - - private Response processJsonRequest(String jsonRequest) { - GenerateArtifactsServiceImpl service = new GenerateArtifactsServiceImpl(/* No authentiction required */ null); - return service.generateArtifacts(jsonRequest); + /** + * Create a (mocked) HTTPS request and invoke the Babel generate artifacts API + * + * @param request for the Babel Service + * @return the Response from the HTTP API + * @throws URISyntaxException + */ + private Response processJsonRequest(String jsonRequest) throws URISyntaxException { + UriInfo mockUriInfo = Mockito.mock(UriInfo.class); + Mockito.when(mockUriInfo.getRequestUri()).thenReturn(new URI("/validate")); // NOSONAR (mocked) + Mockito.when(mockUriInfo.getPath(false)).thenReturn("validate"); // URI prefix is stripped by AJSC routing + Mockito.when(mockUriInfo.getPathParameters()).thenReturn(new MultivaluedHashMap()); + + // Create mocked request headers map + MultivaluedHashMap headersMap = new MultivaluedHashMap<>(); + headersMap.put("X-TransactionId", createSingletonList("transaction-id")); + headersMap.put("X-FromAppId", createSingletonList("app-id")); + headersMap.put("Host", createSingletonList("hostname")); + + HttpHeaders headers = Mockito.mock(HttpHeaders.class); + for (Entry> entry : headersMap.entrySet()) { + Mockito.when(headers.getRequestHeader(entry.getKey())).thenReturn(entry.getValue()); + } + Mockito.when(headers.getRequestHeaders()).thenReturn(headersMap); + + MockHttpServletRequest servletRequest = new MockHttpServletRequest(); + servletRequest.setSecure(true); + servletRequest.setScheme("https"); + servletRequest.setServerPort(9501); + servletRequest.setServerName("localhost"); + servletRequest.setRequestURI("/services/validation-service/v1/app/validate"); + + X509Certificate mockCertificate = Mockito.mock(X509Certificate.class); + Mockito.when(mockCertificate.getSubjectX500Principal()) + .thenReturn(new X500Principal("CN=test, OU=qa, O=Test Ltd, L=London, ST=London, C=GB")); + + servletRequest.setAttribute("javax.servlet.request.X509Certificate", new X509Certificate[] {mockCertificate}); + servletRequest.setAttribute("javax.servlet.request.cipher_suite", ""); + + GenerateArtifactsServiceImpl service = new GenerateArtifactsServiceImpl(auth); + return service.generateArtifacts(mockUriInfo, headers, servletRequest, jsonRequest); + } + + private List createSingletonList(String listItem) { + return Collections.singletonList(listItem); } private String readstringFromFile(String resourceFile) throws IOException, URISyntaxException { - return Files.lines(Paths.get(ClassLoader.getSystemResource(resourceFile).toURI())) + return Files + .lines(Paths + .get(TestGenerateArtifactsServiceImpl.class.getClassLoader().getResource(resourceFile).toURI())) .collect(Collectors.joining()); } - - + } diff --git a/src/test/java/org/onap/aai/babel/service/TestInfoService.java b/src/test/java/org/onap/aai/babel/service/TestInfoService.java new file mode 100644 index 0000000..b97aa92 --- /dev/null +++ b/src/test/java/org/onap/aai/babel/service/TestInfoService.java @@ -0,0 +1,69 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.service; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.startsWith; +import static org.junit.Assert.assertThat; + +import java.time.Clock; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import org.junit.Test; + +public class TestInfoService { + + @Test + public void testInitialisedInfoService() { + String info = new InfoService().getInfo(""); + assertThat(info, startsWith("Status: Up\n")); + assertThat(info, containsString("Started at")); + assertThat(info, containsString("total=1")); + } + + @Test + public void testStatusReport() { + InfoService infoService = new InfoService(); + LocalDateTime now = LocalDateTime.now(); + Clock clock = buildClock(now); + + String info = infoService.statusReport(clock); + assertThat(info, containsString("Started at")); + assertThat(info, containsString("total=1")); + + // Skip ahead 1 day + clock = buildClock(now.plusDays(1)); + info = infoService.statusReport(clock); + assertThat(info, containsString("Up time 1 day ")); + assertThat(info, containsString("total=2")); + + // Skip ahead 5 days + clock = buildClock(now.plusDays(5)); + info = infoService.statusReport(clock); + assertThat(info, containsString("Up time 5 days ")); + assertThat(info, containsString("total=3")); + } + + private Clock buildClock(LocalDateTime now) { + return Clock.fixed(now.toInstant(OffsetDateTime.now().getOffset()), Clock.systemDefaultZone().getZone()); + } + +} diff --git a/src/test/java/org/onap/aai/babel/util/ArtifactTestUtils.java b/src/test/java/org/onap/aai/babel/util/ArtifactTestUtils.java index 74f0c0e..5a4224c 100644 --- a/src/test/java/org/onap/aai/babel/util/ArtifactTestUtils.java +++ b/src/test/java/org/onap/aai/babel/util/ArtifactTestUtils.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017 European Software Marketing Ltd. + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,26 +17,23 @@ * See the License for the specific language governing permissions and * limitations under the License. * ============LICENSE_END========================================================= - * - * ECOMP is a trademark and service mark of AT&T Intellectual Property. */ package org.onap.aai.babel.util; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; -import com.google.common.base.Throwables; -import java.io.BufferedReader; -import java.io.ByteArrayInputStream; import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; +import java.net.URL; +import java.nio.charset.Charset; import java.util.Base64; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import org.apache.commons.io.IOUtils; -import org.openecomp.sdc.generator.data.Artifact; +import org.custommonkey.xmlunit.Diff; +import org.onap.aai.babel.xml.generator.data.Artifact; +import org.xml.sax.SAXException; /** * This class provides some utilities to assist with running tests. @@ -51,50 +48,60 @@ public class ArtifactTestUtils { try { return loadResourceAsString(s); } catch (IOException e) { - throw Throwables.propagate(e); + throw new RuntimeException(e); } }).collect(Collectors.toSet()); - toscaFiles.forEach(ts -> { - boolean payloadFound = false; + compareXMLPayloads(toscaFiles, ymlPayloads); + } + + /** + * Compare 2 XML strings to see if they have the same content + * + * @param string1 + * @param string2 + * @return true if similar + */ + public boolean compareXMLStrings(String string1, String string2) { + boolean similar = false; + + try { + similar = new Diff(string1, string2).similar(); + } catch (SAXException | IOException e) { // NOSONAR + similar = true; + } + + return similar; + } + + public byte[] loadResource(String resourceName) throws IOException { + return IOUtils.toByteArray(getResource(resourceName)); + } - String s = bytesToString(ts.getPayload()); + public String loadResourceAsString(String resourceName) throws IOException { + return IOUtils.toString(getResource(resourceName)); + } + private void compareXMLPayloads(List toscaFiles, Set ymlPayloads) { + for (Artifact artifact : toscaFiles) { + boolean payloadFound = false; for (String ymlPayload : ymlPayloads) { - String tscontent = ymlPayload; - if (s.endsWith(tscontent)) { + if (compareXMLStrings(convertToString(artifact.getPayload()), ymlPayload)) { payloadFound = true; break; } } assertThat("The content of each yml file must match the actual content of the file extracted (" - + ts.getName() + ")", payloadFound, is(true)); - }); - } - - public byte[] loadResource(String resourceName) throws IOException { - - return IOUtils.toByteArray(ArtifactTestUtils.class.getClassLoader().getResource(resourceName)); + + artifact.getName() + ")", payloadFound, is(true)); + } } - public String loadResourceAsString(String resourceName) throws IOException { - - InputStream is = ArtifactTestUtils.class.getClassLoader().getResource(resourceName).openStream(); - - String result = new BufferedReader(new InputStreamReader(is)).lines().collect(Collectors.joining("\n")); - - return result; - + private URL getResource(String resourceName) { + return ArtifactTestUtils.class.getClassLoader().getResource(resourceName); } - public String bytesToString(byte[] source) { - ByteArrayInputStream bis = new ByteArrayInputStream(Base64.getDecoder().decode(source)); - - String result = new BufferedReader(new InputStreamReader(bis)).lines().collect(Collectors.joining("\n")); - - return result; - + private String convertToString(byte[] byteArray) { + return new String(Base64.getDecoder().decode(byteArray), Charset.defaultCharset()); } - } diff --git a/src/test/java/org/onap/aai/babel/util/TestRequestValidator.java b/src/test/java/org/onap/aai/babel/util/TestRequestValidator.java index 030c24d..bffe419 100644 --- a/src/test/java/org/onap/aai/babel/util/TestRequestValidator.java +++ b/src/test/java/org/onap/aai/babel/util/TestRequestValidator.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017 European Software Marketing Ltd. + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,8 +17,6 @@ * See the License for the specific language governing permissions and * limitations under the License. * ============LICENSE_END========================================================= - * - * ECOMP is a trademark and service mark of AT&T Intellectual Property. */ package org.onap.aai.babel.util; @@ -28,45 +26,45 @@ import org.junit.rules.ExpectedException; import org.onap.aai.babel.service.data.BabelRequest; public class TestRequestValidator { - + @Rule public ExpectedException exception = ExpectedException.none(); - + @Test - public void testMissingArtifactNameExceptionThrown() throws Exception{ + public void testMissingArtifactNameExceptionThrown() throws Exception { exception.expect(RequestValidationException.class); exception.expectMessage("No artifact name attribute found in the request body."); - + BabelRequest request = new BabelRequest(); request.setCsar("UEsDBBQACAgIAGsrz0oAAAAAAAAAAAAAAAAJAAAAY3Nhci5tZXRhC3Z"); request.setArtifactVersion("1.0"); request.setArtifactName(null); - RequestValidator.validateRequest(request); - } - + RequestValidator.validateRequest(request); + } + + + @Test + public void testMissingArtifactVersionExceptionThrown() throws Exception { + exception.expect(RequestValidationException.class); + exception.expectMessage("No artifact version attribute found in the request body."); - @Test - public void testMissingArtifactVersionExceptionThrown() throws Exception{ - exception.expect(RequestValidationException.class); - exception.expectMessage("No artifact version attribute found in the request body."); - BabelRequest request = new BabelRequest(); request.setCsar("UEsDBBQACAgIAGsrz0oAAAAAAAAAAAAAAAAJAAAAY3Nhci5tZXRhC3Z"); request.setArtifactVersion(null); request.setArtifactName("hello"); - RequestValidator.validateRequest(request); + RequestValidator.validateRequest(request); } - + @Test - public void testMissingCsarFile() throws Exception{ + public void testMissingCsarFile() throws Exception { exception.expect(RequestValidationException.class); exception.expectMessage("No csar attribute found in the request body."); - + BabelRequest request = new BabelRequest(); request.setCsar(null); request.setArtifactVersion("1.0"); request.setArtifactName("hello"); - RequestValidator.validateRequest(request); + RequestValidator.validateRequest(request); } } diff --git a/src/test/java/org/onap/aai/babel/xml/generator/model/TestVfModule.java b/src/test/java/org/onap/aai/babel/xml/generator/model/TestVfModule.java new file mode 100644 index 0000000..d02b817 --- /dev/null +++ b/src/test/java/org/onap/aai/babel/xml/generator/model/TestVfModule.java @@ -0,0 +1,121 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.model; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.junit.Assert.assertThat; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import org.junit.Before; +import org.junit.Test; +import org.onap.aai.babel.xml.generator.data.WidgetConfigurationUtil; +import org.onap.aai.babel.xml.generator.model.Widget.Type; + +/** + * Direct tests of the Model class VfModule so as to improve code coverage + */ +public class TestVfModule { + + static { + if (System.getProperty("AJSC_HOME") == null) { + System.setProperty("AJSC_HOME", "."); + } + } + + @Before + public void setup() throws FileNotFoundException, IOException { + InputStream in = TestVfModule.class.getClassLoader().getResourceAsStream("artifact-generator.properties"); + Properties properties = new Properties(); + properties.load(in); + in.close(); + WidgetConfigurationUtil.setConfig(properties); + } + + @Test + public void testCreateVfModule() { + VfModule vf = new VfModule(); + Map modelIdentInfo = new HashMap<>(); + modelIdentInfo.put("UUID", "dummy_uuid"); + vf.populateModelIdentificationInformation(modelIdentInfo); + assertThat(vf.hashCode(), is(notNullValue())); + assertThat(vf.equals(vf), is(true)); + // Tests that the overridden equals() method correctly returns false for a different type of Object + // This is necessary to achieve complete code coverage + assertThat(vf.equals("string"), is(false)); // NOSONAR + } + + @Test + public void testNonMemberWidgetToVF() { + VfModule vf = new VfModule(); + Widget widget = Widget.getWidget(Type.SERVICE); + vf.setMembers(Collections.singletonList(widget.getId())); + vf.addWidget(widget); + } + + @Test + public void testAddServiceWidgetToVF() { + VfModule vf = new VfModule(); + addWidgetToModule(vf, Type.SERVICE); + } + + @Test + public void testAddVServerWidgetToVF() { + VfModule vf = new VfModule(); + addWidgetToModule(vf, Type.VSERVER); + } + + @Test + public void testAddLIntfWidgetToVF() { + VfModule vf = new VfModule(); + addWidgetToModule(vf, Type.LINT); + addWidgetToModule(vf, Type.VSERVER); + addWidgetToModule(vf, Type.LINT); + } + + @Test + public void testAddVolumeWidgetToVF() { + VfModule vf = new VfModule(); + addWidgetToModule(vf, Type.VOLUME); + addWidgetToModule(vf, Type.VSERVER); + addWidgetToModule(vf, Type.VOLUME); + } + + @Test + public void testAddOAMNetworkWidgetToVF() { + VfModule vf = new VfModule(); + addWidgetToModule(vf, Type.OAM_NETWORK); + } + + private void addWidgetToModule(VfModule vfModule, Type widgeType) { + Widget widget = Widget.getWidget(widgeType); + String id = widget.getId(); + widget.addKey(id); + vfModule.setMembers(Collections.singletonList(id)); + vfModule.addWidget(widget); + } +} diff --git a/src/test/java/org/onap/aai/babel/xml/generator/model/TestWidget.java b/src/test/java/org/onap/aai/babel/xml/generator/model/TestWidget.java new file mode 100644 index 0000000..e33de67 --- /dev/null +++ b/src/test/java/org/onap/aai/babel/xml/generator/model/TestWidget.java @@ -0,0 +1,55 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.model; + +import org.junit.Test; +import org.onap.aai.babel.xml.generator.model.Widget.Type; + +/** + * Direct tests of the Model so as to improve code coverage + */ +public class TestWidget { + + static { + if (System.getProperty("AJSC_HOME") == null) { + System.setProperty("AJSC_HOME", "."); + } + } + + @Test + public void testGetWidgets() { + Widget.getWidget(Type.VFC); + Widget.getWidget(Type.FLAVOR); + Widget.getWidget(Type.TENANT); + Widget.getWidget(Type.VOLUME_GROUP); + Widget.getWidget(Type.L3_NET); + Widget.getWidget(Type.IMAGE); + Widget.getWidget(Type.TUNNEL_XCONNECT); + } + + @Test + public void testMethods() { + new ServiceWidget().addWidget(new TenantWidget()); + new VolumeGroupWidget().getWidgetType(); + } + + +} diff --git a/src/test/resources/babel-auth.properties b/src/test/resources/babel-auth.properties new file mode 100644 index 0000000..ba4e13a --- /dev/null +++ b/src/test/resources/babel-auth.properties @@ -0,0 +1,2 @@ +auth.policy.file=/auth/auth_policy.json +auth.authentication.disable=true \ No newline at end of file diff --git a/src/test/resources/babel-beans.xml b/src/test/resources/babel-beans.xml new file mode 100644 index 0000000..f102337 --- /dev/null +++ b/src/test/resources/babel-beans.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/compressedArtifacts/Duff.txt b/src/test/resources/compressedArtifacts/Duff.txt new file mode 100644 index 0000000..51faa6e --- /dev/null +++ b/src/test/resources/compressedArtifacts/Duff.txt @@ -0,0 +1 @@ +a bogus file. \ No newline at end of file diff --git a/src/test/resources/compressedArtifacts/catalog_csar.csar b/src/test/resources/compressedArtifacts/catalog_csar.csar new file mode 100644 index 0000000000000000000000000000000000000000..35b17f76152c17d0750b17b61e6b899dff9a7811 GIT binary patch literal 39894 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%0D7x2aGjyPGLis&elx6sywnp+L8A*0*zF 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 literal 0 HcmV?d00001 diff --git a/src/test/resources/compressedArtifacts/noVnfConfiguration.csar b/src/test/resources/compressedArtifacts/noVnfConfiguration.csar new file mode 100644 index 0000000000000000000000000000000000000000..7dd8cd264730136a2c372e4aa19a956c4f6d38a3 GIT binary patch literal 89209 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 Bxx@ef literal 0 HcmV?d00001 diff --git a/src/test/resources/generatedXml/AAI-SCP-Test-VSP-resource-1.0.xml b/src/test/resources/generatedXml/AAI-SCP-Test-VSP-resource-1.0.xml new file mode 100644 index 0000000..22f3ed0 --- /dev/null +++ b/src/test/resources/generatedXml/AAI-SCP-Test-VSP-resource-1.0.xml @@ -0,0 +1,51 @@ + + b2b88a73-5c55-4984-99dd-a35c55935d14 + resource + + + 2e42bac2-318a-410c-b8ff-3b3a31351be7 + SCP-Test-VSP + 1.0 + SCP Test VSP + + + T + unbounded + + + T + unbounded + + + + model-ver + + model-ver.model-version-id + 06258c44-ab48-4b4b-a5db-16892f7d1e76 + + + model.model-invariant-id + 6f288081-b321-47c9-b038-6de70079a3bf + + + + + + + + model-ver + + model-ver.model-version-id + 93a6166f-b3d5-4f06-b4ba-aed48d009ad9 + + + model.model-invariant-id + acc6edd8-a8d4-4b93-afaa-0994068be14c + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/generatedXml/AAI-SD-WAN-Service-Test-service-1.0.xml b/src/test/resources/generatedXml/AAI-SD-WAN-Service-Test-service-1.0.xml index 7748998..d4b0556 100644 --- a/src/test/resources/generatedXml/AAI-SD-WAN-Service-Test-service-1.0.xml +++ b/src/test/resources/generatedXml/AAI-SD-WAN-Service-Test-service-1.0.xml @@ -1,4 +1,4 @@ - + 1c111111-1111-1111-1111-111111111111 service @@ -11,44 +11,7 @@ T unbounded - - - T - unbounded - - - - model-ver - - model-ver.model-version-id - 2a111111-1111-1111-1111-111111111111 - - - model.model-invariant-id - 1a111111-1111-1111-1111-111111111111 - - - - - - T - unbounded - - - - model-ver - - model-ver.model-version-id - 2b111111-1111-1111-1111-111111111111 - - - model.model-invariant-id - 1b111111-1111-1111-1111-111111111111 - - - - - + model-ver @@ -66,4 +29,4 @@ - + \ No newline at end of file diff --git a/src/test/resources/generatedXml/AAI-SD-WAN-Test-VSP-resource-1.0.xml b/src/test/resources/generatedXml/AAI-SD-WAN-Test-VSP-resource-1.0.xml index 1cd85e8..43e97ff 100644 --- a/src/test/resources/generatedXml/AAI-SD-WAN-Test-VSP-resource-1.0.xml +++ b/src/test/resources/generatedXml/AAI-SD-WAN-Test-VSP-resource-1.0.xml @@ -1,4 +1,4 @@ - + 1a111111-1111-1111-1111-111111111111 resource @@ -21,7 +21,7 @@ model-ver model-ver.model-version-id - 7a111111-1111-1111-1111-111111111111 + 5c111111-1111-1111-1111-111111111111 model.model-invariant-id diff --git a/src/test/resources/generatedXml/AAI-ScpTestVsp..asc_heat-int2..module-0-resource-1.xml b/src/test/resources/generatedXml/AAI-ScpTestVsp..asc_heat-int2..module-0-resource-1.xml new file mode 100644 index 0000000..66b1d26 --- /dev/null +++ b/src/test/resources/generatedXml/AAI-ScpTestVsp..asc_heat-int2..module-0-resource-1.xml @@ -0,0 +1,141 @@ + + 6f288081-b321-47c9-b038-6de70079a3bf + resource + + + 06258c44-ab48-4b4b-a5db-16892f7d1e76 + ScpTestVsp..asc_heat-int2..module-0 + 1 + + + T + unbounded + + + T + unbounded + + + F + unbounded + + + + model-ver + + model-ver.model-version-id + f6a038c2-820c-42ba-8c2b-375e24e8f932 + + + model.model-invariant-id + 3f4c7204-739b-4bbb-87a7-8a6856439c90 + + + + + + F + unbounded + + + + model-ver + + model-ver.model-version-id + abcc54bc-bb74-49dc-9043-7f7171707545 + + + model.model-invariant-id + 97c26c99-6870-44c1-8a07-1d900d3f4ce6 + + + + + + F + unbounded + + + + model-ver + + model-ver.model-version-id + 36200fb5-f251-4f5d-a520-7c5ad5c2cd4b + + + model.model-invariant-id + bace8d1c-a261-4041-9e37-823117415d0f + + + + + + T + unbounded + + + + model-ver + + model-ver.model-version-id + 5761e0a7-c6df-4d8a-9ebd-b8f445054dec + + + model.model-invariant-id + 96129eb9-f0de-4e05-8af2-73146473f766 + + + + + + + + model-ver + + model-ver.model-version-id + 8ecb2c5d-7176-4317-a255-26274edfdd53 + + + model.model-invariant-id + ff69d4e0-a8e8-4108-bdb0-dd63217e63c7 + + + + + + T + unbounded + + + + model-ver + + model-ver.model-version-id + 9111f20f-e680-4001-b83f-19a2fc23bfc1 + + + model.model-invariant-id + 3d560d81-57d0-438b-a2a1-5334dba0651a + + + + + + + + model-ver + + model-ver.model-version-id + c00563ae-812b-4e62-8330-7c4d0f47088a + + + model.model-invariant-id + ef86f9c5-2165-44f3-8fc3-96018b609ea5 + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/generatedXml/AAI-SdWanTestVsp..DUMMY..module-0-resource-2.xml b/src/test/resources/generatedXml/AAI-SdWanTestVsp..DUMMY..module-0-resource-2.xml index 6e221ce..b9c0abf 100644 --- a/src/test/resources/generatedXml/AAI-SdWanTestVsp..DUMMY..module-0-resource-2.xml +++ b/src/test/resources/generatedXml/AAI-SdWanTestVsp..DUMMY..module-0-resource-2.xml @@ -1,11 +1,12 @@ - + 6a111111-1111-1111-1111-111111111111 resource - 7a111111-1111-1111-1111-111111111111 + 5c111111-1111-1111-1111-111111111111 SdWanTestVsp..DUMMY..module-0 2 + T diff --git a/src/test/resources/generatedXml/AAI-Tunnel_XConnTest-resource-2.0.xml b/src/test/resources/generatedXml/AAI-Tunnel_XConnTest-resource-2.0.xml index ccecc80..456ad15 100644 --- a/src/test/resources/generatedXml/AAI-Tunnel_XConnTest-resource-2.0.xml +++ b/src/test/resources/generatedXml/AAI-Tunnel_XConnTest-resource-2.0.xml @@ -1,4 +1,4 @@ - + 1b111111-1111-1111-1111-111111111111 resource diff --git a/src/test/resources/jsonFiles/invalid_csar_request.json b/src/test/resources/jsonFiles/invalid_csar_request.json index 3742f36..f7ecca1 100644 --- a/src/test/resources/jsonFiles/invalid_csar_request.json +++ b/src/test/resources/jsonFiles/invalid_csar_request.json @@ -1,3 +1,3 @@ -{"csar": "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SVBfTXV4X0RlbXV4LXVwZGF0ZWQtcmVzb3VyY2UtMS4wLnhtbFBLAQIUABQACAgIAP2uhEoPxTTMggIAAN4PAAA7AAAAAAAAAAAAAAAAADY3AABBcnRpZmFjdHMvQUFJLVR1bm5lbF9YQ29ubi1mb3ItREhWLVRlc3RpbmctcmVzb3VyY2UtMS4wLnhtbFBLAQIUABQACAgIAP2uhEott6ixQgIAAEULAAA5AAAAAAAAAAAAAAAAACE6AABBcnRpZmFjdHMvQUFJLVNlcnZpY2VfQWRtaW4tZm9yLURIVi1UZXN0LXJlc291cmNlLTEuMC54bWxQSwECFAAUAAgICAD9roRKhVs/IKwDAABhJQAAQAAAAAAAAAAAAAAAAADKPAAAQXJ0aWZhY3RzL0FBSS1WaG5mRm9yRTJlVGVzdC4uYmFzZV9URVNULi5tb2R1bGUtMC1yZXNvdXJjZS0yLnhtbFBLAQIUABQACAgIAP2uhEpIeXXPNgIAADELAAAwAAAAAAAAAAAAAAAAAORAAABBcnRpZmFjdHMvQUFJLXZITkYtZm9yLURIVi1UZXN0LXJlc291cmNlLTIuMC54bWxQSwUGAAAAAAwADACcBAAAeEMAAAAA", +{"csar": "xxxx", "artifactVersion":"1.0", "artifactName":"hello"} \ No newline at end of file diff --git a/src/test/resources/jsonFiles/invalid_json_request.json b/src/test/resources/jsonFiles/invalid_json_request.json index 4600202..444840d 100644 --- a/src/test/resources/jsonFiles/invalid_json_request.json +++ b/src/test/resources/jsonFiles/invalid_json_request.json @@ -1,3 +1,3 @@ -{"csar": "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QUktSVBfTXV4X0RlbXV4LXVwZGF0ZWQtcmVzb3VyY2UtMS4wLnhtbFBLAQIUABQACAgIAP2uhEoPxTTMggIAAN4PAAA7AAAAAAAAAAAAAAAAADY3AABBcnRpZmFjdHMvQUFJLVR1bm5lbF9YQ29ubi1mb3ItREhWLVRlc3RpbmctcmVzb3VyY2UtMS4wLnhtbFBLAQIUABQACAgIAP2uhEott6ixQgIAAEULAAA5AAAAAAAAAAAAAAAAACE6AABBcnRpZmFjdHMvQUFJLVNlcnZpY2VfQWRtaW4tZm9yLURIVi1UZXN0LXJlc291cmNlLTEuMC54bWxQSwECFAAUAAgICAD9roRKhVs/IKwDAABhJQAAQAAAAAAAAAAAAAAAAADKPAAAQXJ0aWZhY3RzL0FBSS1WaG5mRm9yRTJlVGVzdC4uYmFzZV9URVNULi5tb2R1bGUtMC1yZXNvdXJjZS0yLnhtbFBLAQIUABQACAgIAP2uhEpIeXXPNgIAADELAAAwAAAAAAAAAAAAAAAAAORAAABBcnRpZmFjdHMvQUFJLXZITkYtZm9yLURIVi1UZXN0LXJlc291cmNlLTIuMC54bWxQSwUGAAAAAAwADACcBAAAeEMAAAAA", +{"csar": "xxxx", "artifactVersion":"1.0", "artifactName":"hello" \ No newline at end of file diff --git a/src/test/resources/jsonFiles/missing_artifact_name_request.json b/src/test/resources/jsonFiles/missing_artifact_name_request.json index a5f4948..049f4e7 100644 --- a/src/test/resources/jsonFiles/missing_artifact_name_request.json +++ b/src/test/resources/jsonFiles/missing_artifact_name_request.json @@ -1,3 +1,3 @@ -{"csar": "UEsDBBQACAgIAPuuhEoAAAAAAAAAAAAAAAAZAAAAVE9TQ0EtTWV0YWRhdGEvVE9TQ0EubWV0YU2MQQrCMBAA73lFPrDVXnNbY4qC1dIEPQe7SiBNJVmE/N6Clx5nYMbdrEboiT10IRLcKZewJCXbZi+0xXFrWqEzeaYJDlVJ68xwwuvZyAHHC/ZohUmcKxzpFVLgNSpKbmBXKH/Dk8BOD5/sH7olOyoc0huY5k9c902do/gBUEsHCPNvtY99AAAAmQAAAFBLAwQUAAgICAD7roRKAAAAAAAAAAAAAAAANwAAAERlZmluaXRpb25zL3NlcnZpY2UtU2RXYW5TZXJ2aWNlRm9yVGVzdGluZy10ZW1wbGF0ZS55bWztnVtzm8gSgJ83v2KqUmeTVAVWCCRB3pzYezZVG8cVO968qQYYLCpcdLgocSo//vQMIEACGUneWEg9D4ksDXPrnv56Lsw8nx5GeEae/yQdwm888A9nN7/fkKsonEcuS2h0T15+YnESuVbC7FerD4iHfnbNg3wMvHvihBFJY0bMe0LTZBZG7g9mEzew3YVrp9SLCUSgAfxqhgsm2Sx27wIKuZOEUf9l/GpTDt3LIsI3N5m5AUlmLKu3FfpzGrgshgLYJAgTUdo7FrCIesR2eUOYaeKGQa0RsmyfWtR5+PksCWOLTm3muIHLyxpPFyyK4cMbkv0Uu/7cY9N76ntTZTp45oOkbZrQN88ICGJBI5cGyefP78/fEFWxR+aYDiRNG2mSxlRVMujEkGyTDnRV18eWYcNTWWRtrA0UxixJHZk2RDbGErUHtjRQR2PHGZu6pfPIAfXZG3J9Lv1zdkmuWbRwLSYa+gY0zQ3uIApI3YrceSIK3RDz/K/bIjaUmFwML+Ch5H7O082iwd8WaM1dGN2/IZcs+RZGX8nfiqTCD3EW5YKL+5L6kAi0TJSyZ9AuYZTEb55J5P3V9EP6fXrO/PQ7Sec210DePoQ4rgfZRCwO08hi0vs5xBPRPmexpIRB88In+d73IKWbNAiYN/3yLgyC1cI3ppg98MWC+H+G0flskUdeTTiv6fTMhirUUm5MNo8uYpfpria6+Ovyz4fTup0FTlsaSTgPvfDuflp8y1MIQpstv4izNNeyIkr2QyFKkI9Mk0S2h3KRs7xw5Hrm+RNVHc7CiiazkWHoOpRdtXUFlHMwkXTFHEkj1VYp1YeKomrLZ7NHxqD8Y5XpEpsooPyqoUq6YxiSZWn6QNE0Ohix5SNWGieh7/6gXGmz552x5oyNkS05w6EBz1uaZExsQxpZKpuooxF0iTLLZR99MZQHL5ZfZ51lramWv9d6yjLa9TnvMdAtlr3kT2aSMRkOlMny0ayRb/8sq7DeYYaiw2QhTs0yxvvAiSjYw9RK0ihrhavI9TkxmroOUfROsl3rTV3Fa+qDsWZNLGmoGmCtGDUlQ5sMJWdg2TZTTcU02Ip4FdOkiqEoEuOS1caOKdHJRJWoodpDYzhWLGu4UbyOPnAGiiWNFYVKmqVSSQdbKamQuTHWFMVWzQbxKuvibWqxZgnXYp55Xpjwxv2Ut2Aue2Euk5p5JMM/FO2PbsJfS7ZFA64IFIWIoogY1wxMls01oGaZOsm92Th1Fb4yMW2qO1SyJ4YpQb80JdNyDMk0rJGuOvYQOLUi/IE9tm1OMmtggvBGYzAHqgJgU0FZxqYy0gcb+7aiT0zVUCaSqenQt4fOSDKViS7ZGvRzYKOlG+Nuwm83480qUI9/9om8dKLQJ39dXgG7qEfp9avmVPYXeYFgkXWtz1ch10ngbZDrKnJbdVTV0h3JtA0HvJOJDZKwh+BwjB1rRBkFua+IfEhNi4FCSA4zJpI20ZlER5CCOrJ0ywabbKn2JpFrpmkPDA2MuEWZpA3YQDIpA5Y4julAWUaq2mTOG0S+ySVoFnrtCZB520P7yzjLiYicmJXUpHwC/XowUrXhRB2DkgBMNBOeN8cg6YniGFRTTGVAJyfQr0tb3kxz4+horqqGDU6gIw3hf0kz6EiiY3Mg6WNzYoORMcGVO02an4JtBz9uODGMIXT2ASjcmMEnQ9VhdAD2AAwNHY47dvt+2fa7KEznxXgMxlWQtz1bcG1T5GKgdTFkvCiybNKYTW8urm9k2Q/t1GPSoFUfsoTlW+eDiNkq+kUeAf5l3qVowQ7ZNj/+vq5GdDAeD3WqS0N7CPRQJ4ACDVDAdAbjsTGQXJ00J1Q8r6sAHl0ydRj0aZotdAoGgpZjK6BsGrW15udvy7Hci+YY7xoM0Gg4ciwYThjMBhUejCbcgA2lCZi7IVXhs14YoHkUzlmUuMVIWjStG0wXzjRroakbxAkNLIhAlEoJip89ajIPzGzRsmUq9Hv3VDKhv4VEGn6sqnj5c+ilPpsK5YBOR73Ko2K2inpTK0yDJMsQlBe6QCJm3KY+nc+hO+RVziYUVrUun9mRr+1/aJBTDVSp2pMsOqem67nV1muYiZB95kOfkdOY3rE3K32wkmM1OXiGz5TK75jrhfCZRc0d/kyUXqRFkhlNoOqWl0IUMRP5QSRSlvNeTI7yThznsRP4MSAmI34IbRZG0O/TmNPBWmYsL3MOLSuNIiakuPxSqshTIp8v3378fHl+cb78rknBCglBhtUvizbhc6MVY5VV2qGpB7JcsUk8ROx/qQslX9UCkGw29zAV5nTLjK4+vb89u7nomFOjinbL6FYoMgkd8unsA5/PtvmENhdf0WeI8N74N9TnGs3juklMKJhqSzhRmYatpF9Rk/PyD/4wTypTsI7VE7XYsl7/pekday/SDVfZWlnIgnopey16Dygl+0753PJr8i71Uw+s2wI+nzMvoa+ztMXMOkssuaUSYh62/CFmoL2NtTDD0GM0aK7GalNsaqZ8ync9BzA4WySxjb62p5RCF9tWZh/etgvsM6TXJLBurQ9mIAmt0Nu2RIm1Tcs1pc7HPOfM6ZjKLqaiat/b26805UXzCavNMyRJWBhgmby8meXfljaYuHFml191a+2ln7ZlTfgyRppsqkSecF0R1vtsntBrvtL19XVhiF9D5mFEH+66a7ocedM5TWZ7dYoHB6Kyw2hSsxFZDmKpq47nS6DuPlxschN4U8m5Ta9xBD0F9BR4Rjc1D4Cri2joudvgMoTZWvQsBNXyqTVzgw29Gt0DdA+6yAy9gx3Mw4pZ77eDwCtz7N5BdRLtiV0Ca56iGyACugHLjN5dfSaJC92fzxUcFtRLFiPZe0X2IEa0b9/lwTz3G+dHPd5vHWN/i9yEyfwh+DKWo3xjYTUTpOx2inR8lD1bMKG5XDu49gulIYXSHBZ2cSxd5tAX4uaa9AeCd9cxdYMZRxr3j8Y2E+vsdWkij0VAHi8zukx9E7rigZMYB8ArOfQMxwjjHWHcaMX7jePjnetumWoGIXrVDZWIXkTv2oQzgca3GDQadPgwjQnfgzsP3WCD6XwKEAtcIoN7xWCchN5xEjoz3P2m7UkPfnHXF9J3i11f0GAk0xzcAIaE3tjcuAGs0nBPPz7GfWB9R3XEqC2b94BIXDgu2xJ53bpwzBXmwGapEb1lDr1BL64U7wvfFdONBO4xgXH3FkJ4Swgf6JIxwrjMoS8wxs1beXgUJB/RFq7TonJeMBn4GPKzb+U5tb4y3L2VB+Rxw+6tQldIriuHBWPcv7WSQ1+InGkT4nh7G9BmxPsN4rxWp8PiAzi7xA3niP4sIPrXhuJi5ZqryGERH4ffZQ59gb041xEH3zsPvjf3wj7g/bTG2ZXX3XDlGUm71SvLiNt6JRC3uPT8y19SPpZF55PbpG3N02mauB7CVgSE7Rps+ctRXEPyA/iRtgRpuwy70PbFf160Swxpu+E1KGGpEbC9Amyx+hCmyV1YWX3AIW7Zmkjd1iFuoTaHuZqM+C1z6At+M0XCEe8+S8mNxrzfYD659WQxe1HumEcUi4AoXmZU3lt1gG844T6ulRz6gl98w3jXmebSVvebtCc3BK7uh8c7IssWRNbWzgCZz+5jkItHYvcHP+eDiL5edALX5ypthUFC3YDvtK4c/1EeF3JYkMbhcZkD8vnY+Vy18P0m9GltvmrbH48z1GVrIqpbZ6gP+30nRHCZQ18QjDPUWXjMl51whrp/YG4/dxzRXLYnonnz/mg8FaS5EghmPBWkTP0pb5E4Ajaf1oh5XZT41hJSGd9aQhpvbm58a6nWdIfC4WN5eekEIRyFYSLz9ULErgiI3WVG13wVmW/eAhV5oGsgcZG4nWSGi8M7b94qDHW/EXtye7eK63aQryIgX5cZXXx344SJm5ic5a1MyFiCjF2GXRj7sCYhaXdvO2TsATK2co5KsSSAuBUBcdtwwPQhr+ria0krOfQFu7k6IXX3Ogbr4U6JAD5AAMcW9ajpdTlb+jqP+m/QzneDaeHCNRoTN4B2rzC+JjWlYyv69PsvyCV/YPuctnSb2HzGfBZRD1cByjZEt2ltFWCpJ7gUsF4J9JdwKeDXuUp1k42uUq9cpcoJOPPqCThIXhGQvA0TFod9hhlOWazk0BcE441YPOx5jFnNiPebxSf3ftjaaXR4kFnZjEjiTSR+4Bgl5HAekMMd9qEjgh/hJNEjONjs5ADcetE3MlgEZHADg7mi4Oo9IhhX7w8CxK02vN8sPq13wfgpkB0W798Vp0fuBzvoOa49zRg15ZnVns3y5cCO5evQSb7RiL2DdgwDFpQ9tBmYQepPrXm652K5EDcIbFpfEq+0Nd/EEEnc0qyvwbSnyW+gcUT3CKxmhayku4zYMXGf+Y9Z3iYtWdSbFl0zdM3qrtnCjZKUevzKs5hQzwu5pGxu08UJtPiOQ1Ml0F3b2l3jhgh9te1NgjDg/fbLjnrDwFXk+hSKfpMGAfOmX8DfCmSH0aRmEVrds0sg6T6s63KzOOJfBMT/MqPympdDPJgHp2RWcugL43FV5BFuFEfWHyzrm2BrcrwFdx1QXyx/vYUnurxOMdjIuM0TMhXERyyLJFvzfIZmBt7JsjT8dpGIut7tUL79++zyOjXfB6AvDrU21TpccyoaKvwRIAyGN7i7vo8T5v8bTAflAOGZ6RYQfBRWtSeyYFHcUpj8p44J0ciagVGwkhbs7KW1oBvUmvks6DKFeLaM/Nieqs98MAVcRV27oSzoqHYS+0k4qp/OPkDrgwjM+9rMVHEp0vImJR901Q1wxqpeCfRmt/ZmP6A7u4NpWDHp6M32ypsVgxGwL/XVLqQx0rh6qjPXEpJrCXKWIGeXYRfO+nik867TRg/2wT5A9rS27bQcxY2wFQFhi2s0SFtcozlE2K4bbeRuP7kr9jwjdstmROw2YJdrCVK3oSZIXaTuk7ymgtDtGXTXbmbGOwORvA/NLhd3BhZKc4gHN+A8c5lDbxiMdwfuc2pDgw3vN4qP+uiG4rWE91fTD+n36Tnz0+8kndvi5S5Ff+I3FLJNAugCiIAuQMt2L3wbEX2BDinh3q5fu7er39Q/zS1d+SyKG85xyjsLSN3mbV1cRQi0F8lUBlFLELXLsAtqrTANEhx67zsBzvtlv8l7WlPf2Q3QeEAighcPSETwPgV4c3VC8O4I3iM6GfHkBr0ssPktD13ebr7Io8pntu+WduMRifd4ZqAvfe7YGP8ILNiRWCsG4JetG12ziA87pqJTHMJ5VmkM9gVdaBGOrXvt4ULfVF/95+ekQuNkG0YKlIE+A5is4hjc4rwAfnzuYTnaOLlV5tAXHxuXkXb1roU977dXfbwTWtcM7KV9aH5A804k9AlEQJ+gYVrtkLeQ4rzaSg7I/CNmfrPt7jf/j3oDaetAnLPPAvIhd0VA7tbG4tTny/1c4cVuEiGb2nF9XEoxw30l9UoghhHDv2zoXVjwftP3eEffD+3iRAIjgTsTuNzKiTBGGHdJCWH8BNs7kcn9Y3LzfeZ4ukHZlojm1tMNCqU5xKlpJHCZQ28IjK9Y7DMv3WDD+43i05ycLo+JQgyXjYgYbsXwAR7yh/Qtc0D6HjV9m2x2v7F7cm9aVCcx8OIYpG5jRk0Xx+AhA8jfR+Mv3iGz7wQ0XiVzwOjNv+NXsC67aROMPTf4WmbTgWXl5xKgGy8v/htyqF1eHDFu4sIgnrnz4snqd/VH45swf/KhAwptNmeBXXcndroo+YGK1Taqc0ejiJXdlfwpDJMt6nouSh1/LGxyuZG+uZ6GHOcb7Leo78MCbPO4zjwvTCDjT2zFx6hWPC/R3nUvJHyTBgHzpl/ehUFwJGJtrNrxSvJhLT4usdZfejleuTYiJLSoN81R9+8Ks+Fe8XWRvoUCfb3OirNFVbO0WcmbUodPpNM2K/NxdNTGiYijqFnbm3fHr6dtjuDx1rzFJB23Ive9ds+nhxGekec/SYfwGw/8w9nN7zfQ2cJ55LKEy+UlKDkfAIO6v1p9QDz0s2se5GPg3Qt7nMaMmPeEpsksjNwf0IP5bOzCtVMYyxIxzoZfzXDBJJCCexdkx3sz6r+MX23KoXtZRPjmJjM3O+hB1JsP6WkAeiEG70GYiNLesYBF1OPjfGgIMxXTjNVGyLJ9alHn4eez/wNQSwcIS3bmJgwSAAA2dgEAUEsDBBQACAgIAPuuhEoAAAAAAAAAAAAAAAAzAAAARGVmaW5pdGlvbnMvcmVzb3VyY2UtSXBNdXhEZW11eFVwZGF0ZWQtdGVtcGxhdGUueW1s7VZLb+M2ED7Xv4JAgGb3QK0oyZLlW1qn3RzWCbJJ9ihQ5MghoFdJyo236X/vSLIUO068KXrYougcBILz+ObxDaGT5N8hE3LySN4gP7TSHs5ufrwhV7qqtQLL9Ya8uwZjtRIW5PvnDp3T41sxyGWZb0hWadIYIOmG8MbeV1p9BUlUKdVayYbnhqABL1GbVmugEoxalRzRiQVevDPvjyG8PZdOflf2XpXE3kNft6iKmpcKDCYgSVnZLtsVlKB5TqRqG5E2VlXlXhN62O896q08TmxlBE8kZKpUba4mWYM2eJiTXmVUUeeQbHiRJyxxJwVOWnLL5xOCg1hzrXhpb28vFnOSztwwEJGgnh+7NACe0jiIPJq5QkrwU5bGgF69MUtTzmLGKPixT4MwSymPIp/y2Jde7IVMCA+NS17AnFxcJZ+ah2QBRfNAmlq2I0YlzltoVdsu3T2bszyvbMsDJGTVaAHdcD4v6JezJXLDWFWuMH1y7p0T7wMLPrAI49lNjWB3v+BRIMSq0pv5YSjUmiZ9Mri4IghMOmDU6a3ZHZSy0vOOLAfX15ADNwh26p5OsMOVtmY+oYdgbZsJyVSOtkMIOlgNRtQCDgkTcjZFjiOtq7xabZLhto1RVhLGC9NHPWwS6xVDJ5DiDrfWkZ4zYDvrTDjP8bdOu9To5RlBxHTGAs/DcWeRxI8HdOamMY1FMHWzIBOzzB99exePp7EbhoyGWRDQYDqd0djPBM0gzaKAMRZ6bHQRjbFVob7ylhG9/5S5AJxn1POiFGmGhIunEaOCI82mM5GmsyfIkfqnzMG5DNc9B1/iQS97NMTnCl+figgN2OlDLzO6DWz7+Sn/o6TrZY96r5nV+CaDtmqYdJ9ljdRD3icG9FoJSMbpJE2j5Jz4wg8zmAkaQ4i9dkFQLgL8+K6YCRkyN56N4Qr+gAGM5SXWhOv8pFDly4rDBHrYNEgRRU6pSCGjwcxDQmRpRCPmZpKFUSSkdyRIP52Py1+/cP65W/PFxztygzuOTitdNTVm8UcX4M9+dXH7bfcwJwWva4y17VK/I0eY71zUuOndot+Oj1A7tpqnKle7/X5ht5wMGdFomD9jQPfOOrtBnCVmMlpVQjRaQ9fO8ZLuNJaS2+VPl7fLxfmiu9PwW6M0FFDao/n0vcTAm/m3wNxXwfYasPl2NW2TB6v2bJzrqrKjWkPeba+5V/VgtntnnEWXtbksj1Q2cONvVMhePO9WtsOJvfJeeQ33S91m9A+qPfnePwtb+f8v8Zj8N/8S/wJQSwcIpZC4s7wDAACiDAAAUEsDBBQACAgIAPuuhEoAAAAAAAAAAAAAAAA6AAAARGVmaW5pdGlvbnMvcmVzb3VyY2UtVHVubmVsWGNvbm5Gb3JEaHZUZXN0aW5nLXRlbXBsYXRlLnltbO1WS2/cNhA+d38FAQN1cqCweu1Ke3OzTZuLXbi2kZtAkcM1AYlUSWrbTdz/3pG0knf9ioMcUhSdg0AM5/nNN4ROin+HzMjJHXmF/NBJdzi7+vGK/GZNYxV4ZnfkzSU4bxX3IN4+dOid7l6bg1zoakeksaR1QModYa2/NVZ9AkGUFmqrRMsqR9CAabwtzRaoAKc2mmF24oHVb9zblzK8vpZe/lT+Vmnib2Hom5u6YVqBwwIE0cb31W5Ag2UVEaoDomy9MvoIhCHt9x71Xu5m3jjOCgFSadXV6ootWIeHFRmunKqbCoodq6siLOazGictmGerGcFBbJlVTPvr6w/rFRGxjGOeSVqKXNIkWQpaJiKiTCwkTxmwLJboNRhHrOTA04xKyJc0WWZAWYq+ccozLngMPBZorFkNK3LVag1V8fGd0bqHef3rDblCrim9QSOcO7eq8X3ZR7Znl0+Y+12DMW/e45EjWTbG7lbkrKqM75iDFDat5YC3ri3vDYa4pI8L3OO13VvegBbGrnpaPFJfQgXMYb7T+ekMsTTWu9WMPs7XAUqIVBXajiHoaDUaUQ84Dqwp2NUVDq8xldnsilHbxdBGwKRwQ9QjTMJBN+KAPA6Y94GIgjFtsJU8eJh673Q4/0EesABnGiZRFNNEIgESGQHN5mVOc56kc5lIJEg8+U5cyOeLRUgXMklokiIr8lhypEYpl0kYhosonFx467yp1SfWjXvwj2UWiWWc0TBOkXh5mtBMgKB5FGahyBZlHrLJf+L3aRjgSEb1QLSnWDDIEcfwTcInxhBuAUF+7OUmt5Fr7+7rf5FygxwR7zmzBh9esF6NQx6qbJB1SPPCgd0qDsU0naJtlViRPIwTYLCkGSwYTRYSYVqGOU3zKElECcs0klO4mv2FAZxnGntakfsh1Eo/ffG4gCFtxsssSmRISx51afkS132Z0/kcv8BEGufRC0GG6WxvPvzC2O9HK41OG2vaBqv43Af4e1hcXHbfv75FzZoGY+1RGtbjBeYHw7J85Lgr741d327vX45ueg0rVaUOYT/erkAiJ1oLqwcc6J/T4NA/OMdaJivDeWst9IBOSnoALSXX5z9dXJ+vf173Ogt/tMpCDdo/V8oI3wAoxt6tvibfwdKNZe+OYDvq5pkHY4B87H9fkQsujfGThYWqX2d3q5rR8lDngnXfgbvQTzf6FQ3OnwX0YaNfmthhX935G5o6+d5/Anv5/xfwJflv/gL+A1BLBwhZS+6PogMAAH8MAABQSwMEFAAICAgA+66ESgAAAAAAAAAAAAAAADgAAABEZWZpbml0aW9ucy9yZXNvdXJjZS1TZXJ2aWNlQWRtaW5Gb3JEaHZUZXN0LXRlbXBsYXRlLnltbO1WS2/jNhA+17+CQIAmOVCQZD0s39K6i+0lWWSTXAWKHDoEJFElKbfeZv97R5Kl2M7GzWIPuyg6B4EYzvObbwid5T+GzMjZE3mD/NRJd7i6+/mOfDC6MQocM1tycQvWGcUdiMtjh97p6a05yE1dbonUhrQWSLElrHWP2qhPIIiqhdoo0bLSEjRgNd4WegNUgFXrmmF24oBVF/byVIa319LLn8o9qpq4Rxj65rpqWK3AYgGC1Nr11a6hBsNKIlQHRNE6pesDEIa033vUO3maOW05ywVIVauuVptvwFg8LMlwZVXVlJBvWVXmQe7PKpy0YI4tZwQHsWFGsdrd3/++WpIgLQRbSEZFmhU0Yn5BCy4zWmQ8XsylCBcJR6/B2BeJECzNKPcLRqM4SeliHmRUzGFeJEUQL3xA45pVsCQfwWwUh/xKVDiCDufV+wdyh2RDE5w6N6pxfdGHlle35EIaXZH31x+0caxk7OPlsb/bNpji4R0eOXJnrc12Sa7KUruOSMho3Rre1WLb4tlgl4j0ifDS7OweoBbaLHuOvFDfQgnMYrZz/3yGwGJNdjmjL7N16BIiVYm2Ywg6Wo1G1AHOBivytlWJk2x0qdfbfNR2MWotYFLYIeohRMGgHGFAVnvMOU+E3pjX20juHefeOe2zYZAjTuDkgygM5zSSqcBPCHThFxnNeBT7MpJ8IeeT7+ASsiLzkySgiYwiZEa8oNlcciqhkGkUBEESBpMLb63TlfrEuvEP/kkayyIMOt4xzAt+SDOQKY0XLPYhkLEENvlPbD8PPJzJqB5o9yUSDHLAOXyh8MHRhBtAlF962cltpNqvz/WfZNwgB7x7zazBZxiMU+OUhyobpJ2q17ndzXyaTt62SiwJBCxhaQSUzxcJrmzqUyZwFf0EsQtZlCXzdApXsb8wgHWsxp5w3Z8vVP3li5cFDGn9KA4BYkmDTIY0EkGCy89TCmkSRVExj2PBTgQZprO308crvTa6bbCSv/sgn4fdtU65/j3OK9Y0GG+H1LAjJ9jv7TamX5h32qweN7s83fwaVqhS7QN/tGAe8s21BpZHNOjfV28/gHeNpUxWmvPWGOgxnZR0D11K7q9/ubm/Xv226nUG/miVgQpq92otI4QDqBh8u/yahHuLN9a9PYDtoJ1XHo0B8hGAXUXWu9XaTRYGyn6l7aNqRst9nfVWfQf2pn6l06/o0H8V0uNO/21m+41152/o6ux7/xzs5P+/wlPy3/wr/AdQSwcIJdVI1qYDAACSDAAAUEsDBBQACAgIAPuuhEoAAAAAAAAAAAAAAAAwAAAARGVmaW5pdGlvbnMvcmVzb3VyY2UtVmhuZkZvckRodlRlc3QtdGVtcGxhdGUueW1s7Z3Zb9tI0sCf138FgcHuJEDI1X34zbGdnQAZ2/D1AftCNMmm1QjJ1pJNZRzkj/+qm7dEyZSsiUWl+mHGEdl3Vf2qT/5mHkY40X77oTUI/5BB/nF2/6977Sbk85BRQcJn7d0tjUTIbEGd98sRVKQfTfPQrgPvWXN5qMUR1axnjcRixkP2nToaCxy2YE5MvEiDF0gATy2+oLpDI/YUEMhdE5T476L3m3JoXhYVvjExY4EmZjSpt839OQkYjaAAjhZwoUr7RAMaEk9zmGwIKxaMB5VGSLJ9665Ow48TwSObmA51WcBkWSNzQcMI/jjVkkcR8+ceNZ+J75lds3PiQ087RJDTEw06YkFCRgLx8PD54lSjw+l0MrGp3ncmXX1AO2N90rWG+rDv9AmZ9Lrd/gBiJS+Pus5w1KcTnY67A33Qn/b1iTud6rY9mHS6gwHpDCm8HBCfnmqLP64+qea9+ONRuwcZgyfQ2XbI5kKVNX/h7uL/zq60y96leo0FT9onamkjrdfpjiGSeJ5Dco+f4E8b5OSJh8+n2hUV33j4VfvS0/vwIIqt4tnnwA0JdGVsiziUBQppxOPQpo80cHgIaVGP2x6PnZVnt9SjJILsfu8and9PoB15KKLTEx0yjEXIgxv4t2xGTXOZB+9l0fX0uXxfFxTaH4pjPPseRH2cBe4nHl72qKyfEXBoBmNGiTDuL+/uzYiG0H+1icqY0EK0RwXEvJIR/4B4Mpk7FaualeBz7vGnZzP7NenweSyrIJMv5WeqXjpJ5DxpYin9wVP6U6WrZDwtiad6t0iM+eSJbp+WiraU1C0HmQ2o2D61IBWGanqLwDWZs1VCjyCRn50iCdcjC75dUyVRVFE07qa1DSJBAlvpBvRh3j2lXqkmD3bKIEIYTs/IZMFYuLaxJEnEgnIQe51IpUmWtT8JSzZg6Ex6/Wmvq9MBGeqDsTvVJ2OX6GPb6VBChuMhneZxkyiDqUVtl1Cd9FwKZqPb06dT2tG7/ZE9Hlnjsd0b51HsOBLcZ9+JbKEkvtW1rBHpDfVJpzPVB8PhSAfj0de7jj3tuqNRr0O7efzcuiVKmf2cmJlGypXHqXTVFdj/kMYRsTwKbQIQACtznr+b2Z3il8LE/Ecig9n5k4r9OUu7JX06B9jSULCsu5WeKykp/q0BhISpVPV0RfSKfpNasylSoY3VOGY8BwmgJpgHZkMJby8/Pnz+cpG/tCoi0OoV9anPL3mnaAXuim8kpKbNA5c9mdAKQSQt4ql2c/3li3l3eft4eWuef7oqmpQH8BbzVEcxm5qZrsAfDmgT+AtFXcCtCE1ZThOMok8g2T8uz+5levfX11/uat5bqffNl7Pzy6oIbWjPkqlc6rrVlO8+//eysByy1mbnZa2252WRTQ0Z/F/BxJC0aarFvelkPOx3bN1xemN9MBnZ+tQdSUwPRiPXHdpd0l/SYtDdkW33HZ10h5Y+GFFLn0ydKdiB0dAmbo/QYWeTFk+s8bA7dMBvGHcJZEnACliTgd7ruI497gyo1R3WaXFvVY1LeK1X1ls6h0ajgQDfTQPIMRs8NvgnE8/g3xGhkSjiNpNWVbOgGSkNtHNw92JBlbOXOQyBkhy2oKpTImNJ3c9vttX2LOFLj/qyeBu0PqRg9m31Wi44Zw/31/kLxPFZYIICCKmy4M6FcUmTI+nzkdgTy3rBQwccCK3oq1SONol2hbYnWfn+F7MwqUYWV9cs6bsHT+XEbDInFvOg7VOX08h/gQrnYvwRYkrjWoophT0pQenHkHpKqqIZm2cJln+rphjdc4j7FPJ4npbSApfNVLVSv67VuiROomrSj7oTxP6a65dvgYQW1S6VUS/rdFN97PeHI+lc69ZgOpZaaOmgW2O92xkOev3BeNSbdpb0cWKNOm63S3V30AHF7QIaJ51RX3c6Uk170+HUsur0aVmZltpDPV3ipJG/Yxg+d2KP6uutVdpuj+6f6s29tdgiTRD+S72rOpzXFbM++udq65MOOBETMtF7Tg9MW38M45pBF0zbhPZ609F0AD/VJ5TFn/Sdsey9CQxwBgMHXJ0hmLaB7Tpdag8HxBnUx3/M+6WX9UudLZCKvnDNpFI59aJTreT35I89YlGv1K9FKuSv5qmU7WnN46TbP5KSYVEDTOIBz+NAVBLlXuzTVN1yawR2EcZvQg2gTZ/M52A20ionfu8G5zbzbS9mi3SwWDIzpZZTsgAj9a8GOG5lb2gl8YpJAuEDC26cU+Zx+HudS3imCqrSSqjCAtuL4RU1h/CnSqRk/dS0hq2Yk7wt4GEA/NF8Dk0HIzcHnBE5orXzjAvicNuOw5CqDst/1EutrGsPVx+vH64uLgtXrU6Wso4SVZeydrCSVToFSdL+lWcpBVa8rxwr5rLb1CSjm9vPj2f3lw1zqhXVZhndQ0fNZ8+RchAi9l269pr1LPsIxmOyG5PBp/Q8CZNOP09miGY8EktJliTjovhHlk4iUw1rpAq+ZVX+Q+Inur5I91JKK2XRFsSL6Qc1qwJySP8iciLog3Ye+7GnvJ4PUBFPkA9J2sozosI21lSi4n+AflMQ2NpaWBzcCRLUV2O5KTY1UzrfspoDmJMtkthGRNenFINWbdtnH9f31wMkV9dfzRofFF9wm3vbFkjY2zRcXerSKb+gblMjsYNxKOz5+tYrTHfWeMpKq4kWwTODa2jvpAFQvxY2F3znxA6/b9bWuXe/Qz021CBNtSoDq9pqJ8OWDyq5D5nV/QA585C8rLQrUhx65pyI2avUQWHXnsfIWxWQt3lG5zcPmmCgbnFEnfXC/xb8LLCHEG0VRINofYchRdeVEcxzu/GZgu94CZpOiOTTWUA77kN0Y07sr7Sss0hXpKvM6CqWk2xS3jNZ0VJZQdSu1gRRuzVqE2lC3G5vA9YZ8XYzOK3V8TI4mTx2qFxtNkJKHEO+SyMRGWG6ZaacNiJ4O/k5PgSfLagSWCkdUuilzGiZzBwWhXHCuMihLQBOJenfOOTddeJ4rS1vN4mPfDJZdZ0kny23kSB1ZUDqVpZxiS/3HUgJl8KS9I1awE33OaheiugGPUcII4Rx1bZBKjvDN7PgyNpDZ23qJn0LmaCG2hCDA96i9RC9awe8SmJwpFutBEJ2e8jiGPe1Y9xl443UPVzqvrTYi/QtWhHpu5a+h73uixwucmgLhxNBQhjvc9H3CGj8i6z8qmUC5UEhe1VA9uYZPapjbdki74GhFrdYLeXQFt7i5PKuo97CVrcbrUe/sbkyw4wHc4smQ7ju52CuBm2rJfJ1WFTGAXCRAwL52IFctvDtRvKvMfe8cndPktY2d/asgWGn9PcqDEFnmGMmDFPXflTiNr8IK7se7LFnPH45u7qLrc8BCIlL7FXvI1kbwc3c6IRsu7aNu7nrK4GuBe7mLlL/SS5GjRlvt6fxaw3+iykbJHDReEjgjcepELzVSiB4cXPZGx2gwr1lrUDu2wzsy1MLLozTK1ZpbQnkdwteA9U1W9hxpI9+Bo700eHAkX61YG1zO45uwH/krsfCnse4dy4JyNuam8oWLBQx8eTNoJFGPI/byefdeOXwNhJYQwLnYRcCLzZeeInw3dRuuKHusCm7fHiMx+KJy/MGOLWOCG465M2ERnthCy0CF4GLc+yNUnnNmbEaG95uCh/9ibHNFEYAq4AArhkDHzJ68SDZUg6t4S/Sd1/0RfC2E7x4dwryd4cBMN6dUlcJRDDenaK9IYnx7pTWAHnNHm9EsAqIYLxCBbGLI9/D4m29yW43aI98b5XqMsbnCNYkIFhXxrbqUxhSRA4LrTiYLXJoC1Vt+XkVHMvuzNbNWog4fXOc0sCZcxaIZZrWHFK6TF81zhyfFcZhjyzbn663RbGOjd57MPg7YmlJ3X/+lA/6pHk4Nqnep0+K1/She4ruabn53n7qB73UA/dS137EFoGrAgK3ZoPhAX+FHFdZlnJoC3BTcULg7vFD5EjeAyYvX0FszczQNSAJ7E/wdPccCer/HYSDdoFyW/EWSNiLyV6fCAzsojWFSR81TIiE9owJaos11ve120Ln5Z1E6DCpgA7TphMZB7kXFH2mpRza4jMl0oQu06u2g1aMeLs9puPfCWrPYxM8FQ9hqwLCdmU54PzmQZMSwr4TsewpvjlpcSWgyKEtkP39n78jYbdX/MxSt5uoR3/XT2UBACwKYOkZ4aoCwrV+rT2VElxuR8juDbI+rrS/cuI/1cp24/bIp/zf4tp6/CYOsn2rewsO8OgkEr3IoS1Ex/v69vHp+SO4nuDoh9DZwjBglNgznzY68nGWv7wfzFc/ZICUVwEpX7MWfcjfosGV6KUc2kJ73L2nwp4+PIvAP2jgAzb9SrsgZBGy1fuHbs/+xK/PIG6bpLQLbv/EO4l2sAOJ2Ua4HjRcl7dZAwW5z/DjMwjeLeawM6E5xBvwkbdFDm3hLU5mv2qXdY0NbzeFf4mN1oYjjQrCVgWEbTFJe/OgCQZ6F0fQ+tD4NoVGowvG40hziCDqLqvDgq7iI0K3VdANkLm77btODHe7CXv049xk2TadI6xYYeQsclZmJLWP+PIeKinjauu1bOg5g76wniszyhqgVP57xiMB9t2esQBnmauVQADjxfc/bTm3MOvtpvCR78cu75/HDVRIY9xAhazFDVRaG4lba8URvgcMXzlWabBR+pwHgsBwpiDrTnwDXWGOmWDJlJlV4ib5SkZHxh13xTcS0nNoOB6UN2jXMzKIfdOex7XWhwXQSaWib25b2UNmxL7Xa4BNPBLq0rYY8p2Gacpj+K7Sh8rB7vp08xcbJu5Tf5/lLe2tM+IIhBO9MBXQC1uzw04tQCxPhbgh99UvxdwJE1FpN95L24BwegRdNtyE1ySV3TfhJfa93f7Z0S9RbN6Kh3BWAeFcM0VyyPvvcIpkKYe28BZxu7cteO0G7/HvviudCcRt7wjcptvelcQcGG5xZFvk0BrS4nb3V57kPpaN7kc/xk1QS+cz6oMt9YzqdDaCFkErM7oDqZASnsvJC4tzCFoEbaM+wzHtrpytmmykbAsoi98XRcg2mj7G74sianGD3WHx9og+LPqL4LY0E4GwVQFhW7OR6hAnjZGySzm0hbI4nt3DvDHS9bDpmm5hC2nEnJrbvhGu2wnMMcN17S7l9MD2fPYcQe95eGi7thJIYNyV/DN3JWcmHQl82AQuH/mTKLQBhIhhFRDDmy5QKb5ZmXRThcmywyKKDK5WAhmMo+CffYw7s+nt5vCRH+DO7CaCVwUEb57R5V8skl+jTe/ixq9e1FQCsbo1Vl+WJKTr7m3XBqIe/8hWXZtheU2+EX2Xvvp3wMxngZmJTOObTfJu6jZsNp/89RNySSNsn9NLExDpJ9fR/1EB/Z+Vc1lqyiGVEnR/NHR/8rCL++PjoaxdpxVe1ME2OD9HPp2Q7DLkXOAZrKK9EKorZ7CkiODxq9VKIFHx+NVP3A6eGep2M/X4JxTKyyr4tRHE626L5fjhEeTypubG9fNSw739+jl+f6R1bMbTWghnPK2FnN3c5sjZUsO9PWeP5tDWsYI2/c2nQaGp5autHTqngVNdxV2DsM5GhBX8rF2rvwIC5+9KHJ9Wvjlyy3lx5iCknnLdohmbZ6+Vf4uMC1Xq6Doo1eiIqpJ1jseCry/WpVv79wt1yG7F/QI5VLZQvFzgctTonpd7QPrcnpkK+9/bCWdCwGDbLx9VWe2Kj1Cgr3dJcbaoYZI2lZX7zTyMcKL99kNrEP4hg/zj7P5f99oNuJUho4KA5Xp3S6XJsAV13i9HUJF+NM1Duw68Z2X64ojK+RASixkP2Xfwi6XrvGBODPZHU7YRnlp8QXXoDvYUqM+PCEr8d9H7TTk0L4sK35iYsWQeRtVbmmESgJgogxtwoUr7RIPs5jpoCCtWLmK5EZJs37qr0/Dj5P8BUEsHCAqU44lPEQAA5mIBAFBLAwQUAAgICAD9roRKAAAAAAAAAAAAAAAAOAAAAEFydGlmYWN0cy9BQUktU0QtV0FOLVNlcnZpY2UtZm9yLVRlc3Rpbmctc2VydmljZS0zLjAueG1s7VlNbxoxEL3nV6Dch11/rNeu6EqVEqmnXhK157E9TlaFBcGGln9fAyHZwIavkEhV4IY9zzN++L31LL3B0FO/83fQryZfL+/revQlSdxw0MW67iKW3bKaUlUPx7Nkai6Li0789BYYiDM4LrGqofSFYD6zClOQMpMgSQgwmBvwFlMttFbO+F7SAmyuWM9GVExoPC0drYIXY82gKY0ny4GXg89ja8HlsJonkkqmjMiByKyPFRoF6FMPqchUCMpqp58qbMDaVq1wQMXNFfz69qNzsyy3E4bjzi1N6rK6W62yCNtSVSG66VrG1nBPEzcuR/V8viXr1fefq8ydsupc8+vVok1g28LUp0H8cScvJzcDNucXMRX9AY81wjw09PGuuO0lm4PtYIdjX1bYL+tZ8VDZ4UPlKfLfHG4H7ir9kC2cZCtv3tIrW0t2RI+pj/OfdnJfjqBfTnbscQOzO/wZQh7qYfF0VHtJY/SAZVbVzlndD7eJ/U2z50K663p9rKwZfGSeKfYfqGDWIjOMAQkjQKpgAfNcABrhueGKOcfXci6B+7GSHEnLO9DZ3TTnU5Npdaqkyx1wYeKTgtCCkTmHkDrvSVhmDX0wmS9BOxSXHCC5lQfv9J+zT+3AnH1qa56lQlS8fylBGihn8f41NysdjAHnpE6ZlJhmHy2tzVL/F5+izBitHYHwmkWfSnPQzGaQCS8QNWdMyLNPbavg7FPbIZ/WpzhaRy7TEMjkIHNNgJkOsSlz2nknyIn1nGef2syy5MSLIISL7FlvQuy8cw9Weh77WhVchoRahLNPbavg7FPbIZ/Wp1KvvJ+/wHKpRZCZilcAwQx4EfsUZVmm0/N9aneWxyY6tx51iILKjYV4F7VgXTBgTXwSiOC5Vu5T+dRa0CtvsQ5R+gEKf6OyjzmC763ktkMnlTU8dj5guIgK9jwFqzME4azXMldKozn80B182E5I14mU2kZW7GmMxMAiO9yBnPOkQ+RO8pQ4R0OIR3Q8B5G1jyL3UuJWBb6uvMab+Mf/Gxqv5ouLx2/FP1BLBwhSMY4YCgMAADEZAABQSwMEFAAICAgA/a6ESgAAAAAAAAAAAAAAADMAAABBcnRpZmFjdHMvQUFJLUlQX011eF9EZW11eC11cGRhdGVkLXJlc291cmNlLTEuMC54bWy9Vk1v2zAMve9XGL0zsvwla8gMFGgH7LBh2AbsWFAS1Rpz7MCWs+bfz0mT1kncJO7Hcgv1nkg+8xGazipDhXc/K8rm08Wdc/OPjOlqNkHnJoj5JC8XVLqqXrKFvMg+eN1vuuZAd4J1jqWD3GQq9ZNICw1BKH2ICBXISARgfW0MhYorSVM2QOzf6JZzympqqrbWj+h1sI9aUN08BHaDT7E9cF6Vq0xcKeSSc6BQhhAlVgEKEQLK0AQySLjWwTZpjzZ0a4kzyr58v/na3t9c0ay999q5QUdmy18DjtST8Ym/l2sQbqjRdT53q/OdfJdFUbkuofdjI5dnq9r7eQW/L795jhqXl7deXnrXwbUXMB4xLrb5+ncO5aSCZt0nb3YPDwGH52tMSX+h0wJhBbUF3ma/puwwOEzWWJu8xCJ3y6wtVdWWZiVqPzxMPFX6mBaOtPL57FZe3dIzrbET6JoKXH3a5i6fQ5E3J3o84JyGP1HIgKuyxymesl50xDXbaleqnsc75P6h5VMhk30Tbyrrg1+YZ4FFS5mKlI60iUErshClgQRplQDBfWt4IoQ2wV7OB+J5qrAXyvIOck4OV/ZbixnqMLGUapCURBD5pAE7dQFDX6faJNyX6X8Wc5d0wnFshOW2O/j4CmXnbLMxTh/h8Fc6+yUj+N5OHho6gcaXPLYQxLp7r6DwQUmlgacxitimIk6i8UM3etjeUK43cuqQWDYxCQahAeQou3VnLaQRl6CSOPQNBiZM+TuLdY4jz3LiUQc+77zeY23zGGX91+jmX/YPUEsHCJIoy+hQAgAATwsAAFBLAwQUAAgICAD9roRKAAAAAAAAAAAAAAAAOwAAAEFydGlmYWN0cy9BQUktVHVubmVsX1hDb25uLWZvci1ESFYtVGVzdGluZy1yZXNvdXJjZS0xLjAueG1s7VfLbtswELznK4zc13qRIlW4AooWRc9FUPRWLMllIlSmDEl267+v5MSJbMuRlEeBovFBgJez3N0RZ0AtloWhfPZ7mbvq/eVNXa/eeZ4ulnOs6zliNs/chlxdlFtvk1ymF7Pmt9jlQLOCZYauhsykJrJRpKUFZRILjAkDipkQ0MRWcySUkV14PYndHevtitKSqmJdatqjd8EuakNldRs4DD7EjsBZ4dpKISpNmkuwlAhgQhIgbzqOuJba6Ih0ZPZFO2l9uzpcUnq1do7yH98/Fs7NbFHOPn35Nruiqs7c9X6fHfCRvtJg7h/V7IUbqnSZrep2/aDuh6/nSndT+raknJbNm60OF08Bp+s7jKNfYLBGaKE2x+v0auGdBvuTNZYmc5hn9TZdO1WsnaGG+264P3Go9SkjPDLK59GjPHukM6N5A+iScmxfbXWTrSDPqoEZT3KG4Q8pZKAu0vtDuvA60Qnb7LttWR2Xd5r7k7YPjcyPtXrXWRf8xDobzNeUSq1kyGwASocILNaicQyRgO83T0LDoyQ8qnmbOI4V74m0vAKd81NnfmkykyBihCRAUtySaQ0kIkiAJyFjRpHgof3LZB4mDSjOmyC5vQcP+s8zfWq85d5v8uZTY7v9d3yKhFZMowQKkDeXL9n4lAhiYEi+0kYiWf7mU4NVbjnhvkootNi6vAamFIGKeQBRyANDmlhD6H/lU0egM7euKUqfoPBnKvspR/C1ldx36AQaPwm4hZBrv9Gt8EElSkMgOQpupeAxm37oJh+2F6TrhZTaR5aNTYxhZAADTIBJa0Gy5ibRyDTyDYYmksErkzVGkaOU+KgCzyuv8814923c+YhML+7+pX8AUEsHCA/FNMyCAgAA3g8AAFBLAwQUAAgICAD9roRKAAAAAAAAAAAAAAAAOQAAAEFydGlmYWN0cy9BQUktU2VydmljZV9BZG1pbi1mb3ItREhWLVRlc3QtcmVzb3VyY2UtMS4wLnhtbL1WyW7bMBC99yuEnNoDtYuUClVAgKLIqSiaoNdiSI4aoloMinbrv6+8xbKl2JJj1zcP35uN80ZMy1piYf0ti6r5dPdszOyj44i6tMEYG0DZqlpgZWq9dBbJXfbOan/pmkPaE9AKKkOUzDzGJcQ5EMkSTkJwOeEiTwhPRBQHufRjKlJngNj1aJYzzDQ29VwL3KHXxi5qgbrZGA6Ne9sRWNXVKpIrqZTAEiJcDiSMKCNx4CVEBhhwyr0odl+CdmhDXisoMXtEvVACf97LUlVWXmvr88MP6wkbs/Oyhp3IKvNs9yjiIFxiI7SamdX5YdT779b7XNel9fD1W60NFACPHwZz6foYioEFlu1FN4eHfUD/fI2p8A+RYNr7b6F5Ab+yp9TpG4fJArRUFRTKLLN5xet5JVGmTtc8TDyX+pQSTpTyZXQpby7pldKcM2iNBayutnlWM1Ko5kyNPc55+J6Ckpg6e5na1OlYJ7jZZbvq6jhen/sbl/tE7GPpbjPrgi+Ms4BijpkbRj5ilBMvyX0SSo+2C0QwgoyGYciDKJJwFHNDHNcV58K23KCddn9RX7uZ6AEFFiIRQUzbDwZzCch2Jbs0gcCHMKEB+8/NPCSdUZwzQXK7HXx6hTpjttkUpU9Q+BuVfckI3lrJQ0PHQLqJ1yrYj4S7GTqecEG8OAIW5TGLaDh96CYP2xXbdSWlDjUrp5KCH0gCHiQkjPOcxGH7XuI0ClwJvgxi78bNGqPIUUo8qcDXldd5nG2foE73Dbr9l/0DUEsHCC23qLFCAgAARQsAAFBLAwQUAAgICAD9roRKAAAAAAAAAAAAAAAAQAAAAEFydGlmYWN0cy9BQUktVmhuZkZvckUyZVRlc3QuLmJhc2VfVEVTVC4ubW9kdWxlLTAtcmVzb3VyY2UtMi54bWztmktz20YMgO/5FZncIe770VF5S/5APL129oGtOZUoD0k59b/vyo5s2aIdkqLjC+2TQABcQItvAZHr7S7i5vN/203d/vnluutu/iiKsNuuXNetnKtWVX2Ldbdr7opb+6X89Dn/re9tIF9xTeXqDqpYOqIUM84Ai8yD4FqDEdQCGmTMKiuyaF30GJ567O5usGyw3e2bgEfte+Gp1i027YPgufBJ9kK52tUPSzQ8amvAG2tBiIjgpBEgQooUgxQuiuNNT8z6vNZui+Vf13X6tmu+MrzCtlutvGvx76uv369Wq6y13yCQo7t7/TeWV7IXN+5VjtiGprrpDteP+qeyPhvc4DZ/f+3zi+cK59fvdWr8AdF1Dg6qaeP+Ka/Wxbmw3zi4Jla121TdXbmv/W5fR4zr4lTcb/irpY8JYZZQLg5pdGhTQnwj1G+jQ50t5P5I2mKgVYMbd9jd7XV1A5uqHZiDM9vhZk+mGKHblY+FuS5OpBPcHaM4fAvj7M99/It3TwtbvYTWz5WeKl94v1u32WOZlCPcBAaGkQCCeQf5kweuJTKBJlnOXtz7wXBctooL0/WO6V6dH2HvlWyeRNCMCNDc5iPVew9Gu3yuOmWkEtwGSz442c+NB1Z0MaGkj8fdYA4u3Fy42efjY7jpfMj9pQ/gvc4Np40BLBEcdNI0/xMthVy4OVOyrQ5MhdziK6NJ7vMDzcgkGmi0hMQDVVEt3HzNYOHmws0eHx/DTa4YIclLSExSEEnGPLIzAjpIF2VgIQq/cHOmZHsX0EQawDGVk00EBYs895uMU6oFlZGkhZuvGczDzfE/STw6W7i5cPOx3+S5hHmKQI23IKRFcM4bSCl5bimzSrmFmzMlO6AjzhoGJkqZuWk5WEYRBDUp9/1JE7XM6a8aLNxcuNnj42O4KbWiSJyGoGICEY3LLZCP4E0SQhIpIoaFm3PN6SofRJjPp0RipiUSmef0xEBzKpTQPGm1zOkDlH/xFGkKYSaQZSaiXLK1fxdB+jazweBZyPOpplqB4FTnMSq3A0wxLTCmGCWfvpknb+J3SOfMhOh9zJSUjRkI4Aya3EcRAz56AjEqzqhGxYP+zckcQ4JRBBhU+cMqfkylj6jwCyt7yhZ870rubeMJkYo7BEMPb7Cgyg0954ffmkQkSWhizISZafRmmzFdM1VqX7IwGZVsyHyjKs88InEwKeTBR5E8cipi0U14oDEqWUMqclAlvlmBr1feyRs7D8LTV3jKTz8/lf8DUEsHCIVbPyCsAwAAYSUAAFBLAwQUAAgICAD9roRKAAAAAAAAAAAAAAAAMAAAAEFydGlmYWN0cy9BQUktdkhORi1mb3ItREhWLVRlc3QtcmVzb3VyY2UtMi4wLnhtbL1WTW+bQBC991eg3AdYWNa7lYtUKYlyyqVRex52hwQVgwVrt/73xY4/sCG2cexyY+a9nQ/mDTuelIZy5+8kL+pvd2/WTr96ni4nLlrrImZuVsypsGW18ObqLv7iNM94xYHGg1WGhYXMxBQpJaUmCI1kwMkfgWRJBFFoQkQZMBbysddDbJ9oF1OKK6rLWaVpg14Z26g5VfW7Yd+4sx2As7JYRhLMRCIkCTRiHHioQpCpUqA1lz7jHP1oG7RF6zu1wAnF86fnRyctK+f+6afzQrXdkFfeI8nEgesfBOqFG6p1lU3t0r8N9uP+1/dn5yF4WIXMilfnkRJHOIHPRptD28S+gymnSfNR631nF9D1rzAF/QGDFmEJTXN8jV/GXtfYT9ZYmazAPLOLeFYk5awwZMZe29xPPJX6kBKuUsqnS/qgNO8EuqIcl5+2fsumkGf1iRo7nNPwHYUM2DLejurYa1kHHLPJdtnV83hd7m9a7BJxD2W6zqwNvjDOHPMZxejL0IyUhEQ2K4JzQ4CRbNaGTg0jHXE0/CDmO/G8rngXtuUG7XS7S/n6zRQikCghMEHSbN5R83PgTAFJCgIlFG9M/7mZ+6QTivMGSG6zg4+vUO+cbTZE6QMU/kllXzKCt1Zy39CpEAUTIoUkNBHw1BeQ8AQByXBpfF+hUcOHbvCwXbFdV1Jqr0K1FmSMBJSmWXJJczfCFBF8pbgvZEKM6xs36xxFnqXEowr8WHmtG9n6uum175vrt/gfUEsHCEh5dc82AgAAMQsAAFBLAQIUABQACAgIAPuuhErzb7WPfQAAAJkAAAAZAAAAAAAAAAAAAAAAAAAAAABUT1NDQS1NZXRhZGF0YS9UT1NDQS5tZXRhUEsBAhQAFAAICAgA+66ESkt25iYMEgAANnYBADcAAAAAAAAAAAAAAAAAxAAAAERlZmluaXRpb25zL3NlcnZpY2UtU2RXYW5TZXJ2aWNlRm9yVGVzdGluZy10ZW1wbGF0ZS55bWxQSwECFAAUAAgICAD7roRKpZC4s7wDAACiDAAAMwAAAAAAAAAAAAAAAAA1EwAARGVmaW5pdGlvbnMvcmVzb3VyY2UtSXBNdXhEZW11eFVwZGF0ZWQtdGVtcGxhdGUueW1sUEsBAhQAFAAICAgA+66ESllL7o+iAwAAfwwAADoAAAAAAAAAAAAAAAAAUhcAAERlZmluaXRpb25zL3Jlc291cmNlLVR1bm5lbFhjb25uRm9yRGh2VGVzdGluZy10ZW1wbGF0ZS55bWxQSwECFAAUAAgICAD7roRKJdVI1qYDAACSDAAAOAAAAAAAAAAAAAAAAABcGwAARGVmaW5pdGlvbnMvcmVzb3VyY2UtU2VydmljZUFkbWluRm9yRGh2VGVzdC10ZW1wbGF0ZS55bWxQSwECFAAUAAgICAD7roRKCpTjiU8RAADmYgEAMAAAAAAAAAAAAAAAAABoHwAARGVmaW5pdGlvbnMvcmVzb3VyY2UtVmhuZkZvckRodlRlc3QtdGVtcGxhdGUueW1sUEsBAhQAFAAICAgA/a6ESlIxjhgKAwAAMRkAADgAAAAAAAAAAAAAAAAAFTEAAEFydGlmYWN0cy9BQUktU0QtV0FOLVNlcnZpY2UtZm9yLVRlc3Rpbmctc2VydmljZS0zLjAueG1sUEsBAhQAFAAICAgA/a6ESpIoy+hQAgAATwsAADMAAAAAAAAAAAAAAAAAhTQAAEFydGlmYWN0cy9BQUktSVBfTXV4X0RlbXV4LXVwZGF0ZWQtcmVzb3VyY2UtMS4wLnhtbFBLAQIUABQACAgIAP2uhEoPxTTMggIAAN4PAAA7AAAAAAAAAAAAAAAAADY3AABBcnRpZmFjdHMvQUFJLVR1bm5lbF9YQ29ubi1mb3ItREhWLVRlc3RpbmctcmVzb3VyY2UtMS4wLnhtbFBLAQIUABQACAgIAP2uhEott6ixQgIAAEULAAA5AAAAAAAAAAAAAAAAACE6AABBcnRpZmFjdHMvQUFJLVNlcnZpY2VfQWRtaW4tZm9yLURIVi1UZXN0LXJlc291cmNlLTEuMC54bWxQSwECFAAUAAgICAD9roRKhVs/IKwDAABhJQAAQAAAAAAAAAAAAAAAAADKPAAAQXJ0aWZhY3RzL0FBSS1WaG5mRm9yRTJlVGVzdC4uYmFzZV9URVNULi5tb2R1bGUtMC1yZXNvdXJjZS0yLnhtbFBLAQIUABQACAgIAP2uhEpIeXXPNgIAADELAAAwAAAAAAAAAAAAAAAAAORAAABBcnRpZmFjdHMvQUFJLXZITkYtZm9yLURIVi1UZXN0LXJlc291cmNlLTIuMC54bWxQSwUGAAAAAAwADACcBAAAeEMAAAAA", +{"csar": "xxxx", "artifactVersion":"1.0" } \ No newline at end of file diff --git a/src/test/resources/jsonFiles/missing_artifact_version_request.json b/src/test/resources/jsonFiles/missing_artifact_version_request.json index cfde298..4eed380 100644 --- a/src/test/resources/jsonFiles/missing_artifact_version_request.json +++ b/src/test/resources/jsonFiles/missing_artifact_version_request.json @@ -1,2 +1,2 @@ -{"csar": "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QUktSVBfTXV4X0RlbXV4LXVwZGF0ZWQtcmVzb3VyY2UtMS4wLnhtbFBLAQIUABQACAgIAP2uhEoPxTTMggIAAN4PAAA7AAAAAAAAAAAAAAAAADY3AABBcnRpZmFjdHMvQUFJLVR1bm5lbF9YQ29ubi1mb3ItREhWLVRlc3RpbmctcmVzb3VyY2UtMS4wLnhtbFBLAQIUABQACAgIAP2uhEott6ixQgIAAEULAAA5AAAAAAAAAAAAAAAAACE6AABBcnRpZmFjdHMvQUFJLVNlcnZpY2VfQWRtaW4tZm9yLURIVi1UZXN0LXJlc291cmNlLTEuMC54bWxQSwECFAAUAAgICAD9roRKhVs/IKwDAABhJQAAQAAAAAAAAAAAAAAAAADKPAAAQXJ0aWZhY3RzL0FBSS1WaG5mRm9yRTJlVGVzdC4uYmFzZV9URVNULi5tb2R1bGUtMC1yZXNvdXJjZS0yLnhtbFBLAQIUABQACAgIAP2uhEpIeXXPNgIAADELAAAwAAAAAAAAAAAAAAAAAORAAABBcnRpZmFjdHMvQUFJLXZITkYtZm9yLURIVi1UZXN0LXJlc291cmNlLTIuMC54bWxQSwUGAAAAAAwADACcBAAAeEMAAAAA", +{"csar": "xxxx", "artifactName":"hello"} \ No newline at end of file diff --git a/src/test/resources/jsonFiles/success_request.json b/src/test/resources/jsonFiles/success_request.json deleted file mode 100644 index 92fa93d..0000000 --- a/src/test/resources/jsonFiles/success_request.json +++ /dev/null @@ -1,3 +0,0 @@ -{"csar": "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QUktSVBfTXV4X0RlbXV4LXVwZGF0ZWQtcmVzb3VyY2UtMS4wLnhtbFBLAQIUABQACAgIAP2uhEoPxTTMggIAAN4PAAA7AAAAAAAAAAAAAAAAADY3AABBcnRpZmFjdHMvQUFJLVR1bm5lbF9YQ29ubi1mb3ItREhWLVRlc3RpbmctcmVzb3VyY2UtMS4wLnhtbFBLAQIUABQACAgIAP2uhEott6ixQgIAAEULAAA5AAAAAAAAAAAAAAAAACE6AABBcnRpZmFjdHMvQUFJLVNlcnZpY2VfQWRtaW4tZm9yLURIVi1UZXN0LXJlc291cmNlLTEuMC54bWxQSwECFAAUAAgICAD9roRKhVs/IKwDAABhJQAAQAAAAAAAAAAAAAAAAADKPAAAQXJ0aWZhY3RzL0FBSS1WaG5mRm9yRTJlVGVzdC4uYmFzZV9URVNULi5tb2R1bGUtMC1yZXNvdXJjZS0yLnhtbFBLAQIUABQACAgIAP2uhEpIeXXPNgIAADELAAAwAAAAAAAAAAAAAAAAAORAAABBcnRpZmFjdHMvQUFJLXZITkYtZm9yLURIVi1UZXN0LXJlc291cmNlLTIuMC54bWxQSwUGAAAAAAwADACcBAAAeEMAAAAA", - "artifactVersion":"1.0", - "artifactName":"hello"} \ No newline at end of file diff --git a/src/test/resources/logback.xml b/src/test/resources/logback.xml new file mode 100644 index 0000000..4f51317 --- /dev/null +++ b/src/test/resources/logback.xml @@ -0,0 +1,190 @@ + + + + + + + + + + + + + + + + + + + + + + + ${logDirectory}/${generalLogName}.log + + ${logDirectory}/${generalLogName}.%d{yyyy-MM-dd}.log.zip + + 60 + + + ${errorLogPattern} + + + + + + INFO + + 256 + + + + + + + ${logDirectory}/${auditLogName}.log + + ${logDirectory}/${auditLogName}.%d{yyyy-MM-dd}.log.zip + + 60 + + + ${auditLogPattern} + + + + 256 + + + + + ${logDirectory}/${metricsLogName}.log + + ${logDirectory}/${metricsLogName}.%d{yyyy-MM-dd}.log.zip + + 60 + + + ${metricsLogPattern} + + + + + 256 + + + + + + ${logDirectory}/${debugLogName}.log + + + ${logDirectory}/${debugLogName}.%d{yyyy-MM-dd}.log.zip + + 60 + + + ${errorLogPattern} + + + + + + + + + e.level.toInt() < INFO.toInt() + + + DENY + NEUTRAL + + 256 + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/response/response.json b/src/test/resources/response/response.json index 8b98d17..a5c7088 100644 --- a/src/test/resources/response/response.json +++ b/src/test/resources/response/response.json @@ -1 +1 @@ -[{"name":"AAI-SD-WAN Service for Testing-service-1.0.xml","type":"MODEL_INVENTORY_PROFILE","payload":[80,71,49,118,90,71,86,115,73,72,104,116,98,71,53,122,80,83,74,111,100,72,82,119,79,105,56,118,98,51,74,110,76,109,57,119,90,87,53,108,89,50,57,116,99,67,53,104,89,87,107,117,97,87,53,50,90,87,53,48,98,51,74,53,76,51,89,120,77,67,73,43,67,105,65,103,73,67,65,56,98,87,57,107,90,87,119,116,97,87,53,50,89,88,74,112,89,87,53,48,76,87,108,107,80,106,77,120,90,68,86,105,78,109,69,119,76,84,81,48,78,84,81,116,78,71,85,122,77,121,48,53,89,84,99,53,76,87,82,105,89,84,65,52,77,122,103,52,78,109,77,53,90,68,119,118,98,87,57,107,90,87,119,116,97,87,53,50,89,88,74,112,89,87,53,48,76,87,108,107,80,103,111,103,73,67,65,103,80,71,49,118,90,71,86,115,76,88,82,53,99,71,85,43,99,50,86,121,100,109,108,106,90,84,119,118,98,87,57,107,90,87,119,116,100,72,108,119,90,84,52,75,73,67,65,103,73,68,120,116,98,50,82,108,98,67,49,50,90,88,74,122,80,103,111,103,73,67,65,103,73,67,65,103,73,68,120,116,98,50,82,108,98,67,49,50,90,88,73,43,67,105,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,68,120,116,98,50,82,108,98,67,49,50,90,88,74,122,97,87,57,117,76,87,108,107,80,106,81,50,78,68,65,120,90,87,86,106,76,84,77,49,89,109,81,116,78,71,85,53,78,105,49,104,90,68,66,107,76,84,65,122,78,84,90,109,90,106,90,105,79,71,77,52,90,68,119,118,98,87,57,107,90,87,119,116,100,109,86,121,99,50,108,118,98,105,49,112,90,68,52,75,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,80,71,49,118,90,71,86,115,76,87,53,104,98,87,85,43,85,48,81,116,86,48,70,79,73,70,78,108,99,110,90,112,89,50,85,103,90,109,57,121,73,70,82,108,99,51,82,112,98,109,99,56,76,50,49,118,90,71,86,115,76,87,53,104,98,87,85,43,67,105,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,68,120,116,98,50,82,108,98,67,49,50,90,88,74,122,97,87,57,117,80,106,69,117,77,68,119,118,98,87,57,107,90,87,119,116,100,109,86,121,99,50,108,118,98,106,52,75,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,80,71,49,118,90,71,86,115,76,87,82,108,99,50,78,121,97,88,66,48,97,87,57,117,80,108,78,69,76,86,100,66,84,105,66,84,90,88,74,50,97,87,78,108,73,71,90,118,99,105,66,69,83,70,89,103,86,71,86,122,100,71,108,117,90,121,66,112,98,105,66,70,77,107,85,56,76,50,49,118,90,71,86,115,76,87,82,108,99,50,78,121,97,88,66,48,97,87,57,117,80,103,111,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,56,98,87,57,107,90,87,119,116,90,87,120,108,98,87,86,117,100,72,77,43,67,105,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,56,98,87,57,107,90,87,119,116,90,87,120,108,98,87,86,117,100,68,52,75,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,56,98,109,86,51,76,87,82,104,100,71,69,116,90,71,86,115,76,87,90,115,89,87,99,43,86,68,119,118,98,109,86,51,76,87,82,104,100,71,69,116,90,71,86,115,76,87,90,115,89,87,99,43,67,105,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,80,71,78,104,99,109,82,112,98,109,70,115,97,88,82,53,80,110,86,117,89,109,57,49,98,109,82,108,90,68,119,118,89,50,70,121,90,71,108,117,89,87,120,112,100,72,107,43,67,105,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,80,71,49,118,90,71,86,115,76,87,86,115,90,87,49,108,98,110,82,122,76,122,52,75,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,56,99,109,86,115,89,88,82,112,98,50,53,122,97,71,108,119,76,87,120,112,99,51,81,43,67,105,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,68,120,121,90,87,120,104,100,71,108,118,98,110,78,111,97,88,65,43,67,105,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,56,99,109,86,115,89,88,82,108,90,67,49,48,98,122,53,116,98,50,82,108,98,67,49,50,90,88,73,56,76,51,74,108,98,71,70,48,90,87,81,116,100,71,56,43,67,105,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,56,99,109,86,115,89,88,82,112,98,50,53,122,97,71,108,119,76,87,82,104,100,71,69,43,67,105,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,80,72,74,108,98,71,70,48,97,87,57,117,99,50,104,112,99,67,49,114,90,88,107,43,98,87,57,107,90,87,119,116,100,109,86,121,76,109,49,118,90,71,86,115,76,88,90,108,99,110,78,112,98,50,52,116,97,87,81,56,76,51,74,108,98,71,70,48,97,87,57,117,99,50,104,112,99,67,49,114,90,88,107,43,67,105,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,80,72,74,108,98,71,70,48,97,87,57,117,99,50,104,112,99,67,49,50,89,87,120,49,90,84,52,48,78,109,73,53,77,106,69,48,78,67,48,53,77,106,78,104,76,84,82,107,77,106,65,116,89,106,103,49,89,83,48,122,89,50,74,107,79,68,81,51,78,106,89,52,89,84,107,56,76,51,74,108,98,71,70,48,97,87,57,117,99,50,104,112,99,67,49,50,89,87,120,49,90,84,52,75,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,68,119,118,99,109,86,115,89,88,82,112,98,50,53,122,97,71,108,119,76,87,82,104,100,71,69,43,67,105,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,56,99,109,86,115,89,88,82,112,98,50,53,122,97,71,108,119,76,87,82,104,100,71,69,43,67,105,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,80,72,74,108,98,71,70,48,97,87,57,117,99,50,104,112,99,67,49,114,90,88,107,43,98,87,57,107,90,87,119,117,98,87,57,107,90,87,119,116,97,87,53,50,89,88,74,112,89,87,53,48,76,87,108,107,80,67,57,121,90,87,120,104,100,71,108,118,98,110,78,111,97,88,65,116,97,50,86,53,80,103,111,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,68,120,121,90,87,120,104,100,71,108,118,98,110,78,111,97,88,65,116,100,109,70,115,100,87,85,43,79,68,73,120,79,84,82,104,90,106,69,116,77,50,77,121,89,121,48,48,79,68,86,104,76,84,104,109,78,68,81,116,78,68,73,119,90,84,73,121,89,84,108,108,89,87,69,48,80,67,57,121,90,87,120,104,100,71,108,118,98,110,78,111,97,88,65,116,100,109,70,115,100,87,85,43,67,105,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,56,76,51,74,108,98,71,70,48,97,87,57,117,99,50,104,112,99,67,49,107,89,88,82,104,80,103,111,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,56,76,51,74,108,98,71,70,48,97,87,57,117,99,50,104,112,99,68,52,75,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,56,76,51,74,108,98,71,70,48,97,87,57,117,99,50,104,112,99,67,49,115,97,88,78,48,80,103,111,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,103,80,67,57,116,98,50,82,108,98,67,49,108,98,71,86,116,90,87,53,48,80,103,111,103,73,67,65,103,73,67,65,103,73,67,65,103,73,67,65,56,76,50,49,118,90,71,86,115,76,87,86,115,90,87,49,108,98,110,82,122,80,103,111,103,73,67,65,103,73,67,65,103,73,68,119,118,98,87,57,107,90,87,119,116,100,109,86,121,80,103,111,103,73,67,65,103,80,67,57,116,98,50,82,108,98,67,49,50,90,88,74,122,80,103,111,56,76,50,49,118,90,71,86,115,80,103,61,61]}] \ No newline at end of file +[{"name":"AAI-29NFOD_S-service-1.0.xml","type":"MODEL","payload":"\n 4da8d1e8-f59a-4370-84ea-c6de836821fc\n service\n \n \n a76ed81f-23d2-4e69-8972-eb1c69855e64\n 29NFOD_S\n 1.0\n 29NFOD\n \n \n T\n unbounded\n \n \n T\n unbounded\n \n \n \n model-ver\n \n model-ver.model-version-id\n 3f283439-4e0e-4a6a-9b31-da5d0cb05b52\n \n \n model.model-invariant-id\n bc3622d2-a645-4806-80f2-96b04a866bbf\n \n \n \n \n \n \n \n model-ver\n \n model-ver.model-version-id\n 46b92144-923a-4d20-b85a-3cbd847668a9\n \n \n model.model-invariant-id\n 82194af1-3c2c-485a-8f44-420e22a9eaa4\n \n \n \n \n \n \n \n"},{"name":"AAI-29NFOD-resource-1.0.xml","type":"MODEL","payload":"\n bc3622d2-a645-4806-80f2-96b04a866bbf\n resource\n \n \n 3f283439-4e0e-4a6a-9b31-da5d0cb05b52\n 29NFOD\n 1.0\n 29NFOD\n \n \n T\n unbounded\n \n \n \n model-ver\n \n model-ver.model-version-id\n 93a6166f-b3d5-4f06-b4ba-aed48d009ad9\n \n \n model.model-invariant-id\n acc6edd8-a8d4-4b93-afaa-0994068be14c\n \n \n \n \n \n \n \n"},{"name":"vnfVendorImageConfigurations","type":"VNFCATALOG","payload":"[{\"application\":\"VM00\",\"application-vendor\":\"29NFOD\",\"application-version\":\"3.16.1\"},{\"application\":\"VM00\",\"application-vendor\":\"29NFOD\",\"application-version\":\"3.16.9\"},{\"application\":\"VM01\",\"application-vendor\":\"29NFOD\",\"application-version\":\"3.16.1\"},{\"application\":\"VM01\",\"application-vendor\":\"29NFOD\",\"application-version\":\"3.16.9\"}]"}] \ No newline at end of file diff --git a/src/test/resources/ymlFiles/artifacts.yml b/src/test/resources/ymlFiles/artifacts.yml new file mode 100644 index 0000000..f7bed1e --- /dev/null +++ b/src/test/resources/ymlFiles/artifacts.yml @@ -0,0 +1,54 @@ +# +# 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. +# + +tosca_definitions_version: tosca_simple_yaml_1_0 + +#metadata: +# filename: tosca/artifacts.yml +# version: '1.0' + +imports: +- data.yml + +artifact_types: + tosca.artifacts.Root: + description: This is the default (root) TOSCA Artifact Type definition that all other TOSCA base Artifact Types derive from. + + tosca.artifacts.Deployment.Image: + derived_from: tosca.artifacts.Deployment + description: This artifact type represents a parent type for any "image" which is an opaque packaging of a TOSCA Node's deployment (whether real or virtual) whose contents are typically already installed and pre-configured (i.e., "stateful") and prepared to be run on a known target container. + + tosca.artifacts.Implementation.Bash: + derived_from: tosca.artifacts.Implementation + description: This artifact type represents a Bash script type that contains Bash commands that can be executed on the Unix Bash shell. + + tosca.artifacts.Deployment.Image.VM: + derived_from: tosca.artifacts.Deployment + description: This artifact represents the parent type for all Virtual Machine (VM) image and container formatted deployment artifacts. These images contain a stateful capture of a machine (e.g., server) including operating system and installed software along with any configurations and can be run on another machine using a hypervisor which virtualizes typical server (i.e., hardware) resources. + + tosca.artifacts.Implementation.Python: + derived_from: tosca.artifacts.Implementation + description: This artifact type represents a Python file that contains Python language constructs that can be executed within a Python interpreter. + + tosca.artifacts.Deployment: + derived_from: tosca.artifacts.Root + description: This artifact type represents the parent type for all deployment artifacts in TOSCA. This class of artifacts typically represents a binary packaging of an application or service that is used to install/create or deploy it as part of a node's lifecycle. + + tosca.artifacts.File: + derived_from: tosca.artifacts.Root + description: This artifact type is used when an artifact definition needs to have its associated file simply treated as a file and no special handling/handlers are invoked (i.e., it is not treated as either an implementation or deployment artifact type). + + tosca.artifacts.Implementation: + derived_from: tosca.artifacts.Root + description: This artifact type represents the parent type for all implementation artifacts in TOSCA. These artifacts are used to implement operations of TOSCA interfaces either directly (e.g., scripts) or indirectly (e.g., config. files). diff --git a/src/test/resources/ymlFiles/data.yml b/src/test/resources/ymlFiles/data.yml new file mode 100644 index 0000000..75e109b --- /dev/null +++ b/src/test/resources/ymlFiles/data.yml @@ -0,0 +1,2241 @@ +# +# 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. +# + +tosca_definitions_version: tosca_simple_yaml_1_0 + +#metadata: +# filename: openecomp-heat/data.yml +# version: '1.0' + +data_types: + + tosca.datatypes.Root: + description: The TOSCA root Data Type all other TOSCA base Data Types derive from + + integer: + derived_from: tosca.datatypes.Root + + string: + derived_from: tosca.datatypes.Root + + boolean: + derived_from: tosca.datatypes.Root + + float: + derived_from: tosca.datatypes.Root + + list: + derived_from: tosca.datatypes.Root + + map: + derived_from: tosca.datatypes.Root + + json: + derived_from: tosca.datatypes.Root + + scalar-unit: + derived_from: tosca.datatypes.Root + + scalar-unit.size: + derived_from: scalar-unit + + scalar-unit.time: + derived_from: scalar-unit + + scalar-unit.frequency: + derived_from: scalar-unit + + tosca.datatypes.Credential: + derived_from: tosca.datatypes.Root + properties: + protocol: + type: string + required: false + token_type: + type: string + default: password + token: + type: string + keys: + type: map + required: false + entry_schema: + type: string + user: + type: string + required: false + + tosca.datatypes.TimeInterval: + derived_from: tosca.datatypes.Root + properties: + start_time: + type: timestamp + required: true + end_time: + type: timestamp + required: true + + tosca.datatypes.network.NetworkInfo: + derived_from: tosca.datatypes.Root + properties: + network_name: + type: string + network_id: + type: string + addresses: + type: list + entry_schema: + type: string + + tosca.datatypes.network.PortInfo: + derived_from: tosca.datatypes.Root + properties: + port_name: + type: string + port_id: + type: string + network_id: + type: string + mac_address: + type: string + addresses: + type: list + entry_schema: + type: string + + tosca.datatypes.network.PortDef: + derived_from: integer + constraints: + - in_range: [ 1, 65535 ] + + tosca.datatypes.network.PortSpec: + derived_from: tosca.datatypes.Root + properties: + protocol: + type: string + required: true + default: tcp + constraints: + - valid_values: [ udp, tcp, igmp ] + target: + type: tosca.datatypes.network.PortDef + target_range: + type: range + constraints: + - in_range: [ 1, 65535 ] + source: + type: tosca.datatypes.network.PortDef + source_range: + type: range + constraints: + - in_range: [ 1, 65535 ] + + ###################new Data Types Onboarding Integration########################## + + org.openecomp.datatypes.heat.network.AddressPair: + derived_from: tosca.datatypes.Root + description: MAC/IP address pairs + properties: + mac_address: + type: string + description: MAC address + required: false + status: SUPPORTED + ip_address: + type: string + description: IP address + required: false + status: SUPPORTED + + org.openecomp.datatypes.heat.network.subnet.HostRoute: + derived_from: tosca.datatypes.Root + description: Host route info for the subnet + properties: + destination: + type: string + description: The destination for static route + required: false + status: SUPPORTED + nexthop: + type: string + description: The next hop for the destination + required: false + status: SUPPORTED + + org.openecomp.datatypes.heat.network.AllocationPool: + derived_from: tosca.datatypes.Root + description: The start and end addresses for the allocation pool + properties: + start: + type: string + description: Start address for the allocation pool + required: false + status: SUPPORTED + end: + type: string + description: End address for the allocation pool + required: false + status: SUPPORTED + + org.openecomp.datatypes.heat.network.neutron.Subnet: + derived_from: tosca.datatypes.Root + description: A subnet represents an IP address block that can be used for assigning IP addresses to virtual instances + properties: + tenant_id: + type: string + description: The ID of the tenant who owns the network + required: false + status: SUPPORTED + enable_dhcp: + type: boolean + description: Set to true if DHCP is enabled and false if DHCP is disabled + required: false + default: true + status: SUPPORTED + ipv6_address_mode: + type: string + description: IPv6 address mode + required: false + status: SUPPORTED + constraints: + - valid_values: + - dhcpv6-stateful + - dhcpv6-stateless + - slaac + ipv6_ra_mode: + type: string + description: IPv6 RA (Router Advertisement) mode + required: false + status: SUPPORTED + constraints: + - valid_values: + - dhcpv6-stateful + - dhcpv6-stateless + - slaac + value_specs: + type: map + description: Extra parameters to include in the request + required: false + default: { + } + status: SUPPORTED + entry_schema: + type: string + allocation_pools: + type: list + description: The start and end addresses for the allocation pools + required: false + status: SUPPORTED + entry_schema: + type: org.openecomp.datatypes.heat.network.AllocationPool + subnetpool: + type: string + description: The name or ID of the subnet pool + required: false + status: SUPPORTED + dns_nameservers: + type: list + description: A specified set of DNS name servers to be used + required: false + default: [ + ] + status: SUPPORTED + entry_schema: + type: string + host_routes: + type: list + description: The gateway IP address + required: false + status: SUPPORTED + entry_schema: + type: org.openecomp.datatypes.heat.network.subnet.HostRoute + ip_version: + type: integer + description: The gateway IP address + required: false + default: 4 + status: SUPPORTED + constraints: + - valid_values: + - '4' + - '6' + name: + type: string + description: The name of the subnet + required: false + status: SUPPORTED + prefixlen: + type: integer + description: Prefix length for subnet allocation from subnet pool + required: false + status: SUPPORTED + constraints: + - greater_or_equal: 0 + cidr: + type: string + description: The CIDR + required: false + status: SUPPORTED + gateway_ip: + type: string + description: The gateway IP address + required: false + status: SUPPORTED + + org.openecomp.datatypes.heat.novaServer.network.PortExtraProperties: + derived_from: tosca.datatypes.Root + description: Nova server network expand properties for port + properties: + port_security_enabled: + type: boolean + description: Flag to enable/disable port security on the port + required: false + status: SUPPORTED + mac_address: + type: string + description: MAC address to give to this port + required: false + status: SUPPORTED + admin_state_up: + type: boolean + description: The administrative state of this port + required: false + default: true + status: SUPPORTED + qos_policy: + type: string + description: The name or ID of QoS policy to attach to this port + required: false + status: SUPPORTED + allowed_address_pairs: + type: list + description: Additional MAC/IP address pairs allowed to pass through the port + required: false + status: SUPPORTED + entry_schema: + type: org.openecomp.datatypes.heat.network.AddressPair + binding:vnic_type: + type: string + description: The vnic type to be bound on the neutron port + required: false + status: SUPPORTED + constraints: + - valid_values: + - macvtap + - direct + - normal + value_specs: + type: map + description: Extra parameters to include in the request + required: false + default: { + } + status: SUPPORTED + entry_schema: + type: string + + org.openecomp.datatypes.heat.novaServer.network.AddressInfo: + derived_from: tosca.datatypes.network.NetworkInfo + description: Network addresses with corresponding port id + properties: + port_id: + type: string + description: Port id + required: false + status: SUPPORTED + + org.openecomp.datatypes.heat.neutron.port.FixedIps: + derived_from: tosca.datatypes.Root + description: subnet/ip_address + properties: + subnet: + type: string + description: Subnet in which to allocate the IP address for this port + required: false + status: SUPPORTED + ip_address: + type: string + description: IP address desired in the subnet for this port + required: false + status: SUPPORTED + + org.openecomp.datatypes.heat.FileInfo: + derived_from: tosca.datatypes.Root + description: Heat File Info + properties: + file: + type: string + description: The required URI string (relative or absolute) which can be used to locate the file + required: true + status: SUPPORTED + file_type: + type: string + description: The type of the file + required: true + status: SUPPORTED + constraints: + - valid_values: + - base + - env + - volume + - network + + org.openecomp.datatypes.heat.contrail.network.rule.PortPairs: + derived_from: tosca.datatypes.Root + description: source and destination port pairs + properties: + start_port: + type: string + description: Start port + required: false + status: SUPPORTED + end_port: + type: string + description: End port + required: false + status: SUPPORTED + + org.openecomp.datatypes.heat.contrail.network.rule.Rule: + derived_from: tosca.datatypes.Root + description: policy rule + properties: + src_ports: + type: list + description: Source ports + required: false + status: SUPPORTED + entry_schema: + type: org.openecomp.datatypes.heat.contrail.network.rule.PortPairs + protocol: + type: string + description: Protocol + required: false + status: SUPPORTED + dst_addresses: + type: list + description: Destination addresses + required: false + status: SUPPORTED + entry_schema: + type: org.openecomp.datatypes.heat.contrail.network.rule.VirtualNetwork + apply_service: + type: string + description: Service to apply + required: false + status: SUPPORTED + dst_ports: + type: list + description: Destination ports + required: false + status: SUPPORTED + entry_schema: + type: org.openecomp.datatypes.heat.contrail.network.rule.PortPairs + src_addresses: + type: list + description: Source addresses + required: false + status: SUPPORTED + entry_schema: + type: org.openecomp.datatypes.heat.contrail.network.rule.VirtualNetwork + direction: + type: string + description: Direction + required: false + status: SUPPORTED + + org.openecomp.datatypes.heat.contrail.network.rule.RuleList: + derived_from: tosca.datatypes.Root + description: list of policy rules + properties: + policy_rule: + type: list + description: Contrail network rule + required: false + status: SUPPORTED + entry_schema: + type: org.openecomp.datatypes.heat.contrail.network.rule.Rule + + org.openecomp.datatypes.heat.contrail.network.rule.VirtualNetwork: + derived_from: tosca.datatypes.Root + description: source and destination addresses + properties: + virtual_network: + type: string + description: Virtual network + required: false + status: SUPPORTED + + org.openecomp.datatypes.heat.network.neutron.SecurityRules.Rule: + derived_from: tosca.datatypes.Root + description: Rules Pairs + properties: + remote_group_id: + type: string + description: The remote group ID to be associated with this security group rule + required: false + status: SUPPORTED + protocol: + type: string + description: The protocol that is matched by the security group rule + required: false + status: SUPPORTED + constraints: + - valid_values: + - tcp + - udp + - icmp + ethertype: + type: string + description: Ethertype of the traffic + required: false + default: IPv4 + status: SUPPORTED + constraints: + - valid_values: + - IPv4 + - IPv6 + port_range_max: + type: integer + description: 'The maximum port number in the range that is matched by the + security group rule. ' + required: false + status: SUPPORTED + constraints: + - in_range: + - 0 + - 65535 + remote_ip_prefix: + type: string + description: The remote IP prefix (CIDR) to be associated with this security group rule + required: false + status: SUPPORTED + remote_mode: + type: string + description: Whether to specify a remote group or a remote IP prefix + required: false + default: remote_ip_prefix + status: SUPPORTED + constraints: + - valid_values: + - remote_ip_prefix + - remote_group_id + direction: + type: string + description: The direction in which the security group rule is applied + required: false + default: ingress + status: SUPPORTED + constraints: + - valid_values: + - egress + - ingress + port_range_min: + type: integer + description: The minimum port number in the range that is matched by the security group rule. + required: false + status: SUPPORTED + constraints: + - in_range: + - 0 + - 65535 + + org.openecomp.datatypes.heat.substitution.SubstitutionFiltering: + derived_from: tosca.datatypes.Root + description: Substitution Filter + properties: + substitute_service_template: + type: string + description: Substitute Service Template + required: true + status: SUPPORTED + index_value: + type: integer + description: Index value of the substitution service template runtime instance + required: false + default: 0 + status: SUPPORTED + constraints: + - greater_or_equal: 0 + count: + type: string + description: Count + required: false + default: 1 + status: SUPPORTED + scaling_enabled: + type: boolean + description: Indicates whether service scaling is enabled + required: false + default: true + status: SUPPORTED + mandatory: + type: boolean + description: Mandatory + required: false + default: true + status: SUPPORTED + + org.openecomp.datatypes.heat.contrailV2.virtual.network.rule.RefDataSequence: + derived_from: tosca.datatypes.Root + description: network policy refs data sequence + properties: + network_policy_refs_data_sequence_major: + type: integer + description: Network Policy ref data sequence Major + required: false + status: SUPPORTED + network_policy_refs_data_sequence_minor: + type: integer + description: Network Policy ref data sequence Minor + required: false + status: SUPPORTED + + org.openecomp.datatypes.heat.contrailV2.virtual.network.rule.RefData: + derived_from: tosca.datatypes.Root + description: network policy refs data + properties: + network_policy_refs_data_sequence: + type: org.openecomp.datatypes.heat.contrailV2.virtual.network.rule.RefDataSequence + description: Network Policy ref data sequence + required: false + status: SUPPORTED + + org.openecomp.datatypes.heat.contrailV2.virtual.network.rule.ref.data.IpamSubnet: + derived_from: tosca.datatypes.Root + description: Network Ipam Ref Data Subnet + properties: + network_ipam_refs_data_ipam_subnets_subnet_ip_prefix_len: + type: string + description: Network ipam refs data ipam subnets ip prefix len + required: false + status: SUPPORTED + network_ipam_refs_data_ipam_subnets_subnet_ip_prefix: + type: string + description: Network ipam refs data ipam subnets ip prefix + required: false + status: SUPPORTED + + org.openecomp.datatypes.heat.contrailV2.virtual.network.rule.ref.data.IpamSubnetList: + derived_from: tosca.datatypes.Root + description: Network Ipam Ref Data Subnet List + properties: + network_ipam_refs_data_ipam_subnets_subnet: + type: org.openecomp.datatypes.heat.contrailV2.virtual.network.rule.ref.data.IpamSubnet + description: Network ipam refs data ipam subnets + required: false + status: SUPPORTED + network_ipam_refs_data_ipam_subnets_addr_from_start: + type: string + description: Network ipam refs data ipam subnets addr from start + required: false + status: SUPPORTED + + org.openecomp.datatypes.heat.contrailV2.virtual.network.rule.IpamRefData: + derived_from: tosca.datatypes.Root + description: Network Ipam Ref Data + properties: + network_ipam_refs_data_ipam_subnets: + type: list + description: Network ipam refs data ipam subnets + required: false + status: SUPPORTED + entry_schema: + type: org.openecomp.datatypes.heat.contrailV2.virtual.network.rule.ref.data.IpamSubnetList + + org.openecomp.datatypes.heat.contrailV2.network.rule.SrcVirtualNetwork: + derived_from: tosca.datatypes.Root + description: source addresses + properties: + network_policy_entries_policy_rule_src_addresses_virtual_network: + type: string + description: Source addresses Virtual network + required: false + status: SUPPORTED + + org.openecomp.datatypes.heat.contrailV2.network.rule.DstVirtualNetwork: + derived_from: tosca.datatypes.Root + description: destination addresses + properties: + network_policy_entries_policy_rule_dst_addresses_virtual_network: + type: string + description: Destination addresses Virtual network + required: false + status: SUPPORTED + + org.openecomp.datatypes.heat.contrailV2.network.rule.DstPortPairs: + derived_from: tosca.datatypes.Root + description: destination port pairs + properties: + network_policy_entries_policy_rule_dst_ports_start_port: + type: string + description: Start port + required: false + status: SUPPORTED + network_policy_entries_policy_rule_dst_ports_end_port: + type: string + description: End port + required: false + status: SUPPORTED + + org.openecomp.datatypes.heat.contrailV2.network.rule.SrcPortPairs: + derived_from: tosca.datatypes.Root + description: source port pairs + properties: + network_policy_entries_policy_rule_src_ports_start_port: + type: string + description: Start port + required: false + status: SUPPORTED + network_policy_entries_policy_rule_src_ports_end_port: + type: string + description: End port + required: false + status: SUPPORTED + + org.openecomp.datatypes.heat.contrailV2.network.rule.ActionList: + derived_from: tosca.datatypes.Root + description: Action List + properties: + network_policy_entries_policy_rule_action_list_simple_action: + type: string + description: Simple Action + required: false + status: SUPPORTED + network_policy_entries_policy_rule_action_list_apply_service: + type: list + description: Apply Service + required: false + status: SUPPORTED + entry_schema: + type: string + + org.openecomp.datatypes.heat.contrailV2.network.rule.Rule: + derived_from: tosca.datatypes.Root + description: policy rule + properties: + network_policy_entries_policy_rule_dst_addresses: + type: list + description: Destination addresses + required: false + status: SUPPORTED + entry_schema: + type: org.openecomp.datatypes.heat.contrailV2.network.rule.DstVirtualNetwork + network_policy_entries_policy_rule_dst_ports: + type: list + description: Destination ports + required: false + status: SUPPORTED + entry_schema: + type: org.openecomp.datatypes.heat.contrailV2.network.rule.DstPortPairs + network_policy_entries_policy_rule_protocol: + type: string + description: Protocol + required: false + status: SUPPORTED + network_policy_entries_policy_rule_src_addresses: + type: list + description: Source addresses + required: false + status: SUPPORTED + entry_schema: + type: org.openecomp.datatypes.heat.contrailV2.network.rule.SrcVirtualNetwork + network_policy_entries_policy_rule_direction: + type: string + description: Direction + required: false + status: SUPPORTED + network_policy_entries_policy_rule_src_ports: + type: list + description: Source ports + required: false + status: SUPPORTED + entry_schema: + type: org.openecomp.datatypes.heat.contrailV2.network.rule.SrcPortPairs + network_policy_entries_policy_rule_action_list: + type: org.openecomp.datatypes.heat.contrailV2.network.rule.ActionList + description: Action list + required: false + status: SUPPORTED + + org.openecomp.datatypes.heat.contrailV2.network.rule.RuleList: + derived_from: tosca.datatypes.Root + description: list of policy rules + properties: + network_policy_entries_policy_rule: + type: list + description: Contrail network rule + required: false + status: SUPPORTED + entry_schema: + type: org.openecomp.datatypes.heat.contrailV2.network.rule.Rule + + org.openecomp.datatypes.heat.network.contrail.port.StaticRoute: + derived_from: tosca.datatypes.Root + description: static route + properties: + prefix: + type: string + description: Route prefix + required: false + status: SUPPORTED + next_hop: + type: string + description: Next hop + required: false + status: SUPPORTED + next_hop_type: + type: string + description: Next hop type + required: false + status: SUPPORTED + + org.openecomp.datatypes.heat.network.contrail.AddressPair: + derived_from: tosca.datatypes.Root + description: Address Pair + properties: + address_mode: + type: string + description: Address mode active-active or active-standy + required: false + status: SUPPORTED + constraints: + - valid_values: + - active-active + - active-standby + prefix: + type: string + description: IP address prefix + required: false + status: SUPPORTED + mac_address: + type: string + description: Mac address + required: false + status: SUPPORTED + + org.openecomp.datatypes.heat.network.contrail.InterfaceData: + derived_from: tosca.datatypes.Root + description: Interface Data + properties: + static_routes: + type: list + description: An ordered list of static routes to be added to this interface + required: false + status: SUPPORTED + entry_schema: + type: org.openecomp.datatypes.heat.network.contrail.port.StaticRoute + virtual_network: + type: string + description: Virtual Network for this interface + required: true + status: SUPPORTED + allowed_address_pairs: + type: list + description: List of allowed address pair for this interface + required: false + status: SUPPORTED + entry_schema: + type: org.openecomp.datatypes.heat.network.contrail.AddressPair + ip_address: + type: string + description: IP for this interface + required: false + status: SUPPORTED + + # Duplicate type - removed after investigating with Renana and Shiri Amichai from Amdocs + # org.openecomp.datatypes.heat.contrailV2.virtual.machine.interface.Properties: + # derived_from: tosca.datatypes.Root + # description: Virtual Machine Interface Properties. + # properties: + # virtual_machine_interface_properties_service_interface_type: + # type: string + # description: Service Interface Type. + # required: false + # status: SUPPORTED + + org.openecomp.datatypes.Root: + derived_from: tosca.datatypes.Root + description: > + The ECOMP root Data Type all other Data Types derive from + properties: + supplemental_data: + type: map + entry_schema: + description: > + A placeholder for missing properties that would be included in future ecomp model versions. + fromat : + type: string + + org.openecomp.datatypes.AssignmentRequirements: + derived_from: org.openecomp.datatypes.Root + properties: + is_required: + description: | + "true" indicates that assignment is required + type: boolean + default: false + required: true + count: + description: number of assignments required + type: integer + required: false + + org.openecomp.datatypes.network.SubnetAssignments: + derived_from: org.openecomp.datatypes.Root + properties: + ip_network_address_plan: + type: string + required: false + description: Reference to EIPAM, VLAN or other address plan ID used to assign subnets to this network + dhcp_enabled: + type: boolean + required: false + description: \"true\" indicates the network has 1 or more policies + ip_version: + type: integer + constraints: + - valid_values: [4,6] + required: true + description: The IP version of the subnet + cidr_mask: + type: integer + required: true + description: The default subnet CIDR mask + min_subnets_count: + type: integer + default: 1 + required: true + description: Quantity of subnets that must be initially assigned + + org.openecomp.datatypes.network.IPv4SubnetAssignments: + derived_from: org.openecomp.datatypes.network.SubnetAssignments + properties: + use_ipv4: + type: boolean + required: true + description: Indicates IPv4 subnet assignments + + org.openecomp.datatypes.network.IPv6SubnetAssignments: + derived_from: org.openecomp.datatypes.network.SubnetAssignments + properties: + use_ipv6: + type: boolean + required: true + description: Indicates IPv6 subnet assignments + + org.openecomp.datatypes.network.NetworkAssignments: + derived_from: org.openecomp.datatypes.Root + properties: + ecomp_generated_network_assignment: + type: boolean + required: true + default: false + description: > + \"true\" indicates that the network assignments will be auto-generated by ECOMP + \"false\" indicates operator-supplied Network assignments file upload is required (e.g. VID will present prompt to operator to upload operator-supplied Network assignments file). + is_shared_network: + type: boolean + required: true + description: \"true\" means this network is shared by multiple Openstack tenants + is_external_network: + type: boolean + required: true + default: false + description: > + \"true\" means this Contrail external network + ipv4_subnet_default_assignment: + type: org.openecomp.datatypes.network.IPv4SubnetAssignments + required: true + description: IPv4 defualt subnet assignments + ipv6_subnet_default_assignment: + type: org.openecomp.datatypes.network.IPv6SubnetAssignments + required: true + description: IPv6 defualt subnet assignments + + org.openecomp.datatypes.network.ProviderNetwork: + derived_from: org.openecomp.datatypes.Root + properties: + is_provider_network: + type: boolean + required: true + description: \"true\" indicates that this a Neutron provider type of network + physical_network_name: + type: string + required: false + constraints: + - valid_values: ["Physnet41", "Physnet42", "Physnet43", "Physnet44", "Physnet21", "Physnet22"] + description: > + Identifies the NUMA processor cluster to which this physical network interface belongs. + NUMA instance correlates to the first digit of the Physical Network Name suffix (e.g. \"01\" = NUMA 0, \"11\" = NUMA 1) + numa: + type: string + required: false + constraints: + - valid_values: ["NUMA 0", "NUMA 1"] + description: > + PNIC instance within the NUMA processor cluster + PNIC Instance correlates to the second digit of the Physical Network Name suffix (e.g. "01" = PNIC 1, "02" = "PNIC 2) + pnic_instance: + type: integer + required: false + description: PNIC instance within the NUMA processor cluster + + org.openecomp.datatypes.network.NetworkFlows: + derived_from: org.openecomp.datatypes.Root + properties: + is_network_policy: + type: boolean + required: false + default: false + description: \"true\" indicates the network has 1 or more policies + network_policy: + type: string + required: false + description: "Identifies the specific Cloud network policy that must be applied to this network (source: from Policy Manager)." + is_bound_to_vpn: + type: boolean + required: false + default: false + description: \"true\" indicates the network has 1 or more vpn bindings + vpn_binding: + type: string + required: false + description: "Identifies the specific VPN Binding entry in A&AI that must be applied when creating this network (source: A&AI)" + + org.openecomp.datatypes.network.VlanRequirements: + derived_from: org.openecomp.datatypes.Root + properties: + vlan_range_plan: + type: string + required: true + description: reference to a vlan range plan + vlan_type: + type: string + required: true + constraints: + - valid_values: ["c-tag", "s-tag"] + description: identifies the vlan type (e.g., c-tag) + vlan_count: + type: integer + required: true + description: identifies the number of vlan tags to assign to the CP from the plan + + org.openecomp.datatypes.network.IpRequirements: + derived_from: org.openecomp.datatypes.Root + properties: + ip_version: + type: integer + required: true + constraints: + - valid_values: + - 4 + - 6 + ip_count: + description: identifies the number of ip address to assign to the CP from the plan + type: integer + required: false + floating_ip_count: + type: integer + required: false + subnet_role: + type: string + required: false + assingment_method: + type: string + required: true + constraints: + - valid_values: + - fixed + - dhcp + dhcp_enabled: + type: boolean + required: false + ip_count_required: + description: identifies the number of ip address to assign to the CP from the plan + type: org.openecomp.datatypes.AssignmentRequirements + required: false + floating_ip_count_required: + type: org.openecomp.datatypes.AssignmentRequirements + required: false + + org.openecomp.datatypes.network.MacAssignments: + derived_from: org.openecomp.datatypes.Root + properties: + mac_range_plan: + type: string + required: true + description: reference to a MAC address range plan + mac_count: + type: integer + required: true + description: identifies the number of MAC addresses to assign to the CP from the plan + + org.openecomp.datatypes.EcompHoming: + derived_from: org.openecomp.datatypes.Root + properties: + ecomp_selected_instance_node_target: + type: boolean + required: true + default: false + description: > + \"true\" indicates that the target deployment node for this instance will be auto-selected by ECOMP + \"false\" indicates operator-supplied instance target deployment node required (e.g. VID will present a prompt to operator and collect the + operator-selected target node for the deployment of this Network instance). + homing_policy: + type: string + required: false + description: Referenc to a service level homing policy that ECOMP will use for instance deployment target node + instance_node_target: + type: string + required: false + description: Instance target deployment node + + org.openecomp.datatypes.EcompNaming: + derived_from: org.openecomp.datatypes.Root + properties: + ecomp_generated_naming: + type: boolean + required: true + default: true + description: > + \"true\" indicates that the name for the instance will be auto-generated by ECOMP. + \"false\" indicates operator-supplied name required (e.g. VID will present prompt to operator and collect the operator-supplied instance name). + naming_policy: + type: string + required: false + description: Referenc to naming policy that ECOMP will use when the name is auto-generated + + org.openecomp.datatypes.network.MacRequirements: + derived_from: org.openecomp.datatypes.Root + properties: + mac_range_plan: + description: reference to a MAC address range plan + type: string + required: false + mac_count: + description: identifies the number of MAC addresses to assign to the CP from the plan + type: integer + required: false + mac_count_required: + description: identifies the number of MAC addresses to assign to the CP from the plan + type: org.openecomp.datatypes.AssignmentRequirements + + org.openecomp.datatypes.heat.contrailV2.virtual.machine.subInterface.AddressPairIp: + derived_from: tosca.datatypes.Root + description: Virtual Machine Sub Interface Address Pair IP. + properties: + ip_prefix: + type: string + description: IP Prefix. + required: false + status: SUPPORTED + ip_prefix_len: + type: integer + description: IP Prefix Len. + required: false + status: SUPPORTED + + org.openecomp.datatypes.heat.contrailV2.virtual.machine.subInterface.MacAddress: + derived_from: tosca.datatypes.Root + description: Virtual Machine Sub Interface Mac Address. + properties: + mac_address: + type: list + description: Mac Addresses List. + required: false + status: SUPPORTED + entry_schema: + type: string + + org.openecomp.datatypes.heat.contrailV2.virtual.machine.subInterface.Properties: + derived_from: tosca.datatypes.Root + description: Virtual Machine Sub Interface VLAN Properties. + properties: + sub_interface_vlan_tag: + type: string + description: Sub Interface VLAN Tag. + required: false + status: SUPPORTED + + org.openecomp.datatypes.heat.contrailV2.virtual.machine.subInterface.AddressPair: + derived_from: tosca.datatypes.Root + description: Virtual Machine Sub Interface Address Pair. + properties: + address_mode: + type: string + description: Address Mode. + required: false + status: SUPPORTED + ip: + type: org.openecomp.datatypes.heat.contrailV2.virtual.machine.subInterface.AddressPairIp + description: IP. + required: false + status: SUPPORTED + mac: + type: string + description: Mac. + required: false + status: SUPPORTED + + org.openecomp.datatypes.heat.contrailV2.virtual.machine.subInterface.AddressPairs: + derived_from: tosca.datatypes.Root + description: Virtual Machine Sub Interface Address Pairs. + properties: + allowed_address_pair: + type: list + description: Addresses pair List. + required: false + status: SUPPORTED + entry_schema: + type: org.openecomp.datatypes.heat.contrailV2.virtual.machine.subInterface.AddressPair + + org.openecomp.datatypes.Naming: + derived_from: org.openecomp.datatypes.Root + properties: + ecomp_generated_naming: + description: | + "true" indicates that the name for the instance will be auto-generated by ECOMP. "false" indicates operator-supplied name required (e.g. VID will present prompt to operator and collect the operator-supplied instance name). + type: boolean + default: true + required: true + naming_policy: + description: Reference to naming policy that ECOMP will use when the name is auto-generated + type: string + required: false + instance_name: + description: indicates operator-supplied name required (e.g. VID will present prompt to operator and collect the operator-supplied instance name). + type: string + required: false + port_id: + description: The unique ID for the network port generated by the network provider. + type: string + required: false + network_id: + description: The unique ID for the network. + type: string + required: false + mac_address: + description: The unique media access control address (MAC address) assigned to the port. + type: string + required: false + addresses: + description: The list of IP address(es) assigned to the port. + type: list + entry_schema: + type: string + required: false + + tosca.datatypes.Credential: + derived_from: tosca.datatypes.Root + description: The Credential type is a complex TOSCA data Type used when describing authorization credentials used to access network accessible resources. + properties: + protocol: + description: The optional protocol name. + type: string + required: false + token_type: + description: The required token type. + type: string + default: password + token: + description: The required token used as a credential for authorization or access to a networked resource. + type: string + keys: + description: The optional list of protocol-specific keys or assertions. + type: map + entry_schema: + type: string + required: false + user: + description: The optional user (name or ID) used for non-token based credentials. + type: string + required: false + + org.openecomp.datatypes.heat.network.AddressPair: + derived_from: tosca.datatypes.Root + description: MAC/IP address pairs + properties: + mac_address: + description: MAC address + type: string + status: supported + required: false + ip_address: + description: IP address + type: string + status: supported + required: false + + org.openecomp.datatypes.heat.novaServer.network.PortExtraProperties: + derived_from: tosca.datatypes.Root + description: Nova server network expand properties for port + properties: + port_security_enabled: + description: Flag to enable/disable port security on the port + type: boolean + status: supported + required: false + mac_address: + description: MAC address to give to this port + type: string + status: supported + required: false + admin_state_up: + description: The administrative state of this port + type: boolean + status: supported + default: true + required: false + qos_policy: + description: The name or ID of QoS policy to attach to this port + type: string + status: supported + required: false + allowed_address_pairs: + description: Additional MAC/IP address pairs allowed to pass through the port + type: list + status: supported + entry_schema: + type: org.openecomp.datatypes.heat.network.AddressPair + required: false + binding:vnic_type: + description: The vnic type to be bound on the neutron port + type: string + status: supported + required: false + constraints: + - valid_values: + - macvtap + - direct + - normal + value_specs: + description: Extra parameters to include in the request + type: map + status: supported + entry_schema: + type: string + default: {} + required: false + + org.openecomp.datatypes.heat.contrailV2.network.rule.SrcPortPairs: + derived_from: tosca.datatypes.Root + description: source port pairs + properties: + network_policy_entries_policy_rule_src_ports_start_port: + description: Start port + type: string + status: supported + required: false + network_policy_entries_policy_rule_src_ports_end_port: + description: End port + type: string + status: supported + required: false + + org.openecomp.datatypes.heat.contrailV2.virtual.machine.subInterface.AddressPair: + derived_from: tosca.datatypes.Root + description: Virtual Machine Sub Interface Address Pair. + properties: + address_mode: + description: Address Mode. + type: string + status: supported + required: false + ip: + description: IP. + type: org.openecomp.datatypes.heat.contrailV2.virtual.machine.subInterface.AddressPairIp + status: supported + required: false + mac: + description: Mac. + type: string + status: supported + required: false + + org.openecomp.datatypes.heat.contrailV2.network.rule.DstVirtualNetwork: + derived_from: tosca.datatypes.Root + description: destination addresses + properties: + network_policy_entries_policy_rule_dst_addresses_virtual_network: + description: Destination addresses Virtual network + type: string + status: supported + required: false + + org.openecomp.datatypes.heat.contrailV2.network.rule.Rule: + derived_from: tosca.datatypes.Root + description: policy rule + properties: + network_policy_entries_policy_rule_dst_addresses: + description: Destination addresses + type: list + status: supported + entry_schema: + type: org.openecomp.datatypes.heat.contrailV2.network.rule.DstVirtualNetwork + required: false + network_policy_entries_policy_rule_dst_ports: + description: Destination ports + type: list + status: supported + entry_schema: + type: org.openecomp.datatypes.heat.contrailV2.network.rule.DstPortPairs + required: false + network_policy_entries_policy_rule_protocol: + description: Protocol + type: string + status: supported + required: false + network_policy_entries_policy_rule_src_addresses: + description: Source addresses + type: list + status: supported + entry_schema: + type: org.openecomp.datatypes.heat.contrailV2.network.rule.SrcVirtualNetwork + required: false + network_policy_entries_policy_rule_direction: + description: Direction + type: string + status: supported + required: false + network_policy_entries_policy_rule_src_ports: + description: Source ports + type: list + status: supported + entry_schema: + type: org.openecomp.datatypes.heat.contrailV2.network.rule.SrcPortPairs + required: false + network_policy_entries_policy_rule_action_list: + description: Action list + type: org.openecomp.datatypes.heat.contrailV2.network.rule.ActionList + status: supported + required: false + + org.openecomp.datatypes.heat.network.neutron.SecurityRules.Rule: + derived_from: tosca.datatypes.Root + description: Rules Pairs + properties: + remote_group_id: + description: The remote group ID to be associated with this security group rule + type: string + status: supported + required: false + protocol: + description: The protocol that is matched by the security group rule + type: string + status: supported + required: false + constraints: + - valid_values: + - tcp + - udp + - icmp + ethertype: + description: Ethertype of the traffic + type: string + status: supported + default: IPv4 + required: false + constraints: + - valid_values: + - IPv4 + - IPv6 + port_range_max: + description: 'The maximum port number in the range that is matched by the security group rule. ' + type: integer + status: supported + required: false + constraints: + - in_range: + - 0 + - 65535 + remote_ip_prefix: + description: The remote IP prefix (CIDR) to be associated with this security group rule + type: string + status: supported + required: false + remote_mode: + description: Whether to specify a remote group or a remote IP prefix + type: string + status: supported + default: remote_ip_prefix + required: false + constraints: + - valid_values: + - remote_ip_prefix + - remote_group_id + direction: + description: The direction in which the security group rule is applied + type: string + status: supported + default: ingress + required: false + constraints: + - valid_values: + - egress + - ingress + port_range_min: + description: The minimum port number in the range that is matched by the security group rule. + type: integer + status: supported + required: false + constraints: + - in_range: + - 0 + - 65535 + + org.openecomp.datatypes.heat.contrailV2.virtual.network.rule.ref.data.IpamSubnet: + derived_from: tosca.datatypes.Root + description: Network Ipam Ref Data Subnet + properties: + network_ipam_refs_data_ipam_subnets_subnet_ip_prefix_len: + description: Network ipam refs data ipam subnets ip prefix len + type: string + status: supported + required: false + network_ipam_refs_data_ipam_subnets_subnet_ip_prefix: + description: Network ipam refs data ipam subnets ip prefix + type: string + status: supported + required: false + + org.openecomp.datatypes.heat.contrailV2.virtual.machine.subInterface.Properties: + derived_from: tosca.datatypes.Root + description: Virtual Machine Sub Interface VLAN Properties. + properties: + sub_interface_vlan_tag: + description: Sub Interface VLAN Tag. + type: string + status: supported + required: false + + org.openecomp.datatypes.heat.contrailV2.virtual.machine.subInterface.AddressPairIp: + derived_from: tosca.datatypes.Root + description: Virtual Machine Sub Interface Address Pair IP. + properties: + ip_prefix: + description: IP Prefix. + type: string + status: supported + required: false + ip_prefix_len: + description: IP Prefix Len. + type: integer + status: supported + required: false + + org.openecomp.datatypes.heat.contrailV2.network.rule.SrcVirtualNetwork: + derived_from: tosca.datatypes.Root + description: source addresses + properties: + network_policy_entries_policy_rule_src_addresses_virtual_network: + description: Source addresses Virtual network + type: string + status: supported + required: false + + org.openecomp.datatypes.heat.contrailV2.network.rule.ActionList: + derived_from: tosca.datatypes.Root + description: Action List + properties: + network_policy_entries_policy_rule_action_list_simple_action: + description: Simple Action + type: string + status: supported + required: false + network_policy_entries_policy_rule_action_list_apply_service: + description: Apply Service + type: list + status: supported + entry_schema: + type: string + required: false + + org.openecomp.datatypes.heat.contrailV2.virtual.network.rule.IpamRefData: + derived_from: tosca.datatypes.Root + description: Network Ipam Ref Data + properties: + network_ipam_refs_data_ipam_subnets: + description: Network ipam refs data ipam subnets + type: list + status: supported + entry_schema: + type: org.openecomp.datatypes.heat.contrailV2.virtual.network.rule.ref.data.IpamSubnetList + required: false + + org.openecomp.datatypes.heat.FileInfo: + derived_from: tosca.datatypes.Root + description: Heat File Info + properties: + file: + description: The required URI string (relative or absolute) which can be used to locate the file + type: string + status: supported + required: true + file_type: + description: The type of the file + type: string + status: supported + required: true + constraints: + - valid_values: + - base + - env + - volume + - network + + org.openecomp.datatypes.heat.network.contrail.InterfaceData: + derived_from: tosca.datatypes.Root + description: Interface Data + properties: + static_routes: + description: An ordered list of static routes to be added to this interface + type: list + status: supported + entry_schema: + type: org.openecomp.datatypes.heat.network.contrail.port.StaticRoute + required: false + virtual_network: + description: Virtual Network for this interface + type: string + status: supported + required: true + allowed_address_pairs: + description: List of allowed address pair for this interface + type: list + status: supported + entry_schema: + type: org.openecomp.datatypes.heat.network.contrail.AddressPair + required: false + ip_address: + description: IP for this interface + type: string + status: supported + required: false + + org.openecomp.datatypes.heat.contrailV2.virtual.network.rule.RefData: + derived_from: tosca.datatypes.Root + description: network policy refs data + properties: + network_policy_refs_data_sequence: + description: Network Policy ref data sequence + type: org.openecomp.datatypes.heat.contrailV2.virtual.network.rule.RefDataSequence + status: supported + required: false + + org.openecomp.datatypes.heat.contrailV2.network.rule.DstPortPairs: + derived_from: tosca.datatypes.Root + description: destination port pairs + properties: + network_policy_entries_policy_rule_dst_ports_start_port: + description: Start port + type: string + status: supported + required: false + network_policy_entries_policy_rule_dst_ports_end_port: + description: End port + type: string + status: supported + required: false + + org.openecomp.datatypes.heat.network.AllocationPool: + derived_from: tosca.datatypes.Root + description: The start and end addresses for the allocation pool + properties: + start: + description: Start address for the allocation pool + type: string + status: supported + required: false + end: + description: End address for the allocation pool + type: string + status: supported + required: false + + org.openecomp.datatypes.heat.contrail.network.rule.PortPairs: + derived_from: tosca.datatypes.Root + description: source and destination port pairs + properties: + start_port: + description: Start port + type: string + status: supported + required: false + end_port: + description: End port + type: string + status: supported + required: false + + org.openecomp.datatypes.heat.contrail.network.rule.VirtualNetwork: + derived_from: tosca.datatypes.Root + description: source and destination addresses + properties: + virtual_network: + description: Virtual network + type: string + status: supported + required: false + + org.openecomp.datatypes.heat.contrail.network.rule.RuleList: + derived_from: tosca.datatypes.Root + description: list of policy rules + properties: + policy_rule: + description: Contrail network rule + type: list + status: supported + entry_schema: + type: org.openecomp.datatypes.heat.contrail.network.rule.Rule + required: false + + org.openecomp.datatypes.heat.network.contrail.AddressPair: + derived_from: tosca.datatypes.Root + description: Address Pair + properties: + address_mode: + description: Address mode active-active or active-standy + type: string + status: supported + required: false + constraints: + - valid_values: + - active-active + - active-standby + prefix: + description: IP address prefix + type: string + status: supported + required: false + mac_address: + description: Mac address + type: string + status: supported + required: false + + org.openecomp.datatypes.heat.contrailV2.virtual.machine.subInterface.MacAddress: + derived_from: tosca.datatypes.Root + description: Virtual Machine Sub Interface Mac Address. + properties: + mac_address: + description: Mac Addresses List. + type: list + status: supported + entry_schema: + type: string + required: false + + org.openecomp.datatypes.heat.contrailV2.virtual.network.rule.ref.data.IpamSubnetList: + derived_from: tosca.datatypes.Root + description: Network Ipam Ref Data Subnet List + properties: + network_ipam_refs_data_ipam_subnets_subnet: + description: Network ipam refs data ipam subnets + type: org.openecomp.datatypes.heat.contrailV2.virtual.network.rule.ref.data.IpamSubnet + status: supported + required: false + network_ipam_refs_data_ipam_subnets_addr_from_start: + description: Network ipam refs data ipam subnets addr from start + type: string + status: supported + required: false + + org.openecomp.datatypes.heat.contrailV2.network.rule.RuleList: + derived_from: tosca.datatypes.Root + description: list of policy rules + properties: + network_policy_entries_policy_rule: + description: Contrail network rule + type: list + status: supported + entry_schema: + type: org.openecomp.datatypes.heat.contrailV2.network.rule.Rule + required: false + + org.openecomp.datatypes.heat.novaServer.network.AddressInfo: + derived_from: tosca.datatypes.network.NetworkInfo + description: Network addresses with corresponding port id + properties: + port_id: + description: Port id + type: string + status: supported + required: false + + org.openecomp.datatypes.heat.contrailV2.virtual.machine.subInterface.AddressPairs: + derived_from: tosca.datatypes.Root + description: Virtual Machine Sub Interface Address Pairs. + properties: + allowed_address_pair: + description: Addresses pair List. + type: list + status: supported + entry_schema: + type: org.openecomp.datatypes.heat.contrailV2.virtual.machine.subInterface.AddressPair + required: false + + org.openecomp.datatypes.heat.neutron.port.FixedIps: + derived_from: tosca.datatypes.Root + description: subnet/ip_address + properties: + subnet: + description: Subnet in which to allocate the IP address for this port + type: string + status: supported + required: false + ip_address: + description: IP address desired in the subnet for this port + type: string + status: supported + required: false + + org.openecomp.datatypes.heat.contrailV2.virtual.machine.interface.Properties: + derived_from: tosca.datatypes.Root + description: Virtual Machine Interface Properties. + properties: + service_interface_type: + description: Service Interface Type. + type: string + status: SUPPORTED + required: false + + org.openecomp.datatypes.heat.network.subnet.HostRoute: + derived_from: tosca.datatypes.Root + description: Host route info for the subnet + properties: + destination: + description: The destination for static route + type: string + status: supported + required: false + nexthop: + description: The next hop for the destination + type: string + status: supported + required: false + + org.openecomp.datatypes.heat.network.contrail.port.StaticRoute: + derived_from: tosca.datatypes.Root + description: static route + properties: + prefix: + description: Route prefix + type: string + status: supported + required: false + next_hop: + description: Next hop + type: string + status: supported + required: false + next_hop_type: + description: Next hop type + type: string + status: supported + required: false + + org.openecomp.datatypes.substitution.SubstitutionFiltering: + derived_from: tosca.datatypes.Root + description: Substitution Filter + properties: + substitute_service_template: + description: Substitute Service Template + type: string + status: supported + required: true + index_value: + description: Index value of the substitution service template runtime instance + type: integer + status: supported + default: 0 + required: false + constraints: + - greater_or_equal: 0 + count: + description: Count + type: float + status: supported + default: 1 + required: false + scaling_enabled: + description: Indicates whether service scaling is enabled + type: boolean + status: supported + default: true + required: false + mandatory: + description: Mandatory + type: boolean + status: supported + default: true + required: false + + org.openecomp.datatypes.heat.network.neutron.Subnet: + derived_from: tosca.datatypes.Root + description: A subnet represents an IP address block that can be used for assigning IP addresses to virtual instances + properties: + tenant_id: + description: The ID of the tenant who owns the network + type: string + status: supported + required: false + enable_dhcp: + description: Set to true if DHCP is enabled and false if DHCP is disabled + type: boolean + status: supported + default: true + required: false + ipv6_address_mode: + description: IPv6 address mode + type: string + status: supported + required: false + constraints: + - valid_values: + - dhcpv6-stateful + - dhcpv6-stateless + - slaac + ipv6_ra_mode: + description: IPv6 RA (Router Advertisement) mode + type: string + status: supported + required: false + constraints: + - valid_values: + - dhcpv6-stateful + - dhcpv6-stateless + - slaac + value_specs: + description: Extra parameters to include in the request + type: map + status: supported + entry_schema: + type: string + default: {} + required: false + allocation_pools: + description: The start and end addresses for the allocation pools + type: list + status: supported + entry_schema: + type: org.openecomp.datatypes.heat.network.AllocationPool + required: false + subnetpool: + description: The name or ID of the subnet pool + type: string + status: supported + required: false + dns_nameservers: + description: A specified set of DNS name servers to be used + type: list + status: supported + entry_schema: + type: string + default: [] + required: false + host_routes: + description: The gateway IP address + type: list + status: supported + entry_schema: + type: org.openecomp.datatypes.heat.network.subnet.HostRoute + required: false + ip_version: + description: The gateway IP address + type: integer + status: supported + default: 4 + required: false + constraints: + - valid_values: + - 4 + - 6 + name: + description: The name of the subnet + type: string + status: supported + required: false + prefixlen: + description: Prefix length for subnet allocation from subnet pool + type: integer + status: supported + required: false + constraints: + - greater_or_equal: 0 + cidr: + description: The CIDR + type: string + status: supported + required: false + gateway_ip: + description: The gateway IP address + type: string + status: supported + required: false + + org.openecomp.datatypes.heat.contrail.network.rule.Rule: + derived_from: tosca.datatypes.Root + description: policy rule + properties: + src_ports: + description: Source ports + type: list + status: supported + entry_schema: + type: org.openecomp.datatypes.heat.contrail.network.rule.PortPairs + required: false + protocol: + description: Protocol + type: string + status: supported + required: false + dst_addresses: + description: Destination addresses + type: list + status: supported + entry_schema: + type: org.openecomp.datatypes.heat.contrail.network.rule.VirtualNetwork + required: false + apply_service: + description: Service to apply + type: string + status: supported + required: false + dst_ports: + description: Destination ports + type: list + status: supported + entry_schema: + type: org.openecomp.datatypes.heat.contrail.network.rule.PortPairs + required: false + src_addresses: + description: Source addresses + type: list + status: supported + entry_schema: + type: org.openecomp.datatypes.heat.contrail.network.rule.VirtualNetwork + required: false + direction: + description: Direction + type: string + status: supported + required: false + + org.openecomp.datatypes.heat.contrailV2.virtual.network.rule.RefDataSequence: + derived_from: tosca.datatypes.Root + description: network policy refs data sequence + properties: + network_policy_refs_data_sequence_major: + description: Network Policy ref data sequence Major + type: integer + status: supported + required: false + network_policy_refs_data_sequence_minor: + description: Network Policy ref data sequence Minor + type: integer + status: supported + required: false + + org.openecomp.datatypes.Naming: + derived_from: tosca.datatypes.Root + description: Naming + properties: + ecomp_generated_naming: + description: | + "true" indicates that the name for the instance will be auto-generated by ECOMP. "false" indicates operator-supplied name required (e.g. VID will present prompt to operator and collect the operator-supplied instance name). + type: boolean + default: true + required: false + status: supported + naming_policy: + description: Reference to naming policy that ECOMP will use when the name is auto-generated + type: string + required: false + status: supported + instance_name: + description: Reference to naming policy that ECOMP will use when the name is auto-generated + type: string + required: false + status: supported + + org.openecomp.datatypes.EcompGeneratedNaming: + derived_from: org.openecomp.datatypes.Naming + description: Naming + properties: + naming_policy: + description: Referenc to naming policy that ECOMP will use when the name is auto-generated + type: string + required: false + + org.openecomp.datatypes.UserDefinedNaming: + derived_from: org.openecomp.datatypes.Naming + description: Naming + properties: + instance_name: + description: Reference to naming policy that ECOMP will use when the name is auto-generated + type: string + required: false + + org.openecomp.datatypes.Root: + derived_from: tosca.datatypes.Root + description: > + The AT&T root Data Type all other Data Types derive from + properties: + supplemental_data: + type: map + entry_schema: + description: > + A placeholder for missing properties that would be included in future ecomp model versions. + fromat : + type: string + + org.openecomp.datatypes.EcompHoming: + derived_from: org.openecomp.datatypes.Root + properties: + ecomp_selected_instance_node_target: + type: boolean + required: true + default: false + description: > + "true" indicates that the target deployment node for this instance will be auto-selected by ECOMP + "false" indicates operator-supplied instance target deployment node required (e.g. VID will present a prompt to operator and collect the + operator-selected target node for the deployment of this Network instance). + homing_policy: + type: string + required: false + description: Referenc to a service level homing policy that ECOMP will use for instance deployment target node + instance_node_target: + type: string + required: false + description: Instance target deployment node + + org.openecomp.datatypes.EcompNaming: + derived_from: org.openecomp.datatypes.Root + properties: + ecomp_generated_naming: + type: boolean + required: true + default: true + description: > + "true" indicates that the name for the instance will be auto-generated by ECOMP. + "false" indicates operator-supplied name required (e.g. VID will present prompt to operator and collect the operator-supplied instance name). + naming_policy: + type: string + required: false + description: Referenc to naming policy that ECOMP will use when the name is auto-generated + org.openecomp.datatypes.network.NetworkAssignments: + derived_from: org.openecomp.datatypes.Root + properties: + ecomp_generated_network_assignment: + type: boolean + required: true + default: false + description: > + "true" indicates that the network assignments will be auto-generated by ECOMP + "false" indicates operator-supplied Network assignments file upload is required (e.g. VID will present prompt to operator to upload operator-supplied Network assignments file). + network_assignments_file: + type: string + required: false + description: Filename of the template that specifies all of the configurable name/value pairs of Network assignments in this Network model + multi_tenant: + type: boolean + required: true + default: true + description: true means this network is shared by multiple Openstack tenants + min_subnets_count: + type: integer + required: true + description: Quantity of subnets that must be initially assigned + ip_network_address_plan: + type: string + required: true + description: Reference to EIPAM, VLAN or other address plan ID used to assign subnets to this network + vlan_network_address_plan: + type: string + required: true + description: Reference to VLAN or other address plan ID used to assign subnets to this network + org.openecomp.datatypes.network.PhysicalNetwork: + derived_from: org.openecomp.datatypes.Root + properties: + provider_network: + type: boolean + required: true + description: true indicates that this a Neutron provider type of network + physical_network_name: + type: string + required: false + constraint: + - valid_values: + - Physnet-SRIOV-1 + - Physnet-SRIOV-2 + - Physnet-SRIOV-11 + - Physnet-SRIOV-12 + description: > + Identifies the NUMA processor cluster to which this physical network interface belongs. + NUMA instance correlates to the first digit of the Physical Network Name suffix (e.g. "01" = NUMA 0, "11" = NUMA 1) + numa: + type: string + required: false + constraint: + - valid_values: + - NUMA 0 + - NUMA 1 + description: > + PNIC instance within the NUMA processor cluster + PNIC Instance correlates to the second digit of the Physical Network Name suffix (e.g. "01" = PNIC 1, "02" = "PNIC 2) + pnic_instance: + type: integer + required: false + description: PNIC instance within the NUMA processor cluster + + org.openecomp.datatypes.network.NetworkFlows: + derived_from: org.openecomp.datatypes.Root + properties: + is_network_policy: + type: boolean + required: false + default: false + description: true indicates the network has 1 or more policies + network_policy: + type: string + required: flase + description: Identifies the specific Cloud network policy that must be applied to this network (source - from Policy Manager) + vpn_binding: + type: string + required: false + description: Identifies the specific VPN Binding entry in A&AI that must be applied when creating this network (source - A&AI) + + org.openecomp.datatypes.Artifact: + derived_from: org.openecomp.datatypes.Root + properties: + artifact_name: + type: string + required: true + description: Artifcat name + artifact_type: + type: string + required: true + description: Artifcat type + artifact_uuid: + type: string + required: true + description: Artifcat UUID + artifact_checksum: + type: string + required: true + description: Artifact checksum + artifact_url: + type: string + required: true + description: Artifcay URL. Can also include only the file name \ No newline at end of file -- 2.16.6