diff options
Diffstat (limited to 'catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/NsDescriptorGeneratorImpl.java')
-rw-r--r-- | catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/NsDescriptorGeneratorImpl.java | 411 |
1 files changed, 411 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/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(); + } + +} |