diff options
author | andre.schmid <andre.schmid@est.tech> | 2020-08-21 11:40:56 +0100 |
---|---|---|
committer | Sébastien Determe <sebastien.determe@intl.att.com> | 2020-09-07 08:06:05 +0000 |
commit | 4188b20055dac1974f6c6f1a6a8320099b154ca5 (patch) | |
tree | 85e8b0b4a00dc78cddc163625564bcb198e21799 /catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java | |
parent | 798f858d18403a31a4d464b07bb1a9a74666c9c7 (diff) |
Plugin to Generate Service ETSI NSD CSAR
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>
Diffstat (limited to 'catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java')
16 files changed, 1951 insertions, 0 deletions
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 index 0000000000..6dc120290c --- /dev/null +++ b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/builder/NsdCsarManifestBuilder.java @@ -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 index 0000000000..e709993b45 --- /dev/null +++ b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/builder/NsdToscaMetadataBuilder.java @@ -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 index 0000000000..2483b2ebe0 --- /dev/null +++ b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/exception/NsdException.java @@ -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 index 0000000000..e4f966fea3 --- /dev/null +++ b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/exception/VnfDescriptorException.java @@ -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 index 0000000000..00aef05ed7 --- /dev/null +++ b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/EtsiNfvNsCsarEntryGenerator.java @@ -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 index 0000000000..f9ee55eecf --- /dev/null +++ b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/EtsiNfvNsdCsarGenerator.java @@ -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 index 0000000000..64356ca7aa --- /dev/null +++ b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/EtsiNfvNsdCsarGeneratorImpl.java @@ -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 index 0000000000..d36757cf48 --- /dev/null +++ b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/NsDescriptorGenerator.java @@ -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 index 0000000000..0c8b86b5e7 --- /dev/null +++ b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/NsDescriptorGeneratorImpl.java @@ -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 index 0000000000..c4599d7cf4 --- /dev/null +++ b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/VnfDescriptorGenerator.java @@ -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 index 0000000000..8cf54d129a --- /dev/null +++ b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/VnfDescriptorGeneratorImpl.java @@ -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 index 0000000000..a8de728702 --- /dev/null +++ b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/model/Nsd.java @@ -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 index 0000000000..53615effb6 --- /dev/null +++ b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/model/VnfDescriptor.java @@ -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 index 0000000000..822d6dc82b --- /dev/null +++ b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/tosca/yaml/NsdTemplateRepresenter.java @@ -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 index 0000000000..4c2cd87184 --- /dev/null +++ b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/tosca/yaml/ToscaTemplateYamlGenerator.java @@ -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 index 0000000000..b93db62088 --- /dev/null +++ b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/tosca/yaml/UnsortedPropertyUtils.java @@ -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 |