Plugin to Generate Service ETSI NSD CSAR 88/111788/3
authorandre.schmid <andre.schmid@est.tech>
Fri, 21 Aug 2020 10:40:56 +0000 (11:40 +0100)
committerSébastien Determe <sebastien.determe@intl.att.com>
Mon, 7 Sep 2020 08:06:05 +0000 (08:06 +0000)
Create a catalog backend plugin to generate an ETSI compliant Network
Service Descriptor (NSD) CSAR as an artifact in the generated TOSCA
CSAR package of a Service.

Change-Id: I2b1556148be39d7bf37602335e638d0cee2b291b
Issue-ID: SDC-3251
Signed-off-by: andre.schmid <andre.schmid@est.tech>
29 files changed:
catalog-be-plugins/etsi-nfv-nsd-csar-plugin/pom.xml [new file with mode: 0644]
catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/builder/NsdCsarManifestBuilder.java [new file with mode: 0644]
catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/builder/NsdToscaMetadataBuilder.java [new file with mode: 0644]
catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/exception/NsdException.java [new file with mode: 0644]
catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/exception/VnfDescriptorException.java [new file with mode: 0644]
catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/EtsiNfvNsCsarEntryGenerator.java [new file with mode: 0644]
catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/EtsiNfvNsdCsarGenerator.java [new file with mode: 0644]
catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/EtsiNfvNsdCsarGeneratorImpl.java [new file with mode: 0644]
catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/NsDescriptorGenerator.java [new file with mode: 0644]
catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/NsDescriptorGeneratorImpl.java [new file with mode: 0644]
catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/VnfDescriptorGenerator.java [new file with mode: 0644]
catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/VnfDescriptorGeneratorImpl.java [new file with mode: 0644]
catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/model/Nsd.java [new file with mode: 0644]
catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/model/VnfDescriptor.java [new file with mode: 0644]
catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/tosca/yaml/NsdTemplateRepresenter.java [new file with mode: 0644]
catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/tosca/yaml/ToscaTemplateYamlGenerator.java [new file with mode: 0644]
catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/tosca/yaml/UnsortedPropertyUtils.java [new file with mode: 0644]
catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/resources/etsi-nfv-types/etsi_nfv_sol001_common_types.yaml [new file with mode: 0644]
catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/resources/etsi-nfv-types/etsi_nfv_sol001_nsd_2_7_1_types.yaml [new file with mode: 0644]
catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/test/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/EtsiNfvNsCsarEntryGeneratorTest.java [new file with mode: 0644]
catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/test/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/EtsiNfvNsdCsarGeneratorImplTest.java [new file with mode: 0644]
catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/test/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/NsDescriptorGeneratorImplTest.java [new file with mode: 0644]
catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/test/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/VnfDescriptorGeneratorImplTest.java [new file with mode: 0644]
catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/test/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/tosca/yaml/ToscaTemplateYamlGeneratorTest.java [new file with mode: 0644]
catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/test/resources/vnf-onboarded-csar/VnfPackage_AMF_v2.csar [new file with mode: 0644]
catalog-be-plugins/pom.xml [new file with mode: 0644]
common-app-api/src/main/java/org/openecomp/sdc/common/api/ArtifactTypeEnum.java
openecomp-be/lib/openecomp-core-lib/openecomp-utilities-lib/src/main/java/org/openecomp/core/utilities/file/FileContentHandler.java
pom.xml

diff --git a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/pom.xml b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/pom.xml
new file mode 100644 (file)
index 0000000..5be6473
--- /dev/null
@@ -0,0 +1,137 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ ============LICENSE_START=======================================================
+  ~  Copyright (C) 2020 Nordix Foundation
+  ~  ================================================================================
+  ~  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.
+  ~
+  ~  SPDX-License-Identifier: Apache-2.0
+  ~  ============LICENSE_END=========================================================
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <parent>
+    <artifactId>catalog-be-plugins</artifactId>
+    <groupId>org.openecomp.sdc</groupId>
+    <version>1.7.0-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+
+  <artifactId>etsi-nfv-nsd-csar-plugin</artifactId>
+
+  <properties>
+    <sdc.version>1.7.0-SNAPSHOT</sdc.version>
+    <hamcrest.version>2.2</hamcrest.version>
+  </properties>
+
+  <dependencies>
+    <!--catalog-be dependencies-->
+    <dependency>
+      <groupId>org.openecomp.sdc</groupId>
+      <artifactId>catalog-be</artifactId>
+      <version>${sdc.version}</version>
+      <classifier>classes</classifier>
+    </dependency>
+
+    <!--test dependencies-->
+    <dependency>
+      <groupId>org.hamcrest</groupId>
+      <artifactId>hamcrest</artifactId>
+      <version>${hamcrest.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.hamcrest</groupId>
+      <artifactId>hamcrest-library</artifactId>
+      <version>${hamcrest.version}</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <profiles>
+    <profile>
+      <id>docker-staging</id>
+      <properties>
+        <docker.tag>${project.version}-STAGING-${maven.build.timestamp}</docker.tag>
+        <docker.latest.tag>${project.version}-STAGING-latest</docker.latest.tag>
+      </properties>
+    </profile>
+    <profile>
+      <id>docker</id>
+      <activation>
+        <activeByDefault>false</activeByDefault>
+      </activation>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>io.fabric8</groupId>
+            <artifactId>docker-maven-plugin</artifactId>
+            <configuration>
+              <apiVersion>1.23</apiVersion>
+              <registry>nexus3.onap.org:10001</registry>
+              <authConfig>
+                <pull>
+                  <username>docker</username>
+                  <password>docker</password>
+                </pull>
+              </authConfig>
+              <images>
+                <image>
+                  <name>onap/sdc-catalog-be-plugin/etsi-nfv-nsd-csar</name>
+                  <alias>catalog-be-plugin-etsi-nfv-nsd-csar</alias>
+                  <build>
+                    <cleanup>try</cleanup>
+                    <from>alpine:3.8</from>
+                    <assembly>
+                      <descriptorRef>artifact</descriptorRef>
+                      <targetDir>/plugins</targetDir>
+                    </assembly>
+                    <tags>
+                      <tag>${parsedVersion.majorVersion}.${parsedVersion.minorVersion}-STAGING-latest</tag>
+                      <tag>${project.version}-${maven.build.timestamp}</tag>
+                    </tags>
+                  </build>
+                </image>
+              </images>
+            </configuration>
+            <executions>
+              <execution>
+                <id>clean-images</id>
+                <phase>pre-clean</phase>
+                <goals>
+                  <goal>remove</goal>
+                </goals>
+              </execution>
+              <execution>
+                <id>generate-images</id>
+                <phase>package</phase>
+                <goals>
+                  <goal>build</goal>
+                </goals>
+              </execution>
+              <execution>
+                <id>push-images</id>
+                <phase>deploy</phase>
+                <goals>
+                  <goal>push</goal>
+                </goals>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
+
+</project>
\ No newline at end of file
diff --git a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/builder/NsdCsarManifestBuilder.java b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/builder/NsdCsarManifestBuilder.java
new file mode 100644 (file)
index 0000000..6dc1202
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.plugins.etsi.nfv.nsd.builder;
+
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+
+/**
+ * Builder for the manifest (.mf) file in a NSD CSAR
+ */
+public class NsdCsarManifestBuilder {
+    private static final String METADATA = "metadata";
+    private static final String SOURCE = "Source";
+    private static final String NSD_DESIGNER = "nsd_designer";
+    private static final String NSD_FILE_STRUCTURE_VERSION = "nsd_file_structure_version";
+    private static final String NSD_RELEASE_DATE_TIME = "nsd_release_date_time";
+    private static final String NSD_NAME = "nsd_name";
+    private static final String NSD_INVARIANT_ID = "nsd_invariant_id";
+    private static final String ATTRIBUTE_SEPARATOR = ": ";
+    private static final String NEW_LINE = "\n";
+    private static final DateTimeFormatter RFC_3339_DATE_TIME_FORMATTER =
+        DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ");
+
+    private final StringBuilder builder;
+    private final StringBuilder metadataBuilder;
+    private final StringBuilder sourceBuilder;
+    private final MetadataHeader metadataHeader;
+    private final HashSet<String> sources;
+
+    public NsdCsarManifestBuilder() {
+        builder = new StringBuilder();
+        metadataBuilder = new StringBuilder();
+        sourceBuilder = new StringBuilder();
+        metadataHeader = new MetadataHeader();
+        sources = new LinkedHashSet<>();
+        metadataBuilder.append(METADATA).append(ATTRIBUTE_SEPARATOR)
+            .append(NEW_LINE);
+    }
+
+    /**
+     * Sets a value for the {@link #NSD_DESIGNER} manifest entry.
+     *
+     * @param designer the value
+     * @return the builder instance
+     */
+    public NsdCsarManifestBuilder withDesigner(final String designer) {
+        metadataHeader.designer = designer;
+        return this;
+    }
+
+    /**
+     * Sets a value for the {@link #NSD_INVARIANT_ID} manifest entry.
+     *
+     * @param invariantId the value
+     * @return the builder instance
+     */
+    public NsdCsarManifestBuilder withInvariantId(final String invariantId) {
+        metadataHeader.invariantId = invariantId;
+        return this;
+    }
+
+    /**
+     * Sets a value for the {@link #NSD_NAME} manifest entry.
+     *
+     * @param nsdName the value
+     * @return the builder instance
+     */
+    public NsdCsarManifestBuilder withName(final String nsdName) {
+        metadataHeader.nsdName = nsdName;
+        return this;
+    }
+
+    /**
+     * Sets the current date time to the {@link #NSD_RELEASE_DATE_TIME} manifest entry.
+     *
+     * @return the builder instance
+     */
+    public NsdCsarManifestBuilder withNowReleaseDateTime() {
+        metadataHeader.nsdReleaseDateTime = getNowDateTime();
+        return this;
+    }
+
+    /**
+     * Sets a value for the {@link #NSD_FILE_STRUCTURE_VERSION} manifest entry.
+     *
+     * @param fileStructureVersion the value
+     * @return the builder instance
+     */
+    public NsdCsarManifestBuilder withFileStructureVersion(final String fileStructureVersion) {
+        metadataHeader.fileStructureVersion = fileStructureVersion;
+        return this;
+    }
+
+    /**
+     * Add a list of {@link #SOURCE} entries to the manifest
+     *
+     * @param sources a list of source path
+     * @return the builder instance
+     */
+    public NsdCsarManifestBuilder withSources(final Collection<String> sources) {
+        this.sources.addAll(sources);
+        return this;
+    }
+
+    /**
+     * Builds a string representing the manifest content based on provided values.
+     *
+     * @return a string representing the manifest content
+     */
+    public String build() {
+        appendEntry(metadataBuilder, NSD_DESIGNER, metadataHeader.designer);
+        appendEntry(metadataBuilder, NSD_INVARIANT_ID, metadataHeader.invariantId);
+        appendEntry(metadataBuilder, NSD_NAME, metadataHeader.nsdName);
+        appendEntry(metadataBuilder, NSD_RELEASE_DATE_TIME, metadataHeader.nsdReleaseDateTime);
+        appendEntry(metadataBuilder, NSD_FILE_STRUCTURE_VERSION, metadataHeader.fileStructureVersion);
+        sources.forEach(source -> appendEntry(sourceBuilder, SOURCE, source));
+
+        builder.append(metadataBuilder)
+            .append(NEW_LINE)
+            .append(sourceBuilder);
+        return builder.toString();
+    }
+
+    private String getNowDateTime() {
+        return ZonedDateTime.now().format(RFC_3339_DATE_TIME_FORMATTER);
+    }
+
+    private void appendEntry(final StringBuilder builder, final String entry, final String value) {
+        if (value != null) {
+            builder.append(entry).append(ATTRIBUTE_SEPARATOR).append(value)
+                .append(NEW_LINE);
+        }
+    }
+
+    private class MetadataHeader {
+        private String fileStructureVersion;
+        private String nsdName;
+        private String nsdReleaseDateTime;
+        private String designer;
+        private String invariantId;
+    }
+
+}
diff --git a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/builder/NsdToscaMetadataBuilder.java b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/builder/NsdToscaMetadataBuilder.java
new file mode 100644 (file)
index 0000000..e709993
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.plugins.etsi.nfv.nsd.builder;
+
+/**
+ * Builder for the TOSCA.meta file in a NSD CSAR
+ */
+public class NsdToscaMetadataBuilder {
+
+    public static final String CSAR_VERSION = "CSAR-Version";
+    public static final String CREATED_BY = "Created-By";
+    public static final String TOSCA_META_FILE_VERSION = "TOSCA-Meta-File-Version";
+    public static final String ENTRY_DEFINITIONS = "Entry-Definitions";
+    public static final String ETSI_ENTRY_CHANGE_LOG = "ETSI-Entry-Change-Log";
+    public static final String ETSI_ENTRY_MANIFEST = "ETSI-Entry-Manifest";
+
+    private static final String ATTRIBUTE_SEPARATOR = ": ";
+    private static final String NEW_LINE = "\n";
+
+    private final StringBuilder builder = new StringBuilder();
+    private String csarVersion;
+    private String createdBy;
+    private String entryDefinitionsPath;
+    private String toscaMetaVersion;
+    private String entryManifest;
+    private String changeLogPath;
+
+    /**
+     * Sets a value for the {@link #CSAR_VERSION} metadata entry.
+     *
+     * @param csarVersion the value
+     * @return the builder instance
+     */
+    public NsdToscaMetadataBuilder withCsarVersion(final String csarVersion) {
+        this.csarVersion = csarVersion;
+        return this;
+    }
+
+    /**
+     * Sets a value for the {@link #CREATED_BY} metadata entry.
+     *
+     * @param createdBy the value
+     * @return the builder instance
+     */
+    public NsdToscaMetadataBuilder withCreatedBy(final String createdBy) {
+        this.createdBy = createdBy;
+        return this;
+    }
+
+    /**
+     * Sets a value for the {@link #TOSCA_META_FILE_VERSION} metadata entry.
+     *
+     * @param toscaMetaVersion the value
+     * @return the builder instance
+     */
+    public NsdToscaMetadataBuilder withToscaMetaVersion(final String toscaMetaVersion) {
+        this.toscaMetaVersion = toscaMetaVersion;
+        return this;
+    }
+
+    /**
+     * Sets a value for the {@link #ENTRY_DEFINITIONS} metadata entry.
+     *
+     * @param entryDefinitionsPath the value
+     * @return the builder instance
+     */
+    public NsdToscaMetadataBuilder withEntryDefinitions(final String entryDefinitionsPath) {
+        this.entryDefinitionsPath = entryDefinitionsPath;
+        return this;
+    }
+
+    /**
+     * Sets a value for the {@link #ETSI_ENTRY_MANIFEST} metadata entry.
+     *
+     * @param entryManifest the value
+     * @return the builder instance
+     */
+    public NsdToscaMetadataBuilder withEntryManifest(final String entryManifest) {
+        this.entryManifest = entryManifest;
+        return this;
+    }
+
+    /**
+     * Sets a value for the {@link #ETSI_ENTRY_CHANGE_LOG} metadata entry.
+     *
+     * @param changeLogPath the value
+     * @return the builder instance
+     */
+    public NsdToscaMetadataBuilder withEntryChangeLog(final String changeLogPath) {
+        this.changeLogPath = changeLogPath;
+        return this;
+    }
+
+    /**
+     * Builds a string representing the TOSCA metadata content based on provided values.
+     *
+     * @return a string representing the TOSCA metadata content
+     */
+    public String build() {
+        appendEntry(CSAR_VERSION, csarVersion);
+        appendEntry(CREATED_BY, createdBy);
+        appendEntry(TOSCA_META_FILE_VERSION, toscaMetaVersion);
+        appendEntry(ENTRY_DEFINITIONS, entryDefinitionsPath);
+        appendEntry(ETSI_ENTRY_MANIFEST, entryManifest);
+        appendEntry(ETSI_ENTRY_CHANGE_LOG, changeLogPath);
+        return builder.toString();
+    }
+
+    private void appendEntry(final String entry, final String value) {
+        if (value != null) {
+            builder.append(entry).append(ATTRIBUTE_SEPARATOR).append(value)
+                .append(NEW_LINE);
+        }
+    }
+
+}
diff --git a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/exception/NsdException.java b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/exception/NsdException.java
new file mode 100644 (file)
index 0000000..2483b2e
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.plugins.etsi.nfv.nsd.exception;
+
+/**
+ * Represents any problem during the NSD CSAR generation.
+ */
+public class NsdException extends Exception {
+
+    public NsdException(final String s) {
+        super(s);
+    }
+
+    public NsdException(final String s, final Throwable throwable) {
+        super(s, throwable);
+    }
+}
diff --git a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/exception/VnfDescriptorException.java b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/exception/VnfDescriptorException.java
new file mode 100644 (file)
index 0000000..e4f966f
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.plugins.etsi.nfv.nsd.exception;
+
+/**
+ * Represents any problem during the Vnf Descriptor generation.
+ */
+public class VnfDescriptorException extends Exception {
+
+    public VnfDescriptorException(final String s, final Throwable throwable) {
+        super(s, throwable);
+    }
+}
diff --git a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/EtsiNfvNsCsarEntryGenerator.java b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/EtsiNfvNsCsarEntryGenerator.java
new file mode 100644 (file)
index 0000000..00aef05
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.plugins.etsi.nfv.nsd.generator;
+
+import static org.openecomp.sdc.common.api.ArtifactTypeEnum.ETSI_PACKAGE;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.plugins.CsarEntryGenerator;
+import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.exception.NsdException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Generates a Network Service CSAR based on a SERVICE component and wraps it in a SDC CSAR entry.
+ */
+@org.springframework.stereotype.Component("etsiNfvNsCsarEntryGenerator")
+public class EtsiNfvNsCsarEntryGenerator implements CsarEntryGenerator {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(EtsiNfvNsCsarEntryGenerator.class);
+    static final String ETSI_NS_COMPONENT_CATEGORY = "ETSI Network Service";
+    static final String NSD_FILE_PATH_FORMAT = "Artifacts/%s/%s.csar";
+
+    private final EtsiNfvNsdCsarGenerator etsiNfvNsdCsarGenerator;
+
+    public EtsiNfvNsCsarEntryGenerator(final EtsiNfvNsdCsarGenerator etsiNfvNsdCsarGenerator) {
+        this.etsiNfvNsdCsarGenerator = etsiNfvNsdCsarGenerator;
+    }
+
+    /**
+     * Generates a Network Service CSAR based on a SERVICE component of category {@link
+     * EtsiNfvNsCsarEntryGenerator#ETSI_NS_COMPONENT_CATEGORY} and wraps it in a SDC CSAR entry.
+     *
+     * @param component the component to create the NS CSAR from
+     * @return an entry to be added in the Component CSAR by SDC
+     */
+    @Override
+    public Map<String, byte[]> generateCsarEntries(final Component component) {
+        final String componentName = component == null ? "null" : component.getName();
+        if (component == null || ComponentTypeEnum.SERVICE != component.getComponentType()) {
+            LOGGER.debug("Ignoring NSD CSAR generation for component '{}' as it is not a SERVICE", componentName);
+            return Collections.emptyMap();
+        }
+
+        final boolean isEOTemplate = component.getCategories().stream()
+            .anyMatch(category -> ETSI_NS_COMPONENT_CATEGORY.equals(category.getName()));
+        if (!isEOTemplate) {
+            LOGGER.debug("Ignoring NSD CSAR generation for component '{}' as it does not belong to the category '{}'",
+                componentName, ETSI_NS_COMPONENT_CATEGORY);
+            return Collections.emptyMap();
+        }
+
+        final byte[] nsdCsar;
+        try {
+            nsdCsar = etsiNfvNsdCsarGenerator.generateNsdCsar(component);
+        } catch (final NsdException e) {
+            LOGGER.error("Could not create NSD CSAR entry for component '{}'"
+                , component.getName(), e);
+            return Collections.emptyMap();
+        } catch (final Exception e) {
+            LOGGER.error("Could not create NSD CSAR entry for component '{}'. An unexpected exception occurred"
+                , component.getName(), e);
+            return Collections.emptyMap();
+        }
+
+        return createEntry(component.getNormalizedName(), nsdCsar);
+    }
+
+    private Map<String, byte[]> createEntry(final String csarName, final byte[] nsdCsar) {
+        final Map<String, byte[]> entryMap = new HashMap<>();
+        final String entryKey = String.format(NSD_FILE_PATH_FORMAT, ETSI_PACKAGE, csarName);
+        entryMap.put(entryKey, nsdCsar);
+        return entryMap;
+    }
+}
diff --git a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/EtsiNfvNsdCsarGenerator.java b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/EtsiNfvNsdCsarGenerator.java
new file mode 100644 (file)
index 0000000..f9ee55e
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.plugins.etsi.nfv.nsd.generator;
+
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.exception.NsdException;
+
+/**
+ * Generator for a ETSI NFV NSD CSAR
+ */
+public interface EtsiNfvNsdCsarGenerator {
+
+    /**
+     * Generates the ETSI NFV Network Service Descriptor based on a SERVICE SDC component.
+     *
+     * @param component the service component
+     * @return the CSAR package content
+     */
+    byte[] generateNsdCsar(Component component) throws NsdException;
+
+}
diff --git a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/EtsiNfvNsdCsarGeneratorImpl.java b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/EtsiNfvNsdCsarGeneratorImpl.java
new file mode 100644 (file)
index 0000000..64356ca
--- /dev/null
@@ -0,0 +1,366 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.plugins.etsi.nfv.nsd.generator;
+
+import static org.openecomp.sdc.common.api.ArtifactTypeEnum.ETSI_PACKAGE;
+import static org.openecomp.sdc.common.api.ArtifactTypeEnum.ONBOARDED_PACKAGE;
+
+import fj.data.Either;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.Set;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.collections.MapUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.io.output.ByteArrayOutputStream;
+import org.apache.commons.lang.StringUtils;
+import org.openecomp.sdc.be.dao.cassandra.ArtifactCassandraDao;
+import org.openecomp.sdc.be.dao.cassandra.CassandraOperationStatus;
+import org.openecomp.sdc.be.datatypes.enums.JsonPresentationFields;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.builder.NsdCsarManifestBuilder;
+import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.builder.NsdToscaMetadataBuilder;
+import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.exception.NsdException;
+import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.model.Nsd;
+import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.model.VnfDescriptor;
+import org.openecomp.sdc.be.resources.data.DAOArtifactData;
+import org.openecomp.sdc.be.tosca.utils.OperationArtifactUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.Resource;
+
+/**
+ * Implementation of a ETSI NFV NSD CSAR generator
+ */
+@org.springframework.stereotype.Component("etsiNfvNsdCsarGenerator")
+public class EtsiNfvNsdCsarGeneratorImpl implements EtsiNfvNsdCsarGenerator {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(EtsiNfvNsdCsarGeneratorImpl.class);
+
+    private static final String MANIFEST_EXT = "mf";
+    private static final String SLASH = "/";
+    private static final String DOT = ".";
+    private static final String DOT_YAML = DOT + "yaml";
+
+    private static final String DEFINITION = "Definitions";
+    private static final String TOSCA_META_PATH = "TOSCA-Metadata/TOSCA.meta";
+
+    private final VnfDescriptorGenerator vnfDescriptorGenerator;
+    private final NsDescriptorGenerator nsDescriptorGeneratorImpl;
+    private final ArtifactCassandraDao artifactCassandraDao;
+
+    public EtsiNfvNsdCsarGeneratorImpl(final VnfDescriptorGenerator vnfDescriptorGenerator,
+                                       final NsDescriptorGenerator nsDescriptorGenerator,
+                                       final ArtifactCassandraDao artifactCassandraDao) {
+        this.vnfDescriptorGenerator = vnfDescriptorGenerator;
+        this.nsDescriptorGeneratorImpl = nsDescriptorGenerator;
+        this.artifactCassandraDao = artifactCassandraDao;
+    }
+
+    @Override
+    public byte[] generateNsdCsar(final Component component) throws NsdException {
+        if (component == null) {
+            throw new NsdException("Could not generate the NSD CSAR, invalid component argument");
+        }
+
+        loadComponentArtifacts(component);
+        loadComponentInstancesArtifacts(component);
+
+        final String componentName = component.getName();
+
+        try {
+            LOGGER.debug("Starting NSD CSAR generation for component '{}'", componentName);
+            final Map<String, byte[]> nsdCsarFiles = new HashMap<>();
+
+            final List<VnfDescriptor> vnfDescriptorList = generateVnfPackages(component);
+            vnfDescriptorList.forEach(vnfPackage -> nsdCsarFiles.putAll(vnfPackage.getDefinitionFiles()));
+
+            final String nsdFileName = getNsdFileName(component);
+            final Nsd nsd = generateNsd(component, vnfDescriptorList);
+            nsdCsarFiles.put(getNsdPath(nsdFileName), nsd.getContents());
+            nsdCsarFiles.put(TOSCA_META_PATH, buildToscaMetaContent(nsdFileName).getBytes());
+            addEtsiSolNsdTypes(nsdCsarFiles);
+            for (final String referencedFile : nsd.getArtifactReferences()) {
+                getReferencedArtifact(component, referencedFile).ifPresent(
+                    artifactDefinition -> nsdCsarFiles.put(referencedFile, artifactDefinition.getPayloadData())
+                );
+            }
+            nsdCsarFiles
+                .put(getManifestPath(nsdFileName), getManifestFileContent(nsd, nsdCsarFiles.keySet()).getBytes());
+
+            final byte[] csar = buildCsarPackage(nsdCsarFiles);
+            LOGGER.debug("Successfully generated NSD CSAR package");
+            return csar;
+        } catch (final Exception exception) {
+            throw new NsdException("Could not generate the NSD CSAR file", exception);
+        }
+    }
+
+    private void loadComponentArtifacts(final Component component) {
+        final Map<String, ArtifactDefinition> allArtifactsMap = component.getAllArtifacts();
+        if (allArtifactsMap == null) {
+            return;
+        }
+        allArtifactsMap.keySet().forEach(key -> {
+            final ArtifactDefinition artifactDefinition = allArtifactsMap.get(key);
+            if (StringUtils.isNotEmpty(artifactDefinition.getEsId())) {
+                final Optional<byte[]> artifactPayload = loadArtifactPayload(artifactDefinition.getEsId());
+                if (artifactPayload.isPresent()) {
+                    artifactDefinition.setPayload(artifactPayload.get());
+                } else {
+                    LOGGER.warn("Could not load component '{}' artifact '{}'",
+                        component.getName(), artifactDefinition.getArtifactName());
+                }
+            }
+        });
+    }
+
+    private void loadComponentInstancesArtifacts(final Component component) {
+        final List<ComponentInstance> componentInstanceList = component.getComponentInstances();
+        if (CollectionUtils.isEmpty(componentInstanceList)) {
+            return;
+        }
+        for (final ComponentInstance componentInstance : componentInstanceList) {
+            final Map<String, ArtifactDefinition> deploymentArtifacts = componentInstance.getDeploymentArtifacts();
+            if (MapUtils.isEmpty(deploymentArtifacts)) {
+                continue;
+            }
+            deploymentArtifacts.values().stream()
+                .filter(artifactDefinition -> StringUtils.isNotEmpty(artifactDefinition.getEsId()))
+                .forEach(artifactDefinition -> {
+                    final Optional<byte[]> artifactPayload = loadArtifactPayload(artifactDefinition.getEsId());
+                    if (artifactPayload.isPresent()) {
+                        artifactDefinition.setPayload(artifactPayload.get());
+                    } else {
+                        LOGGER.warn("Could not load component '{}' instance '{}' artifact '{}'",
+                            component.getName(), componentInstance.getName(), artifactDefinition.getArtifactName());
+                    }
+                });
+        }
+    }
+
+    private List<VnfDescriptor> generateVnfPackages(final Component component) throws NsdException {
+        final List<ComponentInstance> componentInstanceList = component.getComponentInstances();
+        if (CollectionUtils.isEmpty(componentInstanceList)) {
+            LOGGER.warn("Could not find any instance in service '{}'", component.getName());
+            return Collections.emptyList();
+        }
+
+        final List<VnfDescriptor> vnfDescriptorList = new ArrayList<>();
+        for (final ComponentInstance componentInstance : componentInstanceList) {
+            final String componentInstanceName = componentInstance.getName();
+            final ArtifactDefinition onboardedCsarArtifact = findOnboardedCsar(componentInstance).orElse(null);
+            if (onboardedCsarArtifact == null) {
+                LOGGER.warn(
+                    "Unable to generate VNF Package for component instance '{}', no onboarded package present",
+                    componentInstanceName);
+                continue;
+            }
+            final Optional<VnfDescriptor> vnfPackage;
+            try {
+                vnfPackage = vnfDescriptorGenerator.generate(componentInstanceName, onboardedCsarArtifact);
+            } catch (final Exception e) {
+                final String errorMsg =
+                    String.format("Could not generate VNF package for component instance %s", componentInstanceName);
+                throw new NsdException(errorMsg, e);
+            }
+            if (vnfPackage.isPresent()) {
+                vnfDescriptorList.add(vnfPackage.get());
+            } else {
+                LOGGER.warn(
+                    "Unable to generate VNF Package for component instance '{}', no onboarded package present",
+                    componentInstanceName);
+            }
+        }
+
+        return vnfDescriptorList;
+    }
+
+    private Optional<ArtifactDefinition> findOnboardedCsar(final ComponentInstance componentInstance) {
+        final Map<String, ArtifactDefinition> artifactDefinitionMap = componentInstance.getDeploymentArtifacts();
+        if (artifactDefinitionMap == null || artifactDefinitionMap.isEmpty()) {
+            return Optional.empty();
+        }
+        return artifactDefinitionMap.values()
+            .stream()
+            .filter(artifactDefinition -> {
+                final String artifactType = (String) artifactDefinition
+                    .getToscaPresentationValue(JsonPresentationFields.ARTIFACT_TYPE);
+                return ONBOARDED_PACKAGE.getType().equals(artifactType) || ETSI_PACKAGE.getType().equals(artifactType);
+            })
+            .findFirst();
+    }
+
+    private void addEtsiSolNsdTypes(final Map<String, byte[]> nsdCsarFileMap) {
+        final Path baseFolderPath = Paths.get("etsi-nfv-types");
+        String nsdTypesFilename = "etsi_nfv_sol001_nsd_2_7_1_types.yaml";
+
+        try {
+            final Resource resource =
+                new ClassPathResource(Paths.get(baseFolderPath.toString(), nsdTypesFilename).toString());
+            nsdCsarFileMap.put(DEFINITION + "/" + nsdTypesFilename,
+                IOUtils.toByteArray(resource.getInputStream()));
+        } catch (final IOException exception) {
+            LOGGER.error("Error adding {} to NSD CSAR", nsdTypesFilename, exception);
+        }
+
+        String commonTypesFilename = "etsi_nfv_sol001_common_types.yaml";
+        try {
+            final Resource resource =
+                new ClassPathResource(Paths.get(baseFolderPath.toString(), commonTypesFilename).toString());
+            nsdCsarFileMap.put(DEFINITION + "/" + commonTypesFilename,
+                IOUtils.toByteArray(resource.getInputStream()));
+        } catch (final IOException exception) {
+            LOGGER.error("Error adding {} to NSD CSAR", commonTypesFilename, exception);
+        }
+    }
+
+    private Nsd generateNsd(final Component component,
+                            final List<VnfDescriptor> vnfDescriptorList) throws NsdException {
+        return nsDescriptorGeneratorImpl.generate(component, vnfDescriptorList)
+            .orElseThrow(() ->
+                new NsdException(String
+                    .format("Could not generate the Network Service Descriptor for component %s", component.getName()))
+            );
+    }
+
+    private Optional<ArtifactDefinition> getReferencedArtifact(final Component component,
+                                                               final String filePath) throws NsdException {
+        final Map<String, ArtifactDefinition> interfaceOperationArtifactsByName =
+            OperationArtifactUtil.getDistinctInterfaceOperationArtifactsByName(component);
+        final String[] pathComponents = filePath.split(SLASH);
+        final String artifactName = pathComponents[pathComponents.length - 1];
+        final ArtifactDefinition artifactDefinition = interfaceOperationArtifactsByName.get(artifactName);
+        if (artifactDefinition == null) {
+            throw new NsdException(String.format("Could not find artifact '%s'", filePath));
+        }
+        LOGGER.debug("ArtifactName {}, unique ID {}", artifactDefinition.getArtifactName(),
+            artifactDefinition.getUniqueId());
+        if (artifactDefinition.getPayloadData() == null) {
+            final Optional<byte[]> artifactPayload = loadArtifactPayload(artifactDefinition.getEsId());
+
+            if (!artifactPayload.isPresent()) {
+                throw new NsdException(String.format("Could not load artifact '%s' payload", filePath));
+            }
+            artifactDefinition.setPayload(artifactPayload.get());
+        }
+
+        return Optional.of(artifactDefinition);
+    }
+
+    private Optional<byte[]> loadArtifactPayload(final String artifactCassandraId) {
+        final Either<DAOArtifactData, CassandraOperationStatus> artifactResponse = artifactCassandraDao
+            .getArtifact(artifactCassandraId);
+
+        if (artifactResponse.isRight()) {
+            LOGGER.debug("Failed to fetch artifact from Cassandra by id {} error {} ", artifactCassandraId,
+                artifactResponse.right().value());
+            return Optional.empty();
+        }
+        final DAOArtifactData artifactData = artifactResponse.left().value();
+        return Optional.of(artifactData.getDataAsArray());
+    }
+
+    private String buildToscaMetaContent(final String nsdFileName) {
+        LOGGER.debug("Creating TOSCA.meta content");
+        final NsdToscaMetadataBuilder builder = new NsdToscaMetadataBuilder();
+
+        builder.withCsarVersion("1.1")
+            .withCreatedBy("ONAP")
+            .withToscaMetaVersion("1.0")
+            .withEntryDefinitions(getNsdPath(nsdFileName))
+            .withEntryManifest(getManifestPath(nsdFileName))
+            .withEntryChangeLog("ChangeLog.txt");
+
+        final String toscaMetadata = builder.build();
+        LOGGER.debug("Successfully created NS CSAR TOSCA.meta content:\n {}", toscaMetadata);
+        return toscaMetadata;
+    }
+
+    private String getManifestFileContent(final Nsd nsd, final Set<String> files) {
+        LOGGER.debug("Creating NS manifest file content");
+
+        final NsdCsarManifestBuilder nsdCsarManifestBuilder = new NsdCsarManifestBuilder();
+        nsdCsarManifestBuilder.withDesigner(nsd.getDesigner())
+            .withInvariantId(nsd.getInvariantId())
+            .withName(nsd.getName())
+            .withNowReleaseDateTime()
+            .withFileStructureVersion(nsd.getVersion())
+            .withSources(files);
+
+        final String manifest = nsdCsarManifestBuilder.build();
+        LOGGER.debug("Successfully created NS CSAR manifest file content:\n {}", manifest);
+        return manifest;
+
+    }
+
+    private String getManifestPath(final String nsdFileName) {
+        return nsdFileName + DOT + MANIFEST_EXT;
+    }
+
+    private String getNsdPath(final String nsdFileName) {
+        return DEFINITION + SLASH + nsdFileName + DOT_YAML;
+    }
+
+    private String getNsdFileName(final Component component) {
+        return component.getNormalizedName();
+    }
+
+    private byte[] buildCsarPackage(final Map<String, byte[]> nsdCsarFileMap) throws NsdException {
+        if (nsdCsarFileMap.isEmpty()) {
+            throw new NsdException("No files were provided to build the NSD CSAR package");
+        }
+        try (final ByteArrayOutputStream out = new ByteArrayOutputStream();
+            final ZipOutputStream zip = new ZipOutputStream(out)) {
+            for (final Entry<String, byte[]> entry : nsdCsarFileMap.entrySet()) {
+                final String filePath = entry.getKey();
+                final byte[] fileContent = entry.getValue();
+                if (fileContent == null) {
+                    LOGGER.error("Could not add '{}' to NSD CSAR. File content is null", filePath);
+                    continue;
+                }
+                LOGGER.debug("Adding '{}' to NSD CSAR with content size: '{}'", filePath, fileContent.length);
+                zip.putNextEntry(new ZipEntry(filePath));
+                zip.write(fileContent);
+            }
+            zip.flush();
+            zip.finish();
+            LOGGER.debug("NSD CSAR zip file was successfully built");
+
+            return out.toByteArray();
+        } catch (final IOException e) {
+            throw new NsdException("Could not build the NSD CSAR zip file", e);
+        }
+    }
+
+}
diff --git a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/NsDescriptorGenerator.java b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/NsDescriptorGenerator.java
new file mode 100644 (file)
index 0000000..d36757c
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.plugins.etsi.nfv.nsd.generator;
+
+import java.util.List;
+import java.util.Optional;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.exception.NsdException;
+import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.model.Nsd;
+import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.model.VnfDescriptor;
+
+public interface NsDescriptorGenerator {
+
+    /**
+     * Generates the TOSCA Network Service Descriptor (NSD) based on a SERVICE SDC component and its VNF instances.
+     *
+     * @param component the SERVICE component
+     * @param vnfDescriptorList the VNF instances
+     * @return a NSD representation
+     */
+    Optional<Nsd> generate(final Component component, final List<VnfDescriptor> vnfDescriptorList) throws NsdException;
+}
diff --git a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/NsDescriptorGeneratorImpl.java b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/NsDescriptorGeneratorImpl.java
new file mode 100644 (file)
index 0000000..0c8b86b
--- /dev/null
@@ -0,0 +1,411 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.plugins.etsi.nfv.nsd.generator;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import fj.data.Either;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.MapUtils;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.tosca.constraints.ConstraintType;
+import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.exception.NsdException;
+import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.model.Nsd;
+import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.model.VnfDescriptor;
+import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.tosca.yaml.ToscaTemplateYamlGenerator;
+import org.openecomp.sdc.be.tosca.ToscaError;
+import org.openecomp.sdc.be.tosca.ToscaExportHandler;
+import org.openecomp.sdc.be.tosca.model.SubstitutionMapping;
+import org.openecomp.sdc.be.tosca.model.ToscaNodeTemplate;
+import org.openecomp.sdc.be.tosca.model.ToscaNodeType;
+import org.openecomp.sdc.be.tosca.model.ToscaProperty;
+import org.openecomp.sdc.be.tosca.model.ToscaPropertyConstraint;
+import org.openecomp.sdc.be.tosca.model.ToscaPropertyConstraintValidValues;
+import org.openecomp.sdc.be.tosca.model.ToscaTemplate;
+import org.openecomp.sdc.be.tosca.model.ToscaTopolgyTemplate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.ObjectProvider;
+
+@org.springframework.stereotype.Component("nsDescriptorGenerator")
+public class NsDescriptorGeneratorImpl implements NsDescriptorGenerator {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(NsDescriptorGeneratorImpl.class);
+    private static final String TOSCA_VERSION = "tosca_simple_yaml_1_1";
+    private static final String NS_TOSCA_TYPE = "tosca.nodes.nfv.NS";
+    private static final List<Map<String, Map<String, String>>> DEFAULT_IMPORTS_ETSI_SOL_NSD =
+        ImmutableList.of(
+            ImmutableMap.of("etsi_nfv_sol001_nsd_2_7_1_types",
+                ImmutableMap.of("file", "etsi_nfv_sol001_nsd_2_7_1_types.yaml")
+            )
+        );
+    private static final List<Map<String, Map<String, String>>> DEFAULT_IMPORTS = ConfigurationManager
+        .getConfigurationManager().getConfiguration().getDefaultImports();
+    private static final List<String> PROPERTIES_TO_EXCLUDE_FROM_ETSI_SOL_NSD_NS_NODE_TYPE = Arrays
+        .asList("cds_model_name", "cds_model_version", "skip_post_instantiation_configuration", "controller_actor");
+    private static final List<String> PROPERTIES_TO_EXCLUDE_FROM_ETSI_SOL_NSD_NS_NODE_TEMPLATE = Arrays
+        .asList("nf_function", "nf_role", "nf_naming_code", "nf_type", "nf_naming", "availability_zone_max_count",
+            "min_instances", "max_instances", "multi_stage_design", "sdnc_model_name", "sdnc_model_version",
+            "sdnc_artifact_name", "skip_post_instantiation_configuration", "controller_actor");
+
+    private final ToscaExportHandler toscaExportHandler;
+    private final ObjectProvider<ToscaTemplateYamlGenerator> toscaTemplateYamlGeneratorProvider;
+
+    public NsDescriptorGeneratorImpl(final ToscaExportHandler toscaExportHandler,
+                                     final ObjectProvider<ToscaTemplateYamlGenerator> toscaTemplateYamlGeneratorProvider) {
+        this.toscaExportHandler = toscaExportHandler;
+        this.toscaTemplateYamlGeneratorProvider = toscaTemplateYamlGeneratorProvider;
+    }
+
+    public Optional<Nsd> generate(final Component component,
+                                  final List<VnfDescriptor> vnfDescriptorList) throws NsdException {
+        if (!ComponentTypeEnum.SERVICE.equals(component.getComponentType())) {
+            return Optional.empty();
+        }
+
+        final ToscaTemplate toscaTemplate = createNetworkServiceDescriptor(component, vnfDescriptorList);
+        final ToscaNodeType nsNodeType = toscaTemplate.getNode_types().values().stream()
+            .filter(toscaNodeType -> NS_TOSCA_TYPE.equals(toscaNodeType.getDerived_from())).findFirst().orElse(null);
+        if (nsNodeType == null) {
+            return Optional.empty();
+        }
+
+        return Optional.of(buildNsd(toscaTemplate, nsNodeType));
+    }
+
+    private Nsd buildNsd(final ToscaTemplate toscaTemplate, final ToscaNodeType nsNodeType) {
+        final Nsd nsd = new Nsd();
+        nsd.setDesigner(getProperty(nsNodeType, Nsd.DESIGNER_PROPERTY));
+        nsd.setVersion(getProperty(nsNodeType, Nsd.VERSION_PROPERTY));
+        nsd.setName(getProperty(nsNodeType, Nsd.NAME_PROPERTY));
+        nsd.setInvariantId(getProperty(nsNodeType, Nsd.INVARIANT_ID_PROPERTY));
+        final ToscaTemplateYamlGenerator yamlParserProvider =
+            toscaTemplateYamlGeneratorProvider.getObject(toscaTemplate);
+        final byte[] contents = yamlParserProvider.parseToYamlString().getBytes();
+        nsd.setContents(contents);
+        final List<String> interfaceImplementations = getInterfaceImplementations(toscaTemplate);
+        nsd.setArtifactReferences(interfaceImplementations);
+        return nsd;
+    }
+
+    private List<String> getInterfaceImplementations(final ToscaTemplate template) {
+        if (template.getTopology_template().getNode_templates() == null) {
+            return Collections.emptyList();
+        }
+        final List<String> interfaceImplementations = new ArrayList<>();
+        final Collection<ToscaNodeTemplate> nodeTemplates =
+            template.getTopology_template().getNode_templates().values();
+        nodeTemplates.stream()
+            .filter(toscaNodeTemplate -> toscaNodeTemplate.getInterfaces() != null)
+            .forEach(toscaNodeTemplate ->
+                toscaNodeTemplate.getInterfaces().values().forEach(interfaceInstance ->
+                    interfaceImplementations.addAll(getInterfaceImplementations(interfaceInstance))
+            ));
+        return interfaceImplementations;
+    }
+
+    private Collection<String> getInterfaceImplementations(final Object interfaceInstance) {
+        final Collection<String> interfaceImplementations = new ArrayList<>();
+        if (interfaceInstance instanceof Map) {
+            for (final Object value : ((Map<?, ?>) interfaceInstance).values()) {
+                if (value instanceof Map && ((Map<?, ?>) value).get("implementation") != null) {
+                    interfaceImplementations.add(((Map<?, ?>) value).get("implementation").toString());
+                }
+            }
+        }
+        return interfaceImplementations;
+    }
+
+    private String getProperty(final ToscaNodeType nodeType, final String propertyName) {
+        final ToscaProperty toscaProperty = nodeType.getProperties().get(propertyName);
+
+        final String errorMsg =
+            String.format("Property '%s' must be defined and must have a valid values constraint", propertyName);
+        final String returnValueOnError = "unknown";
+        if (toscaProperty == null || CollectionUtils.isEmpty(toscaProperty.getConstraints())) {
+            LOGGER.error(errorMsg);
+            return returnValueOnError;
+        }
+
+        final ToscaPropertyConstraint toscaPropertyConstraint = toscaProperty.getConstraints().get(0);
+        if (ConstraintType.VALID_VALUES != toscaPropertyConstraint.getConstraintType()) {
+            LOGGER.error(errorMsg);
+            return returnValueOnError;
+        }
+
+        final ToscaPropertyConstraintValidValues validValuesConstraint =
+            (ToscaPropertyConstraintValidValues) toscaPropertyConstraint;
+        final List<String> validValues = validValuesConstraint.getValidValues();
+        if(CollectionUtils.isEmpty(validValues)) {
+            LOGGER.error(errorMsg);
+            return returnValueOnError;
+        }
+
+        return validValues.get(0);
+    }
+
+    private ToscaTemplate createNetworkServiceDescriptor(final Component component,
+                                                        final List<VnfDescriptor> vnfDescriptorList)
+        throws NsdException {
+
+        final ToscaTemplate componentToscaTemplate = parseToToscaTemplate(component);
+        final ToscaTemplate componentToscaTemplateInterface = exportComponentInterfaceAsToscaTemplate(component);
+
+        final Entry<String, ToscaNodeType> firstNodeTypeEntry =
+            componentToscaTemplateInterface.getNode_types()
+                .entrySet().stream().findFirst().orElse(null);
+        if (firstNodeTypeEntry == null) {
+            throw new NsdException("Could not find abstract Service type");
+        }
+
+        final String nsNodeTypeName = firstNodeTypeEntry.getKey();
+        final ToscaNodeType nsNodeType = firstNodeTypeEntry.getValue();
+
+        final Map<String, ToscaNodeType> nodeTypeMap = new HashMap<>();
+        nodeTypeMap.put(nsNodeTypeName, createEtsiSolNsNodeType(nsNodeType));
+
+        if (componentToscaTemplate.getNode_types() == null) {
+            componentToscaTemplate.setNode_types(nodeTypeMap);
+        } else {
+            componentToscaTemplate.getNode_types().putAll(nodeTypeMap);
+        }
+
+        setPropertiesForNodeTemplates(componentToscaTemplate);
+        removeCapabilitiesFromNodeTemplates(componentToscaTemplate);
+        removeOnapPropertiesFromInputs(componentToscaTemplate);
+        handleSubstitutionMappings(componentToscaTemplate, nsNodeTypeName);
+
+        final Map<String, ToscaNodeTemplate> nodeTemplates = new HashMap<>();
+        nodeTemplates.put(nsNodeTypeName, createNodeTemplateForNsNodeType(nsNodeTypeName,
+            componentToscaTemplateInterface.getNode_types().get(nsNodeTypeName)));
+
+        if (componentToscaTemplate.getTopology_template().getNode_templates() == null) {
+            componentToscaTemplate.getTopology_template().setNode_templates(nodeTemplates);
+        } else {
+            setNodeTemplateTypesForVnfs(componentToscaTemplate, vnfDescriptorList);
+            componentToscaTemplate.getTopology_template().getNode_templates().putAll(nodeTemplates);
+        }
+
+        removeOnapMetaData(componentToscaTemplate);
+
+        setDefaultImportsForEtsiSolNsNsd(componentToscaTemplate, vnfDescriptorList);
+
+        return componentToscaTemplate;
+    }
+
+    private void handleSubstitutionMappings(final ToscaTemplate componentToscaTemplate, final String nsNodeTypeName) {
+        final SubstitutionMapping substitutionMapping = new SubstitutionMapping();
+        substitutionMapping.setNode_type(nsNodeTypeName);        
+        final SubstitutionMapping onapSubstitutionMapping = componentToscaTemplate.getTopology_template().getSubstitution_mappings();
+        if (onapSubstitutionMapping != null) {
+               substitutionMapping.setRequirements(onapSubstitutionMapping.getRequirements());
+               substitutionMapping.setCapabilities(onapSubstitutionMapping.getCapabilities());
+        }
+        componentToscaTemplate.getTopology_template().setSubstitution_mappings(substitutionMapping);
+    }
+
+    private void setNodeTemplateTypesForVnfs(final ToscaTemplate template,
+                                             final List<VnfDescriptor> vnfDescriptorList) {
+        if (CollectionUtils.isEmpty(vnfDescriptorList)) {
+            return;
+        }
+        final Map<String, ToscaNodeTemplate> nodeTemplateMap = template.getTopology_template().getNode_templates();
+        if (MapUtils.isEmpty(nodeTemplateMap)) {
+            return;
+        }
+        nodeTemplateMap.forEach((key, toscaNodeTemplate) ->
+            vnfDescriptorList.stream()
+                .filter(vnfDescriptor -> key.equals(vnfDescriptor.getName())).findFirst()
+                .ifPresent(vnfDescriptor -> toscaNodeTemplate.setType(vnfDescriptor.getNodeType()))
+        );
+    }
+
+    private void setPropertiesForNodeTemplates(final ToscaTemplate template) {
+        final Map<String, ToscaNodeTemplate> nodeTemplateMap = template.getTopology_template().getNode_templates();
+        if (MapUtils.isEmpty(nodeTemplateMap)) {
+            return;
+        }
+        for (final Entry<String, ToscaNodeTemplate> nodeTemplate : nodeTemplateMap.entrySet()) {
+            final Map<String, Object> propertyMap = nodeTemplate.getValue().getProperties();
+            if (MapUtils.isEmpty(propertyMap)) {
+                nodeTemplate.getValue().setProperties(null);
+                continue;
+            }
+            final Map<String, Object> editedPropertyMap = new HashMap<>();
+            for (final Entry<String, Object> property : propertyMap.entrySet()) {
+                if (!PROPERTIES_TO_EXCLUDE_FROM_ETSI_SOL_NSD_NS_NODE_TEMPLATE.contains(property.getKey())
+                        && propertyIsDefinedInNodeType(property.getKey())) {
+                    editedPropertyMap
+                        .put(property.getKey().substring(property.getKey().indexOf('_') + 1), property.getValue());
+                }
+            }
+            if (editedPropertyMap.isEmpty()) {
+                nodeTemplate.getValue().setProperties(null);
+            } else {
+                nodeTemplate.getValue().setProperties(editedPropertyMap);
+            }
+        }
+    }
+    
+    private void removeCapabilitiesFromNodeTemplates(final ToscaTemplate template) {
+        final Map<String, ToscaNodeTemplate> nodeTemplateMap = template.getTopology_template().getNode_templates();
+        if (MapUtils.isEmpty(nodeTemplateMap)) {
+            return;
+        }
+        for (final Entry<String, ToscaNodeTemplate> nodeTemplate : nodeTemplateMap.entrySet()) {
+               nodeTemplate.getValue().setCapabilities(null);
+        }
+    }
+
+    private void removeOnapPropertiesFromInputs(final ToscaTemplate template) {
+        final ToscaTopolgyTemplate topologyTemplate = template.getTopology_template();
+        final Map<String, ToscaProperty> inputMap = topologyTemplate.getInputs();
+        if (MapUtils.isNotEmpty(inputMap)) {
+            inputMap.entrySet()
+                .removeIf(entry -> PROPERTIES_TO_EXCLUDE_FROM_ETSI_SOL_NSD_NS_NODE_TYPE.contains(entry.getKey()));
+        }
+        if (MapUtils.isEmpty(inputMap)) {
+            topologyTemplate.setInputs(null);
+        }
+    }
+
+    private void removeOnapMetaData(final ToscaTemplate template) {
+        template.setMetadata(null);
+        final Map<String, ToscaNodeTemplate> nodeTemplateMap = template.getTopology_template().getNode_templates();
+        if (MapUtils.isEmpty(nodeTemplateMap)) {
+            return;
+        }
+        nodeTemplateMap.values().forEach(toscaNodeTemplate -> toscaNodeTemplate.setMetadata(null));
+    }
+
+    private void setDefaultImportsForEtsiSolNsNsd(final ToscaTemplate template,
+                                                  final List<VnfDescriptor> vnfDescriptorList) {
+        final List<Map<String, Map<String, String>>> importEntryMap = new ArrayList<>(DEFAULT_IMPORTS_ETSI_SOL_NSD);
+        if (CollectionUtils.isNotEmpty(vnfDescriptorList)) {
+            for (final VnfDescriptor vnfDescriptor : vnfDescriptorList) {
+                final Map<String, String> vnfImportChildEntry = new HashMap<>();
+                vnfImportChildEntry.put("file", vnfDescriptor.getVnfdFileName());
+                final Map<String, Map<String, String>> vnfdImportVnfdEntry = new HashMap<>();
+                vnfdImportVnfdEntry.put(vnfDescriptor.getName(), vnfImportChildEntry);
+                importEntryMap.add(vnfdImportVnfdEntry);
+            }
+        }
+
+        template.setImports(importEntryMap);
+    }
+
+    private ToscaNodeType createEtsiSolNsNodeType(final ToscaNodeType nsNodeType) {
+        final ToscaNodeType toscaNodeType = new ToscaNodeType();
+        toscaNodeType.setDerived_from(NS_TOSCA_TYPE);
+
+        final Map<String, ToscaProperty> propertiesInNsNodeType = nsNodeType.getProperties();
+
+        for (final Entry<String, ToscaProperty> property : propertiesInNsNodeType.entrySet()) {
+            final ToscaProperty toscaProperty = property.getValue();
+            if (toscaProperty.getDefaultp() != null) {
+                final ToscaPropertyConstraintValidValues constraint = new ToscaPropertyConstraintValidValues(
+                    Collections.singletonList(toscaProperty.getDefaultp().toString()));
+                toscaProperty.setConstraints(Collections.singletonList(constraint));
+            }
+        }
+
+        propertiesInNsNodeType.entrySet()
+            .removeIf(entry -> PROPERTIES_TO_EXCLUDE_FROM_ETSI_SOL_NSD_NS_NODE_TYPE.contains(entry.getKey()));
+        toscaNodeType.setProperties(propertiesInNsNodeType);
+
+        return toscaNodeType;
+    }
+
+    private boolean propertyIsDefinedInNodeType(final String propertyName) {
+        // This will achieve what we want for now, but will look into a more generic solution which would involve
+        // checking the node_type definition in the VNFD
+        return !propertyName.equals("additional_parameters");
+    }
+
+
+    private ToscaNodeTemplate createNodeTemplateForNsNodeType(final String nodeType,
+                                                              final ToscaNodeType toscaNodeType) {
+        final ToscaNodeTemplate nodeTemplate = new ToscaNodeTemplate();
+        nodeTemplate.setType(nodeType);
+
+        final Map<String, ToscaProperty> properties = toscaNodeType.getProperties();
+        final Map<String, Object> nodeTemplateProperties = new HashMap<>();
+        for (final Entry<String, ToscaProperty> property : properties.entrySet()) {
+            nodeTemplateProperties.put(property.getKey(), property.getValue().getDefaultp());
+        }
+
+        if (!nodeTemplateProperties.isEmpty()) {
+            nodeTemplate.setProperties(nodeTemplateProperties);
+        }
+
+        final Map<String, Object> interfaces = toscaNodeType.getInterfaces();
+        if (interfaces != null) {
+            for (final Entry<String, Object> nodeInterface : interfaces.entrySet()) {
+                if ("Nslcm".equals(nodeInterface.getKey()) && nodeInterface.getValue() instanceof Map) {
+                    ((Map<?, ?>) nodeInterface.getValue()).remove("type");
+                }
+            }
+            nodeTemplate.setInterfaces(interfaces);
+        }
+
+        return nodeTemplate;
+    }
+
+    private ToscaTemplate parseToToscaTemplate(final Component component) throws NsdException {
+        final Either<ToscaTemplate, ToscaError> toscaTemplateRes = toscaExportHandler.convertToToscaTemplate(component);
+        if (toscaTemplateRes.isRight()) {
+            String errorMsg = String.format("Could not parse component '%s' to tosca template. Error '%s'",
+                component.getName(), toscaTemplateRes.right().value().name());
+            throw new NsdException(errorMsg);
+        }
+
+        return toscaTemplateRes.left().value();
+    }
+
+
+    private ToscaTemplate exportComponentInterfaceAsToscaTemplate(final Component component) throws NsdException {
+        if (null == DEFAULT_IMPORTS) {
+            throw new NsdException("Could not load default CSAR imports from configuration");
+        }
+
+        final ToscaTemplate toscaTemplate = new ToscaTemplate(TOSCA_VERSION);
+        toscaTemplate.setImports(new ArrayList<>(DEFAULT_IMPORTS));
+        final Either<ToscaTemplate, ToscaError> toscaTemplateRes = toscaExportHandler
+            .convertInterfaceNodeType(new HashMap<>(), component, toscaTemplate, new HashMap<>(), false);
+        if (toscaTemplateRes.isRight()) {
+            throw new NsdException(String.format("Could not create abstract service from component '%s'",
+                component.getName()));
+        }
+
+        return toscaTemplateRes.left().value();
+    }
+
+}
diff --git a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/VnfDescriptorGenerator.java b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/VnfDescriptorGenerator.java
new file mode 100644 (file)
index 0000000..c4599d7
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.plugins.etsi.nfv.nsd.generator;
+
+import java.util.Optional;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.exception.VnfDescriptorException;
+import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.model.VnfDescriptor;
+
+/**
+ * Generator of a VNF Descriptor from the ONBOARDED_PACKAGE
+ */
+public interface VnfDescriptorGenerator {
+
+    /**
+     * Generates the a VNF Descriptor based on the ONBOARDED_PACKAGE artifact.
+     *
+     * @param name the name of the VNF package
+     * @param onboardedPackageArtifact the onboarded package for the VNF
+     * @return a representation of the VNF package
+     * @throws VnfDescriptorException when a problem happens during the generation
+     */
+    Optional<VnfDescriptor> generate(final String name, final ArtifactDefinition onboardedPackageArtifact)
+        throws VnfDescriptorException;
+
+}
diff --git a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/VnfDescriptorGeneratorImpl.java b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/VnfDescriptorGeneratorImpl.java
new file mode 100644 (file)
index 0000000..8cf54d1
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.plugins.etsi.nfv.nsd.generator;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import org.apache.commons.collections.MapUtils;
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.io.IOUtils;
+import org.onap.sdc.tosca.services.YamlUtil;
+import org.openecomp.core.utilities.file.FileContentHandler;
+import org.openecomp.core.utilities.file.FileUtils;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.builder.NsdToscaMetadataBuilder;
+import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.exception.VnfDescriptorException;
+import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.model.VnfDescriptor;
+import org.openecomp.sdc.common.zip.exception.ZipException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+import org.yaml.snakeyaml.Yaml;
+
+/**
+ * Implementation of a VNF Descriptor Generator
+ */
+@Component("vnfPackageGenerator")
+public class VnfDescriptorGeneratorImpl implements VnfDescriptorGenerator {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(VnfDescriptorGeneratorImpl.class);
+    private static final String SPACE_REGEX = "\\s+";
+    private static final String CSAR = "csar";
+    private static final String COLON = ":";
+    private static final String EMPTY_STRING = "";
+    private static final String SLASH = "/";
+    private static final String DEFINITIONS_DIRECTORY = "Definitions";
+    private static final String TOSCA_META_PATH = "TOSCA-Metadata/TOSCA.meta";
+
+    public Optional<VnfDescriptor> generate(final String name,
+                                            final ArtifactDefinition onboardedPackageArtifact)
+        throws VnfDescriptorException {
+        if (!isACsarArtifact(onboardedPackageArtifact)) {
+            return Optional.empty();
+        }
+
+        final FileContentHandler fileContentHandler;
+        try {
+            fileContentHandler = FileUtils.getFileContentMapFromZip(onboardedPackageArtifact.getPayloadData());
+        } catch (final ZipException e) {
+            final String errorMsg = String
+                .format("Could not unzip artifact '%s' content", onboardedPackageArtifact.getArtifactName());
+            throw new VnfDescriptorException(errorMsg, e);
+        }
+
+        if (MapUtils.isEmpty(fileContentHandler.getFiles())) {
+            return Optional.empty();
+        }
+        final String mainDefinitionFile;
+        try {
+            mainDefinitionFile = getMainFilePathFromMetaFile(fileContentHandler).orElse(null);
+        } catch (final IOException e) {
+            final String errorMsg = String
+                .format("Could not read main definition file of artifact '%s'",
+                    onboardedPackageArtifact.getArtifactName());
+            throw new VnfDescriptorException(errorMsg, e);
+        }
+        LOGGER.debug("found main file: {}", mainDefinitionFile);
+        if (mainDefinitionFile == null) {
+            return Optional.empty();
+        }
+        final VnfDescriptor vnfDescriptor = new VnfDescriptor();
+        vnfDescriptor.setName(name);
+        final String vnfdFileName = FilenameUtils.getName(mainDefinitionFile);
+
+        vnfDescriptor.setVnfdFileName(vnfdFileName);
+        vnfDescriptor.setDefinitionFiles(getFiles(fileContentHandler, mainDefinitionFile));
+        vnfDescriptor.setNodeType(getNodeType(getFileContent(fileContentHandler, mainDefinitionFile)));
+
+        return Optional.of(vnfDescriptor);
+    }
+
+    private static boolean isACsarArtifact(final ArtifactDefinition definition) {
+        return definition.getPayloadData() != null && definition.getArtifactName() != null && CSAR
+            .equalsIgnoreCase(FilenameUtils.getExtension(definition.getArtifactName()));
+    }
+
+    private Map<String, byte[]> getFiles(final FileContentHandler fileContentHandler, final String filePath) {
+        final Map<String, byte[]> files = new HashMap<>();
+
+        final byte[] fileContent = fileContentHandler.getFileContent(filePath);
+
+        if (fileContent != null) {
+            final String mainYmlFile = new String(fileContent);
+            LOGGER.debug("file content: {}", mainYmlFile);
+
+            files.put(appendDefinitionDirPath(filePath.substring(filePath.lastIndexOf(SLASH) + 1)),
+                getVnfdWithoutTopologyTemplate(fileContent));
+            final List<Object> imports = getImportFilesPath(mainYmlFile);
+            LOGGER.info("found imports {}", imports);
+            for (final Object importObject : imports) {
+                if (importObject != null) {
+                    final String importFilename = importObject.toString();
+                    final String importFileFullPath = appendDefinitionDirPath(importFilename);
+                    final byte[] importFileContent = fileContentHandler.getFileContent(importFileFullPath);
+                    files.put(appendDefinitionDirPath(importFilename), importFileContent);
+                }
+            }
+        }
+        return files;
+    }
+
+    private String getFileContent(final FileContentHandler fileContentHandler, final String filePath) {
+        final byte[] fileContent = fileContentHandler.getFileContent(filePath);
+
+        if (fileContent != null) {
+            return new String(fileContent);
+        }
+        return null;
+    }
+
+    private Optional<String> getMainFilePathFromMetaFile(final FileContentHandler fileContentHandler)
+        throws IOException {
+        final Map<String, String> metaFileContent = getMetaFileContent(fileContentHandler);
+        final String mainFile = metaFileContent.get(NsdToscaMetadataBuilder.ENTRY_DEFINITIONS);
+        if (mainFile != null) {
+            return Optional.of(mainFile.replaceAll(SPACE_REGEX, EMPTY_STRING));
+        }
+        LOGGER.error("{} entry not found in {}", NsdToscaMetadataBuilder.ENTRY_DEFINITIONS, TOSCA_META_PATH);
+        return Optional.empty();
+    }
+
+    private Map<String, String> getMetaFileContent(final FileContentHandler fileContentHandler)
+        throws IOException {
+        final InputStream inputStream = fileContentHandler.getFileContentAsStream(TOSCA_META_PATH);
+        if (inputStream == null) {
+            throw new FileNotFoundException("Unable find " + TOSCA_META_PATH + " file");
+        }
+        final List<String> lines = IOUtils.readLines(inputStream, StandardCharsets.UTF_8);
+
+        return lines.stream().map(str -> str.split(COLON))
+            .collect(Collectors.toMap(str -> str[0], str -> str.length > 1 ? str[1] : EMPTY_STRING));
+    }
+
+    private String appendDefinitionDirPath(final String filename) {
+        return DEFINITIONS_DIRECTORY + SLASH + filename;
+    }
+
+    private List<Object> getImportFilesPath(final String mainYmlFile) {
+        final Map<Object, Object> fileContentMap = new YamlUtil().yamlToObject(mainYmlFile, Map.class);
+
+        final Object importsObject = fileContentMap.get("imports");
+
+        if (importsObject instanceof List) {
+            return (List<Object>) importsObject;
+        }
+        return Collections.emptyList();
+    }
+
+    private byte[] getVnfdWithoutTopologyTemplate(final byte[] vnfdFileContent) {
+        final Yaml yaml = new Yaml();
+        final Map<String, Object> toscaFileContent = (Map<String, Object>) yaml.load(new String(vnfdFileContent));
+        toscaFileContent.remove("topology_template");
+
+        return yaml.dumpAsMap(toscaFileContent).getBytes();
+    }
+
+    private String getNodeType(final String mainYmlFile) {
+        final Map<Object, Object> fileContentMap = new YamlUtil().yamlToObject(mainYmlFile, Map.class);
+
+        final Object nodeTypesObject = fileContentMap.get("node_types");
+        if (nodeTypesObject instanceof Map
+            && ((Map<String, Object>) nodeTypesObject).size() == 1) {
+            return ((Map<String, Object>) nodeTypesObject).keySet().iterator().next();
+        }
+        return null;
+    }
+
+}
diff --git a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/model/Nsd.java b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/model/Nsd.java
new file mode 100644 (file)
index 0000000..a8de728
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.plugins.etsi.nfv.nsd.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents a Network Service Descriptor
+ */
+public class Nsd {
+
+    public static final String DESIGNER_PROPERTY = "designer";
+    public static final String VERSION_PROPERTY = "version";
+    public static final String NAME_PROPERTY = "name";
+    public static final String INVARIANT_ID_PROPERTY = "invariant_id";
+
+    private String designer;
+    private String version;
+    private String name;
+    private String invariantId;
+    private byte[] contents;
+    private List<String> artifactReferences = new ArrayList<>();
+
+    public String getDesigner() {
+        return designer;
+    }
+
+    public void setDesigner(String designer) {
+        this.designer = designer;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public void setVersion(String version) {
+        this.version = version;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getInvariantId() {
+        return invariantId;
+    }
+
+    public void setInvariantId(String invariantId) {
+        this.invariantId = invariantId;
+    }
+
+    public byte[] getContents() {
+        return contents;
+    }
+
+    public void setContents(byte[] contents) {
+        this.contents = contents;
+    }
+
+    public List<String> getArtifactReferences() {
+        return artifactReferences;
+    }
+
+    public void setArtifactReferences(List<String> artifactReferences) {
+        this.artifactReferences = artifactReferences;
+    }
+}
diff --git a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/model/VnfDescriptor.java b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/model/VnfDescriptor.java
new file mode 100644 (file)
index 0000000..53615ef
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.plugins.etsi.nfv.nsd.model;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Represents a Virtual Network Function Descriptor
+ */
+public class VnfDescriptor {
+
+    private String name;
+    private String vnfdFileName;
+    private String nodeType;
+    private Map<String, byte[]> definitionFiles = new HashMap<>();
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Map<String, byte[]> getDefinitionFiles() {
+        return definitionFiles;
+    }
+
+    public void setDefinitionFiles(Map<String, byte[]> definitionFiles) {
+        this.definitionFiles = definitionFiles;
+    }
+
+    public String getVnfdFileName() {
+        return vnfdFileName;
+    }
+
+    public void setVnfdFileName(String vnfdFileName) {
+        this.vnfdFileName = vnfdFileName;
+    }
+
+    public String getNodeType() {
+        return nodeType;
+    }
+
+    public void setNodeType(String nodeType) {
+        this.nodeType = nodeType;
+    }
+
+}
diff --git a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/tosca/yaml/NsdTemplateRepresenter.java b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/tosca/yaml/NsdTemplateRepresenter.java
new file mode 100644 (file)
index 0000000..822d6dc
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.plugins.etsi.nfv.nsd.tosca.yaml;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.openecomp.sdc.be.tosca.model.ToscaProperty;
+import org.openecomp.sdc.be.tosca.model.ToscaPropertyConstraintValidValues;
+import org.openecomp.sdc.be.tosca.model.ToscaTemplate;
+import org.yaml.snakeyaml.introspector.Property;
+import org.yaml.snakeyaml.nodes.MappingNode;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.NodeTuple;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.representer.Represent;
+import org.yaml.snakeyaml.representer.Representer;
+
+/**
+ * A NSD YAML Representer
+ */
+public class NsdTemplateRepresenter extends Representer {
+
+    private final Set<String> ignoredPropertySet = Stream.of("dependencies").collect(Collectors.toSet());
+
+    public NsdTemplateRepresenter() {
+        super();
+        this.nullRepresenter = new RepresentNull();
+    }
+
+    @Override
+    protected NodeTuple representJavaBeanProperty(final Object javaBean, final Property property,
+                                                  final Object propertyValue, final Tag customTag) {
+        if (propertyValue == null) {
+            return null;
+        }
+
+        if (ignoredPropertySet.contains(property.getName())) {
+            return null;
+        }
+
+        if (javaBean instanceof ToscaTemplate) {
+            return handleToscaTemplate(javaBean, property, propertyValue, customTag);
+        }
+
+        if (javaBean instanceof ToscaPropertyConstraintValidValues) {
+            return handleToscaPropertyConstraintValidValues(javaBean, property, propertyValue, customTag);
+        }
+
+        if (javaBean instanceof ToscaProperty) {
+            return handleToscaProperty(javaBean, property, propertyValue, customTag);
+        }
+
+        return super.representJavaBeanProperty(javaBean, property, propertyValue, customTag);
+    }
+
+    private NodeTuple handleToscaProperty(final Object javaBean, final Property property,
+                                          final Object propertyValue, final Tag customTag) {
+        final NodeTuple nodeTuple = super.representJavaBeanProperty(javaBean, property, propertyValue, customTag);
+        if ("_defaultp_".equals(property.getName())) {
+            return new NodeTuple(representData("default"), nodeTuple.getValueNode());
+        }
+
+        return nodeTuple;
+    }
+
+    private NodeTuple handleToscaPropertyConstraintValidValues(final Object javaBean, final Property property,
+                                                               final Object propertyValue, final Tag customTag) {
+        final NodeTuple nodeTuple = super.representJavaBeanProperty(javaBean, property, propertyValue, customTag);
+        if ("validValues".equals(property.getName())) {
+            final String validValuesEntryName = ToscaPropertyConstraintValidValues.getEntryToscaName("validValues");
+            return new NodeTuple(representData(validValuesEntryName), nodeTuple.getValueNode());
+        }
+
+        return nodeTuple;
+    }
+
+    private NodeTuple handleToscaTemplate(final Object javaBean, final Property property,
+                                          final Object propertyValueObj, final Tag customTag) {
+        if ("imports".equals(property.getName())) {
+            final List<Map<String, Map<String, String>>> importsList =
+                (List<Map<String, Map<String, String>>>) propertyValueObj;
+
+            final List<Map<String, String>> newImportList = new ArrayList<>();
+            importsList.forEach(importMap -> importMap.forEach((key, value) -> newImportList.add(value)));
+
+            return super.representJavaBeanProperty(javaBean, property, newImportList, customTag);
+        }
+
+        return super.representJavaBeanProperty(javaBean, property, propertyValueObj, customTag);
+    }
+
+    @Override
+    protected MappingNode representJavaBean(final Set<Property> properties, final Object javaBean) {
+        if (!classTags.containsKey(javaBean.getClass())) {
+            addClassTag(javaBean.getClass(), Tag.MAP);
+        }
+
+        return super.representJavaBean(properties, javaBean);
+    }
+
+    private class RepresentNull implements Represent {
+
+        @Override
+        public Node representData(final Object data) {
+            return representScalar(Tag.NULL, "");
+        }
+    }
+}
diff --git a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/tosca/yaml/ToscaTemplateYamlGenerator.java b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/tosca/yaml/ToscaTemplateYamlGenerator.java
new file mode 100644 (file)
index 0000000..4c2cd87
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.plugins.etsi.nfv.nsd.tosca.yaml;
+
+import org.openecomp.sdc.be.tosca.model.ToscaTemplate;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Component;
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.DumperOptions.FlowStyle;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.representer.Representer;
+
+/**
+ * Handles a Tosca Template YAML parsing
+ */
+@Component
+@Scope(BeanDefinition.SCOPE_PROTOTYPE)
+public class ToscaTemplateYamlGenerator {
+
+    private final ToscaTemplate toscaTemplate;
+    private final Representer representer;
+    private final DumperOptions dumperOptions;
+
+    public ToscaTemplateYamlGenerator(final ToscaTemplate toscaTemplate) {
+        this.toscaTemplate = toscaTemplate;
+        this.representer = new NsdTemplateRepresenter();
+        initRepresenter();
+        this.dumperOptions = new DumperOptions();
+        initDumperOptions();
+    }
+
+    /**
+     * Parses the ToscaTemplate to a String YAML.
+     *
+     * @return the YAML representing the ToscaTemplate
+     */
+    public String parseToYamlString() {
+        final Yaml yaml = new Yaml(representer, dumperOptions);
+        return yaml.dumpAsMap(toscaTemplate);
+    }
+
+    private void initDumperOptions() {
+        dumperOptions.setAllowReadOnlyProperties(false);
+        dumperOptions.setPrettyFlow(true);
+        dumperOptions.setDefaultFlowStyle(FlowStyle.FLOW);
+        dumperOptions.setCanonical(false);
+    }
+
+    private void initRepresenter() {
+        representer.addClassTag(toscaTemplate.getClass(), Tag.MAP);
+        representer.setPropertyUtils(new UnsortedPropertyUtils());
+    }
+
+}
diff --git a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/tosca/yaml/UnsortedPropertyUtils.java b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/tosca/yaml/UnsortedPropertyUtils.java
new file mode 100644 (file)
index 0000000..b93db62
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.plugins.etsi.nfv.nsd.tosca.yaml;
+
+import java.beans.IntrospectionException;
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import org.yaml.snakeyaml.introspector.BeanAccess;
+import org.yaml.snakeyaml.introspector.Property;
+import org.yaml.snakeyaml.introspector.PropertyUtils;
+
+public class UnsortedPropertyUtils extends PropertyUtils {
+
+    @Override
+    protected Set<Property> createPropertySet(final Class clazz, final BeanAccess beanAccess)
+        throws IntrospectionException {
+
+        final Collection<Property> fields = getPropertiesMap(clazz, BeanAccess.FIELD).values();
+        return new LinkedHashSet<>(fields);
+    }
+}
\ No newline at end of file
diff --git a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/resources/etsi-nfv-types/etsi_nfv_sol001_common_types.yaml b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/resources/etsi-nfv-types/etsi_nfv_sol001_common_types.yaml
new file mode 100644 (file)
index 0000000..30ee213
--- /dev/null
@@ -0,0 +1,283 @@
+tosca_definitions_version: tosca_simple_yaml_1_2
+description: ETSI NFV SOL 001 common types definitions version 2.7.1
+metadata:
+  template_name: etsi_nfv_sol001_common_types
+  template_author: ETSI_NFV
+  template_version: 2.7.1
+
+data_types:
+  tosca.datatypes.nfv.L2AddressData:
+    derived_from: tosca.datatypes.Root
+    description: Describes the information on the MAC addresses to be assigned to a connection point. 
+    properties:
+      mac_address_assignment:
+        type: boolean
+        description: Specifies if the address assignment is the responsibility of management and orchestration function or not. If it is set to True, it is the management and orchestration function responsibility
+        required: true 
+  
+  tosca.datatypes.nfv.L3AddressData:
+    derived_from: tosca.datatypes.Root
+    description: Provides information about Layer 3 level addressing scheme and parameters applicable to a CP 
+    properties:
+      ip_address_assignment:
+        type: boolean
+        description: Specifies if the address assignment is the responsibility of management and orchestration function or not. If it is set to True, it is the management and orchestration function responsibility
+        required: true
+      floating_ip_activated:
+        type: boolean
+        description: Specifies if the floating IP scheme is activated on the Connection Point or not 
+        required: true
+      ip_address_type:
+        type: string
+        description: Defines address type. The address type should be aligned with the address type supported by the layer_protocols properties of the parent VnfExtCp
+        required: false
+        constraints:
+          - valid_values: [ ipv4, ipv6 ]
+      number_of_ip_address:
+        type: integer
+        description: Minimum number of IP addresses to be assigned 
+        required: false
+        constraints:
+          - greater_than: 0
+
+  tosca.datatypes.nfv.AddressData:
+    derived_from: tosca.datatypes.Root
+    description: Describes information about the addressing scheme and parameters applicable to a CP 
+    properties:
+      address_type:
+        type: string
+        description: Describes the type of the address to be assigned to a connection point. The content type shall be aligned with the address type supported by the layerProtocol property of the connection point
+        required: true
+        constraints:
+          - valid_values: [ mac_address, ip_address ]
+      l2_address_data:
+        type: tosca.datatypes.nfv.L2AddressData
+        description: Provides the information on the MAC addresses to be assigned to a connection point.
+        required: false
+      l3_address_data:
+        type: tosca.datatypes.nfv.L3AddressData
+        description: Provides the information on the IP addresses to be assigned to a connection point 
+        required: false
+
+  tosca.datatypes.nfv.ConnectivityType:
+    derived_from: tosca.datatypes.Root
+    description: describes additional connectivity information of a virtualLink 
+    properties:
+      layer_protocols:
+        type: list
+        description: Identifies the protocol a virtualLink gives access to (ethernet, mpls, odu2, ipv4, ipv6, pseudo-wire).The top layer protocol of the virtualLink protocol stack shall always be provided. The lower layer protocols may be included when there are specific requirements on these layers.
+        required: true
+        entry_schema:
+          type: string
+          constraints:
+            - valid_values: [ ethernet, mpls, odu2, ipv4, ipv6, pseudo-wire ]
+      flow_pattern:
+        type: string
+        description: Identifies the flow pattern of the connectivity
+        required: false
+        constraints:
+          - valid_values: [ line, tree, mesh ]
+
+  tosca.datatypes.nfv.LinkBitrateRequirements:
+    derived_from: tosca.datatypes.Root
+    description: describes the requirements in terms of bitrate for a virtual link 
+    properties:
+      root:
+        type: integer # in bits per second
+        description: Specifies the throughput requirement in bits per second of the link (e.g. bitrate of E-Line, root bitrate of E-Tree, aggregate capacity of E-LAN).
+        required: true
+        constraints:
+          - greater_or_equal: 0
+      leaf:
+        type: integer # in bits per second
+        description: Specifies the throughput requirement in bits per second of leaf connections to the link when applicable to the connectivity type (e.g. for E-Tree and E LAN branches).
+        required: false
+        constraints:
+          - greater_or_equal: 0
+
+  tosca.datatypes.nfv.CpProtocolData:
+    derived_from: tosca.datatypes.Root
+    description: Describes and associates the protocol layer that a CP uses together with other protocol and connection point information
+    properties:
+      associated_layer_protocol:
+        type: string
+        required: true
+        description: One of the values of the property layer_protocols of the CP 
+        constraints:
+          - valid_values: [ ethernet, mpls, odu2, ipv4, ipv6, pseudo-wire ]
+      address_data:
+        type: list
+        description: Provides information on the addresses to be assigned to the CP
+        entry_schema: 
+          type: tosca.datatypes.nfv.AddressData
+        required: false
+
+  tosca.datatypes.nfv.VnfProfile:
+    derived_from: tosca.datatypes.Root
+    description: describes a profile for instantiating VNFs of a particular NS DF according to a specific VNFD and VNF DF.
+    properties:
+      instantiation_level:
+        type: string
+        description: Identifier of the instantiation level of the VNF DF to be used for instantiation. If not present, the default instantiation level as declared in the VNFD shall be used.
+        required: false
+      min_number_of_instances:
+        type: integer
+        description: Minimum number of instances of the VNF based on this VNFD that is permitted to exist for this VnfProfile.
+        required: true
+        constraints:
+          - greater_or_equal: 0
+      max_number_of_instances:
+        type: integer
+        description: Maximum number of instances of the VNF based on this VNFD that is permitted to exist for this VnfProfile.
+        required: true
+        constraints:
+          - greater_or_equal: 0
+
+  tosca.datatypes.nfv.Qos:
+    derived_from: tosca.datatypes.Root
+    description: describes QoS data for a given VL used in a VNF deployment flavour 
+    properties:
+      latency:
+        type: scalar-unit.time #Number
+        description: Specifies the maximum latency 
+        required: true
+        constraints: 
+          - greater_than: 0 s
+      packet_delay_variation:
+        type: scalar-unit.time #Number
+        description: Specifies the maximum jitter  
+        required: true
+        constraints: 
+          - greater_or_equal: 0 s
+      packet_loss_ratio:
+        type: float
+        description: Specifies the maximum packet loss ratio 
+        required: false
+        constraints:
+           - in_range: [ 0.0, 1.0 ]
+
+  tosca.datatypes.nfv.VnfMonitoringParameter:
+    derived_from: tosca.datatypes.Root
+    description: Represents information on virtualised resource related performance metrics applicable to the VNF.
+    properties:
+      name:
+       type: string
+       description: Human readable name of the monitoring parameter
+       required: true
+      performance_metric:
+       type: string
+       description: Identifies a performance metric to be monitored, according to ETSI GS NFV-IFA 027. 
+       required: true
+       constraints:
+        - valid_values: [ v_cpu_usage_mean_vnf, v_cpu_usage_peak_vnf, v_memory_usage_mean_vnf, v_memory_usage_peak_vnf, v_disk_usage_mean_vnf, v_disk_usage_peak_vnf, byte_incoming_vnf_ext_cp, byte_outgoing_vnf_ext_cp, packet_incoming_vnf_ext_cp, packet_outgoing_vnf_ext_cp ] 
+      collection_period:
+       type: scalar-unit.time
+       description: Describes the periodicity at which to collect the performance information. 
+       required: false
+       constraints: 
+        - greater_than: 0 s
+        
+capability_types:
+  tosca.capabilities.nfv.VirtualLinkable:
+    derived_from: tosca.capabilities.Node
+    description: A node type that includes the VirtualLinkable capability indicates that it can be pointed by tosca.relationships.nfv.VirtualLinksTo relationship type
+
+  tosca.capabilities.nfv.Forwarding:
+    derived_from: tosca.capabilities.Root
+relationship_types:
+  tosca.relationships.nfv.VirtualLinksTo:
+    derived_from: tosca.relationships.DependsOn
+    description: Represents an association relationship between the VduCp and VnfVirtualLink node types or the association between either a VnfExtCp, a PnfExtCp or a Sap and an NsVirtualLink node types. 
+    valid_target_types: [ tosca.capabilities.nfv.VirtualLinkable ]
+
+  tosca.relationships.nfv.ForwardTo:
+    derived_from: tosca.relationships.Root
+    valid_target_types: [ tosca.capabilities.nfv.Forwarding ]
+
+  tosca.relationships.nfv.VipVirtualLinksTo:
+    derived_from: tosca.relationships.DependsOn
+    description: Represents an association relationship between the VipCp and a VnfVirtualLink node types or between the former and a NsVirtualLink node types. 
+    valid_target_types: [ tosca.capabilities.nfv.VirtualLinkable ]
+    
+node_types:
+  tosca.nodes.nfv.Cp:
+    derived_from: tosca.nodes.Root
+    description:  Provides information regarding the purpose of the connection point 
+    properties:
+      layer_protocols:
+        type: list
+        description: Identifies which protocol the connection point uses for connectivity purposes 
+        required: true
+        entry_schema:
+          type: string
+          constraints:
+            - valid_values: [ ethernet, mpls, odu2, ipv4, ipv6, pseudo-wire ]
+      role: #Name in ETSI NFV IFA011: cpRole
+        type: string
+        description: Identifies the role of the port in the context of the traffic flow patterns in the VNF or parent NS 
+        required: false
+        constraints:
+          - valid_values: [ root, leaf ]
+      description:
+        type: string
+        description: Provides human-readable information on the purpose of the connection point 
+        required: false
+      protocol:
+        type: list
+        description: Provides information on the addresses to be assigned to the connection point(s) instantiated from this Connection Point Descriptor 
+        required: false
+        entry_schema:
+          type: tosca.datatypes.nfv.CpProtocolData
+      trunk_mode:
+        type: boolean
+        description: Provides information about whether the CP instantiated from this Cp is in Trunk mode (802.1Q or other), When operating in "trunk mode", the Cp is capable of carrying traffic for several VLANs. Absence of this property implies that trunkMode is not configured for the Cp i.e. It is equivalent to boolean value "false".
+        required: false
+
+policy_types:
+  tosca.policies.nfv.Abstract.SecurityGroupRule:
+    derived_from: tosca.policies.Root
+    description: The Abstract.SecurityGroupRule type represents an abstract policy type without any target requirements
+    properties:
+      description:
+        type: string
+        description: Human readable description of the security group rule.
+        required: false
+      direction:
+        type: string
+        description: The direction in which the security group rule is applied. The direction of 'ingress' or 'egress' is specified against the associated CP. I.e., 'ingress' means the packets entering a CP, while 'egress' means the packets sent out of a CP.
+        required: false
+        constraints:
+          - valid_values: [ ingress, egress ]
+        default: ingress
+      ether_type:
+        type: string
+        description: Indicates the protocol carried over the Ethernet layer.
+        required: false
+        constraints:
+          - valid_values: [ ipv4, ipv6 ]
+        default: ipv4
+      protocol:
+        type: string
+        description: Indicates the protocol carried over the IP layer. Permitted values include any protocol defined in the IANA protocol registry, e.g. TCP, UDP, ICMP, etc.
+        required: false
+        constraints:
+          - valid_values: [ hopopt, icmp, igmp, ggp, ipv4, st, tcp, cbt, egp, igp, bbn_rcc_mon, nvp_ii, pup, argus, emcon, xnet, chaos, udp, mux, dcn_meas, hmp, prm, xns_idp, trunk_1, trunk_2, leaf_1, leaf_2, rdp, irtp, iso_tp4, netblt, mfe_nsp, merit_inp, dccp, 3pc, idpr, xtp, ddp, idpr_cmtp, tp++, il, ipv6, sdrp, ipv6_route, ipv6_frag, idrp, rsvp, gre, dsr, bna, esp, ah, i_nlsp, swipe, narp, mobile, tlsp, skip, ipv6_icmp, ipv6_no_nxt, ipv6_opts, cftp, sat_expak, kryptolan, rvd, ippc, sat_mon, visa, ipcv, cpnx, cphb, wsn, pvp, br_sat_mon, sun_nd, wb_mon, wb_expak, iso_ip, vmtp, secure_vmtp, vines, ttp, iptm, nsfnet_igp, dgp, tcf, eigrp, ospfigp, sprite_rpc, larp, mtp, ax.25, ipip, micp, scc_sp, etherip, encap, gmtp, ifmp, pnni, pim, aris, scps, qnx, a/n, ip_comp, snp, compaq_peer, ipx_in_ip, vrrp, pgm, l2tp, ddx, iatp, stp, srp, uti, smp, sm, ptp, isis, fire, crtp, crudp, sscopmce, iplt, sps, pipe, sctp, fc, rsvp_e2e_ignore, mobility, udp_lite, mpls_in_ip, manet, hip, shim6, wesp, rohc ]
+        default: tcp
+      port_range_min:
+        type: integer
+        description: Indicates minimum port number in the range that is matched by the security group rule. If a value is provided at design-time, this value may be overridden at run-time based on other deployment requirements or constraints.
+        required: false
+        constraints:
+          - greater_or_equal: 0
+          - less_or_equal: 65535
+        default: 0
+      port_range_max:
+        type: integer
+        description: Indicates maximum port number in the range that is matched by the security group rule. If a value is provided at design-time, this value may be overridden at run-time based on other deployment requirements or constraints.
+        required: false
+        constraints:
+          - greater_or_equal: 0
+          - less_or_equal: 65535
+        default: 65535
+
diff --git a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/resources/etsi-nfv-types/etsi_nfv_sol001_nsd_2_7_1_types.yaml b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/resources/etsi-nfv-types/etsi_nfv_sol001_nsd_2_7_1_types.yaml
new file mode 100644 (file)
index 0000000..5d24beb
--- /dev/null
@@ -0,0 +1,450 @@
+tosca_definitions_version: tosca_simple_yaml_1_2
+description: ETSI NFV SOL 001 nsd types definitions version 2.7.1
+metadata:
+  template_name: etsi_nfv_sol001_nsd_types
+  template_author: ETSI_NFV
+  template_version: 2.7.1
+
+imports:
+  - file: etsi_nfv_sol001_common_types.yaml
+
+data_types:
+  tosca.datatypes.nfv.ServiceAvailability:
+    derived_from: tosca.datatypes.Root
+    description: service availability 
+    properties:
+      level:
+        type: string
+        description: service availability levels
+        required: true
+        constraints:
+          - valid_values: [ level1, level2, level3 ]
+
+  tosca.datatypes.nfv.NsVlProfile:
+    derived_from: tosca.datatypes.Root
+    description: Describes additional instantiation data for a given NsVirtualLink used in a specific NS deployment flavour.
+    properties:
+      max_bitrate_requirements:
+        type: tosca.datatypes.nfv.LinkBitrateRequirements
+        description: Specifies the maximum bitrate requirements for a VL instantiated according to this profile.
+        required: true
+      min_bitrate_requirements:
+        type: tosca.datatypes.nfv.LinkBitrateRequirements
+        description: Specifies the minimum bitrate requirements for a VL instantiated according to this profile.
+        required: true
+      qos:
+        type: tosca.datatypes.nfv.NsVirtualLinkQos
+        description: Specifies the QoS requirements of a VL instantiated according to this profile.
+        required: false
+      service_availability:
+        type: tosca.datatypes.nfv.ServiceAvailability
+        description: Network service virtual link service availability levels, as described in ETSI GS NFV-REL 001
+        required: false 
+
+  tosca.datatypes.nfv.NsVirtualLinkQos:
+    derived_from: tosca.datatypes.nfv.Qos
+    description: describes QoS data for a given VL used in a VNF deployment flavour 
+    properties:
+      priority:
+        type: integer
+        constraints:
+          - greater_or_equal: 0
+        description: Specifies the priority level in case of congestion on the underlying physical links
+        required: false
+
+  tosca.datatypes.nfv.NsProfile:
+    derived_from: tosca.datatypes.Root
+    description: describes a profile for instantiating NSs of a particular NS DF according to a specific NSD and NS DF.
+    properties: 
+      ns_instantiation_level:
+        type: string
+        description: Identifier of the instantiation level of the NS DF to be used for instantiation. If not present, the default instantiation level as declared in the NSD shall be used.
+        required: false
+      min_number_of_instances:
+        type: integer
+        description: Minimum number of instances of the NS based on this NSD that is permitted to exist for this NsProfile.
+        required: true
+        constraints:
+          - greater_or_equal: 0
+      max_number_of_instances:
+        type: integer
+        description: Maximum number of instances of the NS based on this NSD that is permitted to exist for this NsProfile.
+        required: true
+        constraints:
+          - greater_or_equal: 0
+      flavour_id:
+        type: string
+        description: Identifies the applicable network service DF within the scope of the NSD.
+        required: true
+
+  tosca.datatypes.nfv.Mask:
+    derived_from: tosca.datatypes.Root
+    properties:  
+      starting_point: 
+        description: Indicates the offset between the last bit of the source mac address and the first bit of the sequence of bits to be matched.
+        type: integer
+        required: true    
+      length: 
+        description: Indicates the number of bits to be matched.
+        type: integer
+        required: true
+      value:
+        description: Provide the sequence of bit values to be matched.
+        type: string
+        required: true 
+
+  tosca.datatypes.nfv.NsOperationAdditionalParameters:
+    derived_from: tosca.datatypes.Root
+    description: Is an empty base type for deriving data types for describing NS-specific additional parameters to be passed when invoking NS lifecycle management operations 
+    #properties:
+  tosca.datatypes.nfv.NsMonitoringParameter:
+    derived_from: tosca.datatypes.Root
+    description: Represents information on virtualised resource related performance metrics applicable to the NS.
+    properties:
+      name:
+        type: string
+        description: Human readable name of the monitoring parameter
+        required: true
+      performance_metric:
+        type: string
+        description: Identifies a performance metric to be monitored, according to ETSI GS NFV-IFA 027. 
+        required: true
+        constraints:
+           - valid_values: [byte_incoming_sap, byte_outgoing_sap, packet_incoming_sap, packet_outgoing_sap, byte_incoming, byte_outgoing, packet_incoming, packet_outgoing ] 
+      collection_period:  
+        type: scalar-unit.time
+        description: Describes the periodicity at which to collect the performance information.
+        required: false
+
+interface_types:
+  tosca.interfaces.nfv.Nslcm:
+    derived_from: tosca.interfaces.Root
+    description: This interface encompasses a set of TOSCA operations corresponding to NS LCM operations defined in ETSI GS NFV-IFA 013. as well as to preamble and postamble procedures to the execution of the NS LCM operations.
+    instantiate_start:
+      description: Preamble to execution of the instantiate operation
+    instantiate:
+      description: Base procedure for instantiating an NS, corresponding to the Instantiate NS operation defined in ETSI GS NFV-IFA 013.
+      # inputs:
+        # additional_parameters:
+        #   type: tosca.datatypes.nfv.NsOperationAdditionalParameters
+        #   required: false 
+    instantiate_end:
+      description: Postamble to the execution of the instantiate operation
+    terminate_start:
+      description: Preamble to execution of the terminate operation
+    terminate:
+      description: Base procedure for terminating an NS, corresponding to the Terminate NS operation defined in ETSI GS NFV-IFA 013. 
+    terminate_end:
+      description: Postamble to the execution of the terminate operation
+    update_start:
+      description: Preamble to execution of the update operation
+    update:
+      description: Base procedure for updating an NS, corresponding to the Update NS operation defined in ETSI GS NFV-IFA 013. 
+    update_end:
+      description: Postamble to the execution of the update operation
+    scale_start:
+      description: Preamble to execution of the scale operation
+    scale:
+      description: Base procedure for scaling an NS, corresponding to the Scale NS operation defined in ETSI GS NFV-IFA 013.
+      # inputs:
+        # additional_parameters:
+        #   type: tosca.datatypes.nfv.NsOperationAdditionalParameters
+        #   required: false 
+    scale_end:
+      description: Postamble to the execution of the scale operation
+    heal_start:
+      description: Preamble to execution of the heal operation
+    heal:
+      description: Base procedure for healing an NS, corresponding to the Heal NS operation defined in ETSI GS NFV-IFA 013.
+      # inputs:
+        # additional_parameters:
+        #   type: tosca.datatypes.nfv.NsOperationAdditionalParameters
+        #   required: false 
+    heal_end:
+      description: Postamble to the execution of the heal operation
+
+node_types:
+  tosca.nodes.nfv.NS:
+    derived_from: tosca.nodes.Root
+    properties: 
+      descriptor_id: 
+        type: string # UUID
+        description: Identifier of this NS descriptor
+        required: true
+      designer:
+        type: string
+        description: Identifies the designer of the NSD.
+        required: true
+      version:
+        type: string
+        description: Identifies the version of the NSD.
+        required: true
+      name:
+        type: string
+        description: Provides the human readable name of the NSD.
+        required: true
+      invariant_id: # UUID
+        type: string
+        description: Identifies an NSD in a version independent manner. This attribute is invariant across versions of NSD
+        required: true
+      flavour_id:
+        type: string
+        description: Identifier of the NS Deployment Flavour within the NSD
+        required: true
+      ns_profile:
+        type: tosca.datatypes.nfv.NsProfile
+        description: Specifies a profile of a NS, when this NS is used as nested NS within another NS.
+        required: false
+    requirements:
+      - virtual_link:
+          capability: tosca.capabilities.nfv.VirtualLinkable
+          relationship: tosca.relationships.nfv.VirtualLinksTo
+          node: tosca.nodes.nfv.NsVirtualLink
+          occurrences: [ 0, 1 ]
+    interfaces:
+      Nslcm:
+        type: tosca.interfaces.nfv.Nslcm
+
+  tosca.nodes.nfv.Sap:
+    derived_from: tosca.nodes.nfv.Cp
+    description: node definition of SAP.
+    requirements:
+      - external_virtual_link:
+          capability: tosca.capabilities.nfv.VirtualLinkable
+          relationship: tosca.relationships.nfv.VirtualLinksTo
+          occurrences: [0, 1]
+      - internal_virtual_link:
+          capability: tosca.capabilities.nfv.VirtualLinkable
+          relationship: tosca.relationships.nfv.VirtualLinksTo
+          occurrences: [1, 1]
+
+  tosca.nodes.nfv.NsVirtualLink:
+    derived_from: tosca.nodes.Root
+    description: node definition of Virtual Links
+    properties: 
+      vl_profile:
+        type: tosca.datatypes.nfv.NsVlProfile # only covers min/max bitrate requirements
+        description: Specifies instantiation parameters for a virtual link of a particular NS deployment flavour.
+        required: true
+      connectivity_type:
+        type: tosca.datatypes.nfv.ConnectivityType
+        required: true
+      test_access:
+        type: list
+        description: Test access facilities available on the VL 
+        required: false
+        entry_schema:
+          type: string
+          constraints:
+            - valid_values: [ passive_monitoring, active_loopback ]
+      description:
+        type: string
+        required: false
+        description: Human readable information on the purpose of the virtual link (e.g. VL for control plane traffic).
+    capabilities:
+      virtual_linkable:
+        type: tosca.capabilities.nfv.VirtualLinkable
+
+  tosca.nodes.nfv.NfpPositionElement:
+    derived_from: tosca.nodes.Root
+    description: node definition of NfpPositionElement
+    capabilities:
+      forwarding: 
+        type: tosca.capabilities.nfv.Forwarding
+    requirements:
+      - profile_element:
+          capability: tosca.capabilities.nfv.Forwarding
+          relationship: tosca.relationships.nfv.ForwardTo
+          occurrences: [ 1, 2 ] # When the number of occurrences is 1, the ingress and egress traffic is associated to a single VnfExtCp or Sap; When the number of occurrences is 2, the ingress VnfExtCp or Sap is associated to the first value and the egress VnfExtCp or Sap is associated to the second value.
+
+  tosca.nodes.nfv.NfpPosition:
+    derived_from: tosca.nodes.Root
+    description: node definition of NFP position
+    properties:
+      forwarding_behaviour: 
+        type: string
+        description: Identifies a rule to apply to forward traffic to CP or SAP instances corresponding to the referenced NfpPositionElement(s). 
+        constraints:
+          - valid_values: [ all, lb, ff ]
+        required: false
+#     forwarding_behaviour_input_parameters: 
+#       description: Provides input parameters to configure the forwarding behaviour.
+#       type: map
+#       required: false
+#       entry_schema:
+#         type: strin
+
+    capabilities:
+      forwarding: 
+        type: tosca.capabilities.nfv.Forwarding
+    requirements:
+      - element:
+          capability: tosca.capabilities.nfv.Forwarding
+          node: tosca.nodes.nfv.NfpPositionElement
+          relationship: tosca.relationships.nfv.ForwardTo
+          occurrences: [ 1, UNBOUNDED ]
+
+  tosca.nodes.nfv.NFP:
+    derived_from: tosca.nodes.Root
+    description: node definition of NFP
+    requirements:
+      - nfp_position:
+          capability: tosca.capabilities.nfv.Forwarding
+          node: tosca.nodes.nfv.NfpPosition
+          relationship: tosca.relationships.nfv.ForwardTo
+          occurrences: [ 1, UNBOUNDED ]
+
+  tosca.nodes.nfv.Forwarding:
+    derived_from: tosca.nodes.Root
+    capabilities:
+      virtual_linkable:
+        type: tosca.capabilities.nfv.VirtualLinkable
+      forwarding:
+        type: tosca.capabilities.nfv.Forwarding
+        occurrences: [ 1, 2 ]  #When the number of occurrences is 1, the ingress and egress traffic is associated to a single VnfExtCp, PnfExtCp or Sap; When the number of occurrences is 2, the ingress VnfExtCp, PnfExtCp or Sap is associated to the first value and the egress VnfExtCp, PnfExtCp or Sap is associated to the second value.
+    requirements:
+      - virtual_link:
+          capability: tosca.capabilities.nfv.VirtualLinkable
+          relationship: tosca.relationships.nfv.VirtualLinksTo
+
+group_types:
+  tosca.groups.nfv.NsPlacementGroup:
+    derived_from: tosca.groups.Root
+    description: NsPlacementGroup is used for describing the affinity or anti-affinity relationship applicable between VNF instances created using different VNFDs, the Virtual Link instances created using different VLDs or the nested NS instances created using different NSDs when used in a NSD.
+    properties:
+      description:
+        type: string
+        description: Human readable description of the group
+        required: true
+    members: [tosca.nodes.nfv.VNF, tosca.nodes.nfv.NsVirtualLink, tosca.nodes.nfv.NS]
+
+  tosca.groups.nfv.VNFFG:
+    derived_from: tosca.groups.Root
+    description: the VNFFG group type describes a topology of the NS or a portion of the NS, and optionally forwarding rules, applicable to the traffic conveyed over this topology
+    properties:
+      description:
+        type: string
+        description: Human readable description of the group
+        required: true
+    members: [ tosca.nodes.nfv.NFP, tosca.nodes.nfv.VNF, tosca.nodes.nfv.PNF, tosca.nodes.nfv.NS, tosca.nodes.nfv.NsVirtualLink, tosca.nodes.nfv.NfpPositionElement ]
+
+
+policy_types:
+  tosca.policies.nfv.NsAffinityRule:
+    derived_from: tosca.policies.Placement
+    description: The NsAffinityRule describes the affinity rules applicable for the defined targets
+    properties:
+      scope:
+        type: string
+        description: Specifies the scope of the local affinity rule.
+        required: true
+        constraints:
+          - valid_values: [ nfvi_node, zone, zone_group, nfvi_pop ]
+    targets: [tosca.nodes.nfv.VNF, tosca.nodes.nfv.NsVirtualLink, tosca.nodes.nfv.NS, tosca.groups.nfv.NsPlacementGroup ]
+
+  tosca.policies.nfv.NsAntiAffinityRule:
+    derived_from: tosca.policies.Placement
+    description: The NsAntiAffinityRule describes the anti-affinity rules applicable for the defined targets
+    properties:
+      scope:
+        type: string
+        description: Specifies the scope of the local affinity rule..
+        required: true
+        constraints:
+          - valid_values: [ nfvi_node, zone, zone_group, nfvi_pop ]
+    targets: [tosca.nodes.nfv.VNF, tosca.nodes.nfv.NsVirtualLink, tosca.nodes.nfv.NS, tosca.groups.nfv.NsPlacementGroup ]
+
+  tosca.policies.nfv.NsSecurityGroupRule:
+    derived_from: tosca.policies.nfv.Abstract.SecurityGroupRule
+    description: The NsSecurityGroupRule type is a policy type specified the matching criteria for the ingress and/or egress traffic to/from visited SAPs.
+    targets: [ tosca.nodes.nfv.Sap ]
+
+  tosca.policies.nfv.NfpRule:
+    derived_from: tosca.policies.Root
+    description: policy definition of NfpRule
+    properties:
+      ether_destination_address:
+        description: Indicates a destination Mac address.
+        type: string
+        required: false    
+      ether_source_address:
+        description: Indicates a source Mac address.
+        type: string
+        required: false
+      ether_type:
+        description: Indicates the protocol carried over the Ethernet layer.
+        type: string
+        constraints:
+          - valid_values: [ ipv4, ipv6 ]
+        required: false
+      vlan_tag:
+        description: Indicates a VLAN identifier in an IEEE 802.1Q-2014 tag [14]. Multiple tags can be included for QinQ stacking.
+        type: list
+        entry_schema:
+          type: string
+        required: false            
+      protocol:
+        description: 'Indicates the L4 protocol, For IPv4 [15] this corresponds to the field called "Protocol" to identify the next level protocol. For IPv6 [16] this corresponds to the field is called the "Next Header" field. Permitted values: Any keyword defined in the IANA [17] protocol registry.'
+        type: string
+        required: false
+      dscp:
+        description: For IPv4 [15] a string of "0" and "1" digits that corresponds to the 6-bit Differentiated Services Code Point (DSCP) field of the IP header. For IPv6 [16] a string of "0" and "1" digits that corresponds to the 6 differentiated services bits of the traffic class header field.
+        type: string
+        required: false
+      source_port_range:
+        description: Indicates a range of source ports.
+        type: range
+        required: false
+        constraints:
+          - in_range: [0, 65535]
+      destination_port_range:
+        description: Indicates a range of destination ports.
+        type: range
+        required: false
+        constraints:
+          - in_range: [0, 65535]
+      source_ip_address_prefix:
+        description: Indicates the source IP address range in CIDR format.
+        type: string
+        required: false   
+      destination_ip_address_prefix:
+        description: Indicates the destination IP address range in CIDR format.
+        type: string
+        required: false
+      extended_criteria:
+        description: Indicates values of specific bits in a frame.
+        type: list
+        entry_schema:
+          type: tosca.datatypes.nfv.Mask
+        required: false
+    targets: [ tosca.nodes.nfv.NFP ]
+
+  tosca.policies.nfv.NsMonitoring:
+    derived_from: tosca.policies.Root
+    description: Policy type is used to identify information to be monitored during the lifetime of a network service instance as defined in ETSI GS NFV-IFA 014 [2].
+    properties:
+      ns_monitoring_parameters:
+        type: list
+        description: Specifies a virtualised resource related performance metric to be monitored on the NS level.
+        required: true
+        entry_schema:
+          type: tosca.datatypes.nfv.NsMonitoringParameter
+        constraints:
+          - min_length: 1
+    targets: [ tosca.nodes.nfv.NS ]
+
+  tosca.policies.nfv.VnfMonitoring:
+    derived_from: tosca.policies.Root
+    description: Policy type is used to identify information to be monitored during the lifetime of a VNF instance as defined in ETSI GS NFV-IFA 014 [2].
+    properties:
+      vnf_monitoring_parameters:
+        type: list
+        description: Specifies a virtualised resource related performance metric to be monitored on the NS level.
+        required: true
+        entry_schema:
+          type: tosca.datatypes.nfv.VnfMonitoringParameter
+        constraints:
+          - min_length: 1
+    targets: [ tosca.nodes.nfv.VNF ]
+
diff --git a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/test/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/EtsiNfvNsCsarEntryGeneratorTest.java b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/test/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/EtsiNfvNsCsarEntryGeneratorTest.java
new file mode 100644 (file)
index 0000000..e02e170
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.plugins.etsi.nfv.nsd.generator;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.anEmptyMap;
+import static org.hamcrest.Matchers.hasEntry;
+import static org.hamcrest.core.Is.is;
+import static org.mockito.Mockito.when;
+import static org.openecomp.sdc.be.plugins.etsi.nfv.nsd.generator.EtsiNfvNsCsarEntryGenerator.ETSI_NS_COMPONENT_CATEGORY;
+import static org.openecomp.sdc.be.plugins.etsi.nfv.nsd.generator.EtsiNfvNsCsarEntryGenerator.NSD_FILE_PATH_FORMAT;
+import static org.openecomp.sdc.common.api.ArtifactTypeEnum.ETSI_PACKAGE;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.exception.NsdException;
+
+class EtsiNfvNsCsarEntryGeneratorTest {
+
+
+    @Mock
+    private EtsiNfvNsdCsarGenerator etsiNfvNsdCsarGenerator;
+    @Mock
+    private Service service;
+    @InjectMocks
+    private EtsiNfvNsCsarEntryGenerator etsiNfvNsCsarEntryGenerator;
+
+    private static final String SERVICE_NORMALIZED_NAME = "normalizedName";
+    private static final String CSAR_ENTRY_EMPTY_ASSERT = "Csar Entries should be empty";
+
+    @BeforeEach
+    void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    void successfullyEntryGenerationTest() throws NsdException {
+        mockServiceComponent();
+        final byte[] expectedNsdCsar = new byte[5];
+        when(etsiNfvNsdCsarGenerator.generateNsdCsar(service)).thenReturn(expectedNsdCsar);
+
+        final Map<String, byte[]> entryMap = etsiNfvNsCsarEntryGenerator.generateCsarEntries(service);
+
+        assertThat("Csar Entries should contain only one entry", entryMap.size(), is(1));
+        assertThat("Csar Entries should contain the expected entry", entryMap,
+            hasEntry(String.format(NSD_FILE_PATH_FORMAT, ETSI_PACKAGE, SERVICE_NORMALIZED_NAME), expectedNsdCsar));
+    }
+
+    @Test
+    void knownNsdGenerationErrorTest() throws NsdException {
+        mockServiceComponent();
+        when(etsiNfvNsdCsarGenerator.generateNsdCsar(service)).thenThrow(new NsdException(""));
+        final Map<String, byte[]> entryMap = etsiNfvNsCsarEntryGenerator.generateCsarEntries(service);
+
+        assertThat(CSAR_ENTRY_EMPTY_ASSERT, entryMap, is(anEmptyMap()));
+    }
+
+    @Test
+    void unknownNsdGenerationErrorTest() throws NsdException {
+        mockServiceComponent();
+        when(etsiNfvNsdCsarGenerator.generateNsdCsar(service)).thenThrow(new RuntimeException());
+        final Map<String, byte[]> entryMap = etsiNfvNsCsarEntryGenerator.generateCsarEntries(service);
+
+        assertThat(CSAR_ENTRY_EMPTY_ASSERT, entryMap, is(anEmptyMap()));
+    }
+
+    @Test
+    void componentNullOrNotAServiceTest() {
+        Map<String, byte[]> entryMap = etsiNfvNsCsarEntryGenerator.generateCsarEntries(service);
+        assertThat(CSAR_ENTRY_EMPTY_ASSERT, entryMap, is(anEmptyMap()));
+
+        entryMap = etsiNfvNsCsarEntryGenerator.generateCsarEntries(null);
+        assertThat(CSAR_ENTRY_EMPTY_ASSERT, entryMap, is(anEmptyMap()));
+    }
+
+    @Test
+    void componentNotExpectedCategoryTest() {
+        when(service.getComponentType()).thenReturn(ComponentTypeEnum.SERVICE);
+        final List<CategoryDefinition> categoryDefinitionList = new ArrayList<>();
+        final CategoryDefinition nsComponentCategoryDefinition = new CategoryDefinition();
+        nsComponentCategoryDefinition.setName("notExpectedCategory");
+        categoryDefinitionList.add(nsComponentCategoryDefinition);
+        when(service.getCategories()).thenReturn(categoryDefinitionList);
+        final Map<String, byte[]> entryMap = etsiNfvNsCsarEntryGenerator.generateCsarEntries(service);
+        assertThat(CSAR_ENTRY_EMPTY_ASSERT, entryMap, is(anEmptyMap()));
+    }
+
+    private void mockServiceComponent() {
+        when(service.getName()).thenReturn("anyName");
+        when(service.getComponentType()).thenReturn(ComponentTypeEnum.SERVICE);
+        when(service.getNormalizedName()).thenReturn(SERVICE_NORMALIZED_NAME);
+
+        final List<CategoryDefinition> categoryDefinitionList = new ArrayList<>();
+        final CategoryDefinition nsComponentCategoryDefinition = new CategoryDefinition();
+        nsComponentCategoryDefinition.setName(ETSI_NS_COMPONENT_CATEGORY);
+        categoryDefinitionList.add(nsComponentCategoryDefinition);
+        when(service.getCategories()).thenReturn(categoryDefinitionList);
+    }
+}
\ No newline at end of file
diff --git a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/test/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/EtsiNfvNsdCsarGeneratorImplTest.java b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/test/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/EtsiNfvNsdCsarGeneratorImplTest.java
new file mode 100644 (file)
index 0000000..9144140
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.plugins.etsi.nfv.nsd.generator;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.openecomp.sdc.be.plugins.etsi.nfv.nsd.generator.EtsiNfvNsCsarEntryGenerator.ETSI_NS_COMPONENT_CATEGORY;
+import static org.openecomp.sdc.common.api.ArtifactTypeEnum.ONBOARDED_PACKAGE;
+
+import fj.data.Either;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.openecomp.sdc.be.dao.cassandra.ArtifactCassandraDao;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.JsonPresentationFields;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.exception.NsdException;
+import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.exception.VnfDescriptorException;
+import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.model.Nsd;
+import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.model.VnfDescriptor;
+import org.openecomp.sdc.be.resources.data.DAOArtifactData;
+
+class EtsiNfvNsdCsarGeneratorImplTest {
+
+    @Mock
+    private VnfDescriptorGenerator vnfDescriptorGenerator;
+    @Mock
+    private NsDescriptorGenerator nsDescriptorGeneratorImpl;
+    @Mock
+    private ArtifactCassandraDao artifactCassandraDao;
+    @InjectMocks
+    private EtsiNfvNsdCsarGeneratorImpl etsiNfvNsdCsarGenerator;
+    @Mock
+    private Service service;
+
+    private static final String SERVICE_NORMALIZED_NAME = "normalizedName";
+
+    @BeforeEach
+    void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    void generateNsdCsarSuccessfulTest() throws VnfDescriptorException, NsdException {
+        mockServiceComponent();
+        mockServiceComponentArtifacts();
+        final byte[] nsdCsar = etsiNfvNsdCsarGenerator.generateNsdCsar(service);
+        assertThat("", nsdCsar, is(notNullValue()));
+    }
+
+    @Test()
+    void invalidComponentTest() {
+        assertThrows(NsdException.class, () -> etsiNfvNsdCsarGenerator.generateNsdCsar(null));
+    }
+
+    private void mockServiceComponent() throws VnfDescriptorException, NsdException {
+        when(service.getNormalizedName()).thenReturn(SERVICE_NORMALIZED_NAME);
+        when(service.getComponentType()).thenReturn(ComponentTypeEnum.SERVICE);
+        final String componentInstance1Name = "componentInstance1";
+        final ComponentInstance componentInstance1 = mockServiceComponentInstance(componentInstance1Name);
+        final ArtifactDefinition instanceArtifact1 = mockComponentInstanceArtifact(componentInstance1,
+            "instanceArtifact1");
+
+        final VnfDescriptor vnfDescriptor1 = new VnfDescriptor();
+        final List<VnfDescriptor> vnfDescriptorList = Collections.singletonList(vnfDescriptor1);
+        final Nsd nsd = new Nsd();
+        when(vnfDescriptorGenerator.generate(componentInstance1Name, instanceArtifact1))
+            .thenReturn(Optional.of(vnfDescriptor1));
+        when(nsDescriptorGeneratorImpl.generate(service, vnfDescriptorList)).thenReturn(Optional.of(nsd));
+
+        final List<CategoryDefinition> categoryDefinitionList = new ArrayList<>();
+        final CategoryDefinition nsComponentCategoryDefinition = new CategoryDefinition();
+        nsComponentCategoryDefinition.setName(ETSI_NS_COMPONENT_CATEGORY);
+        categoryDefinitionList.add(nsComponentCategoryDefinition);
+        when(service.getCategories()).thenReturn(categoryDefinitionList);
+    }
+
+    private void mockServiceComponentArtifacts() {
+        final Map<String, ArtifactDefinition> allArtifactsMap = new HashMap<>();
+        final String artifact1Id = "artifact1";
+        final ArtifactDefinition artifact1 = mockArtifactDefinition(artifact1Id);
+        final byte[] artifact1Bytes = new byte[1];
+        allArtifactsMap.put(artifact1Id, artifact1);
+        when(service.getAllArtifacts()).thenReturn(allArtifactsMap);
+        final DAOArtifactData artifact1Data = new DAOArtifactData();
+        artifact1Data.setDataAsArray(artifact1Bytes);
+        when(artifactCassandraDao.getArtifact(artifact1Id)).thenReturn(Either.left(artifact1Data));
+    }
+
+    private ComponentInstance mockServiceComponentInstance(final String componentInstanceName) {
+        final Map<String, ArtifactDefinition> deploymentArtifactMap = new HashMap<>();
+        final String instanceArtifact1Id = "instanceArtifact1";
+        final ArtifactDefinition instanceArtifact1 = mockArtifactDefinition(instanceArtifact1Id);
+        instanceArtifact1.setToscaPresentationValue(JsonPresentationFields.ARTIFACT_TYPE, ONBOARDED_PACKAGE.getType());
+        deploymentArtifactMap.put(instanceArtifact1Id, instanceArtifact1);
+        DAOArtifactData instanceArtifact1Data = new DAOArtifactData();
+        final byte[] instanceArtifact1Bytes = new byte[1];
+        instanceArtifact1Data.setDataAsArray(instanceArtifact1Bytes);
+        when(artifactCassandraDao.getArtifact(instanceArtifact1Id)).thenReturn(Either.left(instanceArtifact1Data));
+        final ComponentInstance componentInstance = mock(ComponentInstance.class);
+        when(componentInstance.getDeploymentArtifacts()).thenReturn(deploymentArtifactMap);
+        when(componentInstance.getName()).thenReturn(componentInstanceName);
+        final List<ComponentInstance> componentInstanceList = new ArrayList<>();
+        componentInstanceList.add(componentInstance);
+        when(service.getComponentInstances()).thenReturn(componentInstanceList);
+
+        return componentInstance;
+    }
+
+    private ArtifactDefinition mockComponentInstanceArtifact(final ComponentInstance componentInstance,
+                                                             final String instanceArtifactId) {
+        final Map<String, ArtifactDefinition> deploymentArtifactMap = new HashMap<>();
+        when(componentInstance.getDeploymentArtifacts()).thenReturn(deploymentArtifactMap);
+
+        final ArtifactDefinition instanceArtifact1 = mockArtifactDefinition(instanceArtifactId);
+        instanceArtifact1.setToscaPresentationValue(JsonPresentationFields.ARTIFACT_TYPE, ONBOARDED_PACKAGE.getType());
+        deploymentArtifactMap.put(instanceArtifactId, instanceArtifact1);
+        DAOArtifactData instanceArtifact1Data = new DAOArtifactData();
+        final byte[] instanceArtifact1Bytes = new byte[1];
+        instanceArtifact1Data.setDataAsArray(instanceArtifact1Bytes);
+        when(artifactCassandraDao.getArtifact(instanceArtifactId)).thenReturn(Either.left(instanceArtifact1Data));
+        return instanceArtifact1;
+    }
+
+    private ArtifactDefinition mockArtifactDefinition(final String artifactId) {
+        final ArtifactDefinition artifact = new ArtifactDefinition();
+        artifact.setEsId(artifactId);
+
+        return artifact;
+    }
+}
\ No newline at end of file
diff --git a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/test/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/NsDescriptorGeneratorImplTest.java b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/test/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/NsDescriptorGeneratorImplTest.java
new file mode 100644 (file)
index 0000000..7ed7b7d
--- /dev/null
@@ -0,0 +1,240 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.plugins.etsi.nfv.nsd.generator;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.anEmptyMap;
+import static org.hamcrest.Matchers.greaterThan;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.hamcrest.core.IsNull.nullValue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import fj.data.Either;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.openecomp.sdc.be.config.Configuration;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.exception.NsdException;
+import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.model.Nsd;
+import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.model.VnfDescriptor;
+import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.tosca.yaml.ToscaTemplateYamlGenerator;
+import org.openecomp.sdc.be.tosca.ToscaExportHandler;
+import org.openecomp.sdc.be.tosca.model.SubstitutionMapping;
+import org.openecomp.sdc.be.tosca.model.ToscaNodeTemplate;
+import org.openecomp.sdc.be.tosca.model.ToscaNodeType;
+import org.openecomp.sdc.be.tosca.model.ToscaProperty;
+import org.openecomp.sdc.be.tosca.model.ToscaPropertyConstraint;
+import org.openecomp.sdc.be.tosca.model.ToscaPropertyConstraintValidValues;
+import org.openecomp.sdc.be.tosca.model.ToscaTemplate;
+import org.openecomp.sdc.be.tosca.model.ToscaTemplateCapability;
+import org.openecomp.sdc.be.tosca.model.ToscaTopolgyTemplate;
+import org.openecomp.sdc.common.api.ConfigurationSource;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.ObjectProvider;
+import org.yaml.snakeyaml.Yaml;
+
+class NsDescriptorGeneratorImplTest {
+
+    private static final String VNFD_AMF_NODE_NAME = "vnfd_amf";
+    private static final String VIRTUAL_LINK_REQUIREMENT_NAME = "virtual_link";
+
+    @Mock
+    private ToscaExportHandler toscaExportHandler;
+
+    private final ObjectProvider<ToscaTemplateYamlGenerator> toscaTemplateYamlGeneratorProvider = new ObjectProvider<ToscaTemplateYamlGenerator>() {
+        @Override
+        public ToscaTemplateYamlGenerator getObject(Object... args) {
+            return new ToscaTemplateYamlGenerator((ToscaTemplate) args[0]);
+        }
+
+        @Override
+        public ToscaTemplateYamlGenerator getIfAvailable() {
+            return null;
+        }
+
+        @Override
+        public ToscaTemplateYamlGenerator getIfUnique() {
+            return null;
+        }
+
+        @Override
+        public ToscaTemplateYamlGenerator getObject() {
+            return null;
+        }
+    };
+
+    private NsDescriptorGeneratorImpl nsDescriptorGenerator;
+
+    @BeforeEach
+    void setUp() {
+        setUpConfigurationMock();
+        MockitoAnnotations.initMocks(this);
+        nsDescriptorGenerator = new NsDescriptorGeneratorImpl(toscaExportHandler, toscaTemplateYamlGeneratorProvider);
+    }
+
+    private void setUpConfigurationMock() {
+        final List<Map<String, Map<String, String>>> defaultImports = new ArrayList<>();
+        final Map<String, Map<String, String>> importMap = new HashMap<>();
+        final Map<String, String> nodeImportEntry = new HashMap<>();
+        nodeImportEntry.put("file", "nodes.yml");
+        importMap.put("nodes", nodeImportEntry);
+        defaultImports.add(importMap);
+
+        final ConfigurationSource configurationSource = mock(ConfigurationSource.class);
+        final Configuration configuration = new Configuration();
+        configuration.setDefaultImports(defaultImports);
+        configuration.setHeatEnvArtifactHeader("");
+        configuration.setHeatEnvArtifactFooter("");
+        when(configurationSource.getAndWatchConfiguration(any(), any())).thenReturn(configuration);
+        new ConfigurationManager(configurationSource);
+    }
+
+    @Test
+    void testGenerate() throws IOException, NsdException {
+        //given
+        final Component component = mock(Component.class);
+        when(component.getComponentType()).thenReturn(ComponentTypeEnum.SERVICE);
+        final ToscaTemplate componentToscaTemplate = new ToscaTemplate("");
+        final ToscaTopolgyTemplate componentToscaTopologyTemplate = new ToscaTopolgyTemplate();
+        componentToscaTemplate.setTopology_template(componentToscaTopologyTemplate);
+
+        final HashMap<String, ToscaNodeTemplate> nodeTemplateMap = new HashMap<>();
+        final ToscaNodeTemplate vnfAmfNodeTemplate = new ToscaNodeTemplate();
+        vnfAmfNodeTemplate.setType("com.ericsson.resource.abstract.Ericsson.AMF");
+        //a property to be excluded
+        vnfAmfNodeTemplate.setProperties(ImmutableMap.of("nf_naming_code", new ToscaProperty()));
+        //a property that wont be excluded
+        vnfAmfNodeTemplate.setProperties(ImmutableMap.of("will_not_be_excluded", new ToscaProperty()));
+        nodeTemplateMap.put(VNFD_AMF_NODE_NAME, vnfAmfNodeTemplate);
+        
+        final Map<String, ToscaTemplateCapability> vnfAmfCapabilities = new HashMap<>();
+        vnfAmfCapabilities.put("myCapability", new ToscaTemplateCapability());
+               vnfAmfNodeTemplate.setCapabilities(vnfAmfCapabilities);
+        componentToscaTopologyTemplate.setNode_templates(nodeTemplateMap);
+        
+        final SubstitutionMapping substitutionMapping = mock(SubstitutionMapping.class);
+        Map<String, String[]> requirements = new HashMap<>();
+        String[] requirementAssignment = {"VNF1", VIRTUAL_LINK_REQUIREMENT_NAME};
+        requirements.put(VIRTUAL_LINK_REQUIREMENT_NAME, requirementAssignment);
+               when(substitutionMapping.getRequirements()).thenReturn(requirements);
+               Map<String, String[]> capabilities = new HashMap<>();
+        String[] capabilitiesAssignment = {"VNF1", "capability1"};
+        capabilities.put("capability", capabilitiesAssignment);
+               when(substitutionMapping.getCapabilities()).thenReturn(capabilities);
+               componentToscaTopologyTemplate.setSubstitution_mappings(substitutionMapping);
+
+        final ToscaTemplate componentInterfaceToscaTemplate = new ToscaTemplate("");
+        final ToscaNodeType interfaceToscaNodeType = new ToscaNodeType();
+        interfaceToscaNodeType.setProperties(
+            ImmutableMap.of("designer", createToscaProperty("designerValue"),
+                "version", createToscaProperty("versionValue"),
+                "name", createToscaProperty("nameValue"),
+                "invariant_id", createToscaProperty("invariantIdValue"))
+        );
+        final String nsNodeTypeName = "nsNodeTypeName";
+        componentInterfaceToscaTemplate.setNode_types(ImmutableMap.of(nsNodeTypeName, interfaceToscaNodeType));
+
+
+        when(toscaExportHandler.convertToToscaTemplate(component)).thenReturn(Either.left(componentToscaTemplate));
+        when(toscaExportHandler.convertInterfaceNodeType(any(), any(), any(), any(), anyBoolean()))
+            .thenReturn(Either.left(componentInterfaceToscaTemplate));
+
+        final List<VnfDescriptor> vnfDescriptorList = new ArrayList<>();
+        VnfDescriptor vnfDescriptor1 = new VnfDescriptor();
+        vnfDescriptor1.setName(VNFD_AMF_NODE_NAME);
+        vnfDescriptor1.setVnfdFileName("vnfd_amf.yaml");
+        vnfDescriptor1.setNodeType("com.ericsson.resource.abstract.Ericsson.AMF");
+
+        vnfDescriptorList.add(vnfDescriptor1);
+
+        //when
+        final Nsd nsd = nsDescriptorGenerator.generate(component, vnfDescriptorList).orElse(null);
+        //then
+        assertThat("Nsd should not be null", nsd, is(notNullValue()));
+        assertThat("Nsd designer should be as expected", nsd.getDesigner(), is("designerValue"));
+        assertThat("Nsd version should be as expected", nsd.getVersion(), is("versionValue"));
+        assertThat("Nsd name should be as expected", nsd.getName(), is("nameValue"));
+        assertThat("Nsd invariantId should be as expected", nsd.getInvariantId(), is("invariantIdValue"));
+        assertThat("Nsd content should not be empty", nsd.getContents(), is(notNullValue()));
+        assertThat("Nsd content should not be empty", nsd.getContents().length, is(greaterThan(0)));
+
+        final Map<String, Object> toscaTemplateYaml = readYamlAsMap(nsd.getContents());
+        @SuppressWarnings("unchecked")
+        final Map<String, Object> topologyTemplate = (Map<String, Object>) toscaTemplateYaml.get("topology_template");
+        assertThat("topology_template should not be empty", topologyTemplate, is(not(anEmptyMap())));
+        @SuppressWarnings("unchecked")
+        final Map<String, Object> substitutionMappings =
+            (Map<String, Object>) topologyTemplate.get("substitution_mappings");
+        assertThat("substitution_mappings should not be empty", substitutionMappings, is(not(anEmptyMap())));
+        assertThat("substitution_mappings->node_type should not be null",
+            substitutionMappings.get("node_type"), is(notNullValue()));
+        assertThat("substitution_mappings->node_type should be as expected",
+            substitutionMappings.get("node_type"), is(nsNodeTypeName));
+        
+        final Map<String, List<String>> subMappingRequirements = (Map<String, List<String>>) substitutionMappings.get("requirements");
+        assertThat(subMappingRequirements.get(VIRTUAL_LINK_REQUIREMENT_NAME).get(0), is("VNF1"));
+        assertThat(subMappingRequirements.get(VIRTUAL_LINK_REQUIREMENT_NAME).get(1), is(VIRTUAL_LINK_REQUIREMENT_NAME));
+        final Map<String, List<String>> subMappingCapabilities = (Map<String, List<String>>) substitutionMappings.get("capabilities");
+        assertThat(subMappingCapabilities.get("capability").get(0), is("VNF1"));
+        assertThat(subMappingCapabilities.get("capability").get(1), is("capability1"));
+        
+        @SuppressWarnings("unchecked")
+               final Map<String, Object> nodeTemplates =
+                (Map<String, Object>) topologyTemplate.get("node_templates");
+        @SuppressWarnings("unchecked")
+               final Map<String, Object> nodeTemplate =
+                (Map<String, Object>) nodeTemplates.get(VNFD_AMF_NODE_NAME);
+        assertThat("capabilities should be null",
+                       nodeTemplate.get("capabilities"), is(nullValue()));
+    }
+
+    private ToscaProperty createToscaProperty(final String value) {
+        final ToscaProperty toscaProperty = new ToscaProperty();
+        final ToscaPropertyConstraint toscaPropertyConstraint =
+            new ToscaPropertyConstraintValidValues(ImmutableList.of(value));
+        toscaProperty.setConstraints(ImmutableList.of(toscaPropertyConstraint));
+        return toscaProperty;
+    }
+
+    @SuppressWarnings("unchecked")
+    private Map<String, Object> readYamlAsMap(final byte[] yamlContents) throws IOException {
+        final Yaml yaml = new Yaml();
+        try (final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(yamlContents)) {
+            return  (Map<String, Object>) yaml.load(byteArrayInputStream);
+        }
+    }
+}
\ No newline at end of file
diff --git a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/test/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/VnfDescriptorGeneratorImplTest.java b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/test/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/VnfDescriptorGeneratorImplTest.java
new file mode 100644 (file)
index 0000000..e223fda
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.plugins.etsi.nfv.nsd.generator;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.notNullValue;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.apache.commons.io.IOUtils;
+import org.junit.jupiter.api.Test;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.exception.VnfDescriptorException;
+import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.model.VnfDescriptor;
+
+class VnfDescriptorGeneratorImplTest {
+
+    private final VnfDescriptorGeneratorImpl vnfDescriptorGenerator = new VnfDescriptorGeneratorImpl();
+    private final Path testResourcesPath = Paths.get("src", "test", "resources", "vnf-onboarded-csar");
+
+    @Test
+    void testGenerate() throws IOException, VnfDescriptorException {
+        final byte[] onboardedPackage = getResourceAsByteArray("VnfPackage_AMF_v2.csar");
+        final ArtifactDefinition artifactDefinition = new ArtifactDefinition();
+        artifactDefinition.setPayload(onboardedPackage);
+        artifactDefinition.setArtifactName("vnf-onboarded-csar.csar");
+        final String vnfDescriptorName = "vnf-onboarded-csar";
+        final VnfDescriptor vnfDescriptor = vnfDescriptorGenerator
+            .generate(vnfDescriptorName, artifactDefinition).orElse(null);
+        final String expectedNodeType = "com.ericsson.resource.abstract.Ericsson.AMF";
+        final String expectedVnfdFileName = "vnfd_amf.yaml";
+        assertThat("Vnf Descriptor should be present", vnfDescriptor, is(notNullValue()));
+        assertThat("Vnf Descriptor should have the expected name", vnfDescriptor.getName(),
+            is(vnfDescriptorName));
+        assertThat("Vnf Descriptor should have the expected node type", vnfDescriptor.getNodeType(),
+            is(expectedNodeType));
+        assertThat("Vnf Descriptor should have the expected vnfd file name", vnfDescriptor.getVnfdFileName(),
+            is(expectedVnfdFileName));
+        assertThat("Vnf Descriptor should contain the expected definition files count",
+            vnfDescriptor.getDefinitionFiles().size(), is(2));
+        assertThat("Vnf Descriptor should contain the expected definition entries",
+            vnfDescriptor.getDefinitionFiles().keySet(), contains("Definitions/vnfd_amf.yaml",
+                "Definitions/etsi_nfv_sol001_vnfd_2_5_1_types.yaml"));
+    }
+
+    private byte[] getResourceAsByteArray(final String filename) throws IOException {
+        try (final InputStream inputStream = readFileAsStream(filename)) {
+            return IOUtils.toByteArray(inputStream);
+        } catch (final IOException ex) {
+            throw new IOException(
+                String.format("Could not read the file \"%s\"", filename), ex);
+        }
+    }
+
+    private FileInputStream readFileAsStream(final String fileName) throws FileNotFoundException {
+        final Path path = Paths.get(testResourcesPath.toString(), fileName);
+        return new FileInputStream(path.toFile());
+    }
+
+}
\ No newline at end of file
diff --git a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/test/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/tosca/yaml/ToscaTemplateYamlGeneratorTest.java b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/test/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/tosca/yaml/ToscaTemplateYamlGeneratorTest.java
new file mode 100644 (file)
index 0000000..5341598
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.plugins.etsi.nfv.nsd.tosca.yaml;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.junit.jupiter.api.Test;
+import org.openecomp.sdc.be.tosca.model.ToscaNodeType;
+import org.openecomp.sdc.be.tosca.model.ToscaProperty;
+import org.openecomp.sdc.be.tosca.model.ToscaPropertyConstraintValidValues;
+import org.openecomp.sdc.be.tosca.model.ToscaTemplate;
+
+class ToscaTemplateYamlGeneratorTest {
+
+    @Test
+    void testGenerateYamlWithImportsKey() {
+        //given
+        final ToscaTemplate toscaTemplate = new ToscaTemplate("tosca_simple_yaml_1_1");
+        final List<Map<String, Map<String, String>>> importList =
+            ImmutableList.of(
+                ImmutableMap.of("etsi_nfv_sol001_nsd_2_7_1_types",
+                    ImmutableMap.of("file", "etsi_nfv_sol001_nsd_2_7_1_types.yaml")
+                ),
+                ImmutableMap.of("anotherImport",
+                    ImmutableMap.of("file", "anotherImport.yaml")
+                )
+            );
+        toscaTemplate.setImports(importList);
+        final ToscaTemplateYamlGenerator toscaTemplateYamlGenerator = new ToscaTemplateYamlGenerator(toscaTemplate);
+        //when
+        final String toscaTemplateYamlString = toscaTemplateYamlGenerator.parseToYamlString();
+
+        //then
+        final String expectedImports = "imports:\n"
+            + "- file: etsi_nfv_sol001_nsd_2_7_1_types.yaml\n"
+            + "- file: anotherImport.yaml";
+        assertThat("Imports format should be as expected", toscaTemplateYamlString.contains(expectedImports), is(true));
+    }
+
+    @Test
+    void testGenerateYamlWithToscaProperty() {
+        //given
+        final ToscaTemplate toscaTemplate = new ToscaTemplate("tosca_simple_yaml_1_1");
+
+        final Map<String, ToscaProperty> toscaPropertyMap = new HashMap<>();
+        final ToscaProperty toscaProperty = new ToscaProperty();
+        final String defaultpValue = "defaultpValue";
+        toscaProperty.setDefaultp(defaultpValue);
+        ToscaPropertyConstraintValidValues toscaPropertyConstraintValidValues =
+            new ToscaPropertyConstraintValidValues(Collections.singletonList(defaultpValue));
+        toscaProperty.setConstraints(Collections.singletonList(toscaPropertyConstraintValidValues));
+        final String propertyName = "aProperty";
+        toscaPropertyMap.put(propertyName, toscaProperty);
+
+        final Map<String, ToscaNodeType> toscaNodeMap = new HashMap<>();
+        final ToscaNodeType toscaNodeType = new ToscaNodeType();
+        toscaNodeType.setProperties(toscaPropertyMap);
+        toscaNodeMap.put("aNode", toscaNodeType);
+        toscaTemplate.setNode_types(toscaNodeMap);
+        final ToscaTemplateYamlGenerator toscaTemplateYamlGenerator = new ToscaTemplateYamlGenerator(toscaTemplate);
+        //when
+        final String toscaTemplateYamlString = toscaTemplateYamlGenerator.parseToYamlString();
+
+        final String expectedProperty = String.format("%s:\n"
+                + "        default: %s\n"
+                + "        constraints:\n"
+                + "        - valid_values:\n"
+                + "          - %s",
+            propertyName, defaultpValue, defaultpValue);
+        //then
+        assertThat("Property format should be as expected",
+            toscaTemplateYamlString.contains(expectedProperty), is(true));
+    }
+}
\ No newline at end of file
diff --git a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/test/resources/vnf-onboarded-csar/VnfPackage_AMF_v2.csar b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/test/resources/vnf-onboarded-csar/VnfPackage_AMF_v2.csar
new file mode 100644 (file)
index 0000000..1663f97
Binary files /dev/null and b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/test/resources/vnf-onboarded-csar/VnfPackage_AMF_v2.csar differ
diff --git a/catalog-be-plugins/pom.xml b/catalog-be-plugins/pom.xml
new file mode 100644 (file)
index 0000000..f0f8d99
--- /dev/null
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ ============LICENSE_START=======================================================
+  ~  Copyright (C) 2020 Nordix Foundation
+  ~  ================================================================================
+  ~  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.
+  ~
+  ~  SPDX-License-Identifier: Apache-2.0
+  ~  ============LICENSE_END=========================================================
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <parent>
+    <artifactId>sdc-main</artifactId>
+    <groupId>org.openecomp.sdc</groupId>
+    <version>1.7.0-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>catalog-be-plugins</artifactId>
+  <packaging>pom</packaging>
+
+  <modules>
+    <module>etsi-nfv-nsd-csar-plugin</module>
+  </modules>
+
+</project>
\ No newline at end of file
index 6a8b405..161097d 100644 (file)
@@ -57,6 +57,7 @@ public enum ArtifactTypeEnum {
     DCAE_POLICY("DCAE_POLICY"),
     DCAE_TOSCA("DCAE_TOSCA"),
     DG_XML("DG_XML"),
+    ETSI_PACKAGE("ETSI_PACKAGE"),
     GUIDE("GUIDE"),
     HEAT_ARTIFACT("HEAT_ARTIFACT"),
     HEAT_ENV("HEAT_ENV"),
index db5be60..2a3a3bd 100644 (file)
@@ -29,9 +29,12 @@ import java.util.stream.Collectors;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.MapUtils;
 
+/**
+ * Stores the content of files in a path:byte[] structure
+ */
 public class FileContentHandler {
 
-    private Map<String, byte[]> files = new HashMap<>();
+    private final Map<String, byte[]> files = new HashMap<>();
 
     public FileContentHandler() {
     }
@@ -55,30 +58,70 @@ public class FileContentHandler {
         return new ByteArrayInputStream(content);
     }
 
-    public byte[] getFileContent(final String fileName) {
-        return files.get(fileName);
+    /**
+     * Gets the content of a file.
+     *
+     * @param filePath the file path
+     * @return the content of the file
+     */
+    public byte[] getFileContent(final String filePath) {
+        return files.get(filePath);
     }
 
-    public boolean isFolder(final String fileName) {
-        return files.get(fileName) == null;
+    /**
+     * Checks if the path is a folder.
+     *
+     * @param filePath the file path to verify
+     * @return {@code true} if the path is a folder, {@code false} otherwise
+     */
+    public boolean isFolder(final String filePath) {
+        return files.get(filePath) == null;
     }
 
-    public boolean isFile(final String fileName) {
-        return files.get(fileName) != null;
+    /**
+     * Checks if the path is a file.
+     *
+     * @param filePath the file path to verify
+     * @return {@code true} if the path is a file, {@code false} otherwise
+     */
+    public boolean isFile(final String filePath) {
+        return files.get(filePath) != null;
     }
 
-    public void addFolder(final String folder) {
-        files.put(folder, null);
+    /**
+     * Adds a folder.
+     *
+     * @param folderPath the folder path to add
+     */
+    public void addFolder(final String folderPath) {
+        files.put(folderPath, null);
     }
 
-    public void addFile(final String fileName, final byte[] content) {
-        files.put(fileName, content == null ? new byte[0] : content);
+    /**
+     * Adds a file.
+     *
+     * @param filePath the file path
+     * @param content the file content
+     */
+    public void addFile(final String filePath, final byte[] content) {
+        files.put(filePath, content == null ? new byte[0] : content);
     }
 
-    public void addFile(final String fileName, final InputStream is) {
-        files.put(fileName, FileUtils.toByteArray(is));
+    /**
+     * Adds a file.
+     *
+     * @param filePath the file path
+     * @param fileInputStream the file input stream
+     */
+    public void addFile(final String filePath, final InputStream fileInputStream) {
+        files.put(filePath, FileUtils.toByteArray(fileInputStream));
     }
 
+    /**
+     * Gets only the files, ignoring directories from the structure.
+     *
+     * @return a file path:content map
+     */
     public Map<String, byte[]> getFiles() {
         return files.entrySet().stream().filter(entry -> entry.getValue() != null)
             .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
@@ -88,10 +131,20 @@ public class FileContentHandler {
         addAll(files);
     }
 
+    /**
+     * Gets only the file paths, ignoring directories from the structure.
+     *
+     * @return a set of the file paths
+     */
     public Set<String> getFileList() {
         return files.keySet().stream().filter(this::isFile).collect(Collectors.toSet());
     }
 
+    /**
+     * Gets only the folder paths, ignoring files from the structure.
+     *
+     * @return a set of the folder paths
+     */
     public Set<String> getFolderList() {
         return files.keySet().stream().filter(this::isFolder).collect(Collectors.toSet());
     }
@@ -109,16 +162,33 @@ public class FileContentHandler {
         }
     }
 
+    /**
+     * Checks if the file structure is empty.
+     *
+     * @return {@code true} if the file structure is empty, {@code false} otherwise
+     */
     public boolean isEmpty() {
         return MapUtils.isEmpty(this.files);
     }
 
-    public byte[] remove(final String fileName) {
-        return files.remove(fileName);
+    /**
+     * Removes a file or folder from the file structure.
+     *
+     * @param filePath the file path to remove
+     * @return the removed file content
+     */
+    public byte[] remove(final String filePath) {
+        return files.remove(filePath);
     }
 
-    public boolean containsFile(final String fileName) {
-        return files.containsKey(fileName);
+    /**
+     * Checks if the file structure contains the provided file.
+     *
+     * @param filePath the file path to search
+     * @return {@code true} if the file exists, {@code false} otherwise
+     */
+    public boolean containsFile(final String filePath) {
+        return files.containsKey(filePath);
     }
 
 }
diff --git a/pom.xml b/pom.xml
index df4da63..bb6cdaf 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -573,6 +573,7 @@ Modifications copyright (c) 2018-2019 Nokia
                 <module>catalog-dao</module>
                 <module>catalog-model</module>
                 <module>catalog-be</module>
+                <module>catalog-be-plugins</module>
                 <module>asdctool</module>
                 <module>catalog-ui</module>
                 <module>catalog-fe</module>
@@ -623,6 +624,7 @@ Modifications copyright (c) 2018-2019 Nokia
                 <module>common-app-logging</module>
                 <module>common-app-api</module>
                 <module>common-be</module>
+                <module>catalog-be-plugins</module>
                 <module>catalog-dao</module>
                 <module>catalog-model</module>
                 <module>catalog-be</module>