From 974bbeb1ca432885c1569ec71c1b5060d5c1c519 Mon Sep 17 00:00:00 2001 From: vasraz Date: Tue, 19 Apr 2022 15:29:16 +0100 Subject: [PATCH] Update tosca parser for node template artifacts Signed-off-by: Vasyl Razinkov Change-Id: I824f69a771fb17f3ad8562a96c3670d87158dc6e Issue-ID: SDC-3965 --- .../onap/sdc/toscaparser/api/EntityTemplate.java | 330 +-------- .../org/onap/sdc/toscaparser/api/NodeTemplate.java | 390 ++--------- .../onap/sdc/toscaparser/api/ToscaTemplate.java | 176 +++-- .../sdc/toscaparser/api/elements/ArtifactDef.java | 55 ++ .../toscaparser/api/elements/ArtifactTypeDef.java | 45 +- .../toscaparser/api/elements/TypeValidation.java | 2 + .../sdc/tosca/parser/elements/EntityDetails.java | 3 +- .../parser/elements/NodeTemplateEntityDetails.java | 2 +- .../elements/queries/NodeTemplateEntityQuery.java | 4 +- .../elements/queries/TopologyTemplateQuery.java | 2 +- .../onap/sdc/tosca/parser/impl/QueryProcessor.java | 4 +- .../sdc/tosca/parser/impl/SdcCsarHelperImpl.java | 767 +++++++++++---------- .../tosca/parser/impl/SdcToscaParserFactory.java | 122 ++-- .../org/onap/sdc/impl/SdcToscaParserBasicTest.java | 19 +- .../onap/sdc/impl/ToscaParserArtifactsTest.java | 80 +++ .../onap/sdc/impl/ToscaParserNodeTemplateTest.java | 26 +- .../queries/TopologyTemplateQueryTest.java | 18 +- .../impl/ToscaParserNodeTemplateMockTest.java | 2 +- .../resources/csars/resource-VspWithArtifacts.csar | Bin 0 -> 70481 bytes 19 files changed, 774 insertions(+), 1273 deletions(-) create mode 100644 jtosca/src/main/java/org/onap/sdc/toscaparser/api/elements/ArtifactDef.java create mode 100644 sdc-tosca/src/test/java/org/onap/sdc/impl/ToscaParserArtifactsTest.java create mode 100644 sdc-tosca/src/test/resources/csars/resource-VspWithArtifacts.csar diff --git a/jtosca/src/main/java/org/onap/sdc/toscaparser/api/EntityTemplate.java b/jtosca/src/main/java/org/onap/sdc/toscaparser/api/EntityTemplate.java index 93bfe2b..9f8ccbd 100644 --- a/jtosca/src/main/java/org/onap/sdc/toscaparser/api/EntityTemplate.java +++ b/jtosca/src/main/java/org/onap/sdc/toscaparser/api/EntityTemplate.java @@ -107,12 +107,12 @@ public abstract class EntityTemplate { } } if (_entityName.equals("relationship_type")) { - Object relationship = _template.get("relationship"); + Object relationship = _template.get(RELATIONSHIP); type = null; if (relationship != null && relationship instanceof LinkedHashMap) { type = (String) ((LinkedHashMap) relationship).get("type"); } else if (relationship instanceof String) { - type = (String) entityTpl.get("relationship"); + type = (String) entityTpl.get(RELATIONSHIP); } else { type = (String) entityTpl.get("type"); } @@ -197,10 +197,9 @@ public abstract class EntityTemplate { for (String reqName : req.keySet()) { Object reqItem = req.get(reqName); if (reqItem instanceof LinkedHashMap) { - Object rel = ((LinkedHashMap) reqItem).get("relationship"); -// LinkedHashMap relationship = rel instanceof LinkedHashMap ? (LinkedHashMap) rel : null; + Object rel = ((LinkedHashMap) reqItem).get(RELATIONSHIP); String nodeName = ((LinkedHashMap) reqItem).get("node").toString(); - Object capability = ((LinkedHashMap) reqItem).get("capability"); + Object capability = ((LinkedHashMap) reqItem).get(CAPABILITY); String capabilityString = capability != null ? capability.toString() : null; reqs.add(new RequirementAssignment(reqName, nodeName, capabilityString, rel)); @@ -314,7 +313,7 @@ public abstract class EntityTemplate { } } // then update (if available) with the node properties - LinkedHashMap pp = (LinkedHashMap) props.get("properties"); + LinkedHashMap pp = (LinkedHashMap) props.get(PROPERTIES); if (pp != null) { properties.putAll(pp); } @@ -343,7 +342,7 @@ public abstract class EntityTemplate { LinkedHashMap capabilities = (LinkedHashMap) ((EntityType) typeDefinition).getValue(CAPABILITIES, entityTpl, false); if (capabilities != null) { - _commonValidateField(capabilities, allowedCaps, "capabilities"); + _commonValidateField(capabilities, allowedCaps, CAPABILITIES); _validateCapabilitiesProperties(capabilities); } } @@ -370,7 +369,7 @@ public abstract class EntityTemplate { int maxInstances = (int) propDict.get("max_instances"); int defaultInstances = (int) propDict.get("default_instances"); if (defaultInstances < minInstances || defaultInstances > maxInstances) { - //err_msg = ('"properties" of template "%s": ' + //err_msg = ('PROPERTIES of template "%s": ' // '"default_instances" value is not between ' // '"min_instances" and "max_instances".' % // self.name) @@ -396,7 +395,7 @@ public abstract class EntityTemplate { // validate all required properties have values if (properties != null) { ArrayList reqPropsNoValueOrDefault = new ArrayList(); - _commonValidateField(properties, allowedProps, "properties"); + _commonValidateField(properties, allowedProps, PROPERTIES); // make sure it's not missing any property required by a tosca type for (String r : requiredProps) { if (properties.get(r) == null) { @@ -427,12 +426,12 @@ public abstract class EntityTemplate { return;//??? } boolean bBad = false; - Object relationship = ((LinkedHashMap) template).get("relationship"); + Object relationship = ((LinkedHashMap) template).get(RELATIONSHIP); if (relationship != null) { if (!(relationship instanceof String)) { bBad = (((LinkedHashMap) relationship).get(TYPE) == null); } else if (relationship instanceof String) { - bBad = (template.get("relationship") == null); + bBad = (template.get(RELATIONSHIP) == null); } } else { bBad = (template.get(TYPE) == null); @@ -503,7 +502,7 @@ public abstract class EntityTemplate { Object relDef = relValue; LinkedHashMap rel = null; if (relDef instanceof LinkedHashMap) { - Object relob = ((LinkedHashMap) relDef).get("relationship"); + Object relob = ((LinkedHashMap) relDef).get(RELATIONSHIP); if (relob instanceof LinkedHashMap) { rel = (LinkedHashMap) relob; } @@ -576,310 +575,3 @@ public abstract class EntityTemplate { '}'; } } - -/*python - -class EntityTemplate(object): - '''Base class for TOSCA templates.''' - - SECTIONS = (DERIVED_FROM, PROPERTIES, REQUIREMENTS, - INTERFACES, CAPABILITIES, TYPE, DESCRIPTION, DIRECTIVES, - ATTRIBUTES, ARTIFACTS, NODE_FILTER, COPY) = \ - ('derived_from', 'properties', 'requirements', 'interfaces', - 'capabilities', 'type', 'description', 'directives', - 'attributes', 'artifacts', 'node_filter', 'copy') - REQUIREMENTS_SECTION = (NODE, CAPABILITY, RELATIONSHIP, OCCURRENCES, NODE_FILTER) = \ - ('node', 'capability', 'relationship', - 'occurrences', 'node_filter') - # Special key names - SPECIAL_SECTIONS = (METADATA) = ('metadata') - - def __init__(self, name, template, entity_name, custom_def=None): - self.name = name - self.entity_tpl = template - self.custom_def = custom_def - self._validate_field(self.entity_tpl) - type = self.entity_tpl.get('type') - UnsupportedType.validate_type(type) - if entity_name == 'node_type': - self.type_definition = NodeType(type, custom_def) \ - if type is not None else None - if entity_name == 'relationship_type': - relationship = template.get('relationship') - type = None - if relationship and isinstance(relationship, dict): - type = relationship.get('type') - elif isinstance(relationship, str): - type = self.entity_tpl['relationship'] - else: - type = self.entity_tpl['type'] - UnsupportedType.validate_type(type) - self.type_definition = RelationshipType(type, - None, custom_def) - if entity_name == 'policy_type': - if not type: - msg = (_('Policy definition of "%(pname)s" must have' - ' a "type" ''attribute.') % dict(pname=name)) - ValidationIssueCollector.appendException( - ValidationError(msg)) - - self.type_definition = PolicyType(type, custom_def) - if entity_name == 'group_type': - self.type_definition = GroupType(type, custom_def) \ - if type is not None else None - self._properties = None - self._interfaces = None - self._requirements = None - self._capabilities = None - - @property - def type(self): - if self.type_definition: - return self.type_definition.type - - @property - def parent_type(self): - if self.type_definition: - return self.type_definition.parent_type - - @property - def requirements(self): - if self._requirements is None: - self._requirements = self.type_definition.get_value( - self.REQUIREMENTS, - self.entity_tpl) or [] - return self._requirements - - def get_properties_objects(self): - '''Return properties objects for this template.''' - if self._properties is None: - self._properties = self._create_properties() - return self._properties - - def get_properties(self): - '''Return a dictionary of property name-object pairs.''' - return {prop.name: prop - for prop in self.get_properties_objects()} - - def get_property_value(self, name): - '''Return the value of a given property name.''' - props = self.get_properties() - if props and name in props.keys(): - return props[name].value - - @property - def interfaces(self): - if self._interfaces is None: - self._interfaces = self._create_interfaces() - return self._interfaces - - def get_capabilities_objects(self): - '''Return capabilities objects for this template.''' - if not self._capabilities: - self._capabilities = self._create_capabilities() - return self._capabilities - - def get_capabilities(self): - '''Return a dictionary of capability name-object pairs.''' - return {cap.name: cap - for cap in self.get_capabilities_objects()} - - def is_derived_from(self, type_str): - '''Check if object inherits from the given type. - - Returns true if this object is derived from 'type_str'. - False otherwise. - ''' - if not self.type: - return False - elif self.type == type_str: - return True - elif self.parent_type: - return self.parent_type.is_derived_from(type_str) - else: - return False - - def _create_capabilities(self): - capability = [] - caps = self.type_definition.get_value(self.CAPABILITIES, - self.entity_tpl, True) - if caps: - for name, props in caps.items(): - capabilities = self.type_definition.get_capabilities() - if name in capabilities.keys(): - c = capabilities[name] - properties = {} - # first use the definition default value - if c.properties: - for property_name in c.properties.keys(): - prop_def = c.properties[property_name] - if 'default' in prop_def: - properties[property_name] = prop_def['default'] - # then update (if available) with the node properties - if 'properties' in props and props['properties']: - properties.update(props['properties']) - - cap = CapabilityAssignment(name, properties, c) - capability.append(cap) - return capability - - def _validate_properties(self, template, entitytype): - properties = entitytype.get_value(self.PROPERTIES, template) - self._common_validate_properties(entitytype, properties) - - def _validate_capabilities(self): - type_capabilities = self.type_definition.get_capabilities() - allowed_caps = \ - type_capabilities.keys() if type_capabilities else [] - capabilities = self.type_definition.get_value(self.CAPABILITIES, - self.entity_tpl) - if capabilities: - self._common_validate_field(capabilities, allowed_caps, - 'capabilities') - self._validate_capabilities_properties(capabilities) - - def _validate_capabilities_properties(self, capabilities): - for cap, props in capabilities.items(): - capability = self.get_capability(cap) - if not capability: - continue - capabilitydef = capability.definition - self._common_validate_properties(capabilitydef, - props[self.PROPERTIES]) - - # validating capability properties values - for prop in self.get_capability(cap).get_properties_objects(): - prop.validate() - - # tODO(srinivas_tadepalli): temporary work around to validate - # default_instances until standardized in specification - if cap == "scalable" and prop.name == "default_instances": - prop_dict = props[self.PROPERTIES] - min_instances = prop_dict.get("min_instances") - max_instances = prop_dict.get("max_instances") - default_instances = prop_dict.get("default_instances") - if not (min_instances <= default_instances - <= max_instances): - err_msg = ('"properties" of template "%s": ' - '"default_instances" value is not between ' - '"min_instances" and "max_instances".' % - self.name) - ValidationIssueCollector.appendException( - ValidationError(message=err_msg)) - - def _common_validate_properties(self, entitytype, properties): - allowed_props = [] - required_props = [] - for p in entitytype.get_properties_def_objects(): - allowed_props.append(p.name) - # If property is 'required' and has no 'default' value then record - if p.required and p.default is None: - required_props.append(p.name) - # validate all required properties have values - if properties: - req_props_no_value_or_default = [] - self._common_validate_field(properties, allowed_props, - 'properties') - # make sure it's not missing any property required by a tosca type - for r in required_props: - if r not in properties.keys(): - req_props_no_value_or_default.append(r) - # Required properties found without value or a default value - if req_props_no_value_or_default: - ValidationIssueCollector.appendException( - MissingRequiredFieldError( - what='"properties" of template "%s"' % self.name, - required=req_props_no_value_or_default)) - else: - # Required properties in schema, but not in template - if required_props: - ValidationIssueCollector.appendException( - MissingRequiredFieldError( - what='"properties" of template "%s"' % self.name, - required=required_props)) - - def _validate_field(self, template): - if not isinstance(template, dict): - ValidationIssueCollector.appendException( - MissingRequiredFieldError( - what='Template "%s"' % self.name, required=self.TYPE)) - try: - relationship = template.get('relationship') - if relationship and not isinstance(relationship, str): - relationship[self.TYPE] - elif isinstance(relationship, str): - template['relationship'] - else: - template[self.TYPE] - except KeyError: - ValidationIssueCollector.appendException( - MissingRequiredFieldError( - what='Template "%s"' % self.name, required=self.TYPE)) - - def _common_validate_field(self, schema, allowedlist, section): - for name in schema: - if name not in allowedlist: - ValidationIssueCollector.appendException( - UnknownFieldError( - what=('"%(section)s" of template "%(nodename)s"' - % {'section': section, 'nodename': self.name}), - field=name)) - - def _create_properties(self): - props = [] - properties = self.type_definition.get_value(self.PROPERTIES, - self.entity_tpl) or {} - for name, value in properties.items(): - props_def = self.type_definition.get_properties_def() - if props_def and name in props_def: - prop = Property(name, value, - props_def[name].schema, self.custom_def) - props.append(prop) - for p in self.type_definition.get_properties_def_objects(): - if p.default is not None and p.name not in properties.keys(): - prop = Property(p.name, p.default, p.schema, self.custom_def) - props.append(prop) - return props - - def _create_interfaces(self): - interfaces = [] - type_interfaces = None - if isinstance(self.type_definition, RelationshipType): - if isinstance(self.entity_tpl, dict): - if self.INTERFACES in self.entity_tpl: - type_interfaces = self.entity_tpl[self.INTERFACES] - else: - for rel_def, value in self.entity_tpl.items(): - if rel_def != 'type': - rel_def = self.entity_tpl.get(rel_def) - rel = None - if isinstance(rel_def, dict): - rel = rel_def.get('relationship') - if rel: - if self.INTERFACES in rel: - type_interfaces = rel[self.INTERFACES] - break - else: - type_interfaces = self.type_definition.get_value(self.INTERFACES, - self.entity_tpl) - if type_interfaces: - for interface_type, value in type_interfaces.items(): - for op, op_def in value.items(): - iface = InterfacesDef(self.type_definition, - interfacetype=interface_type, - node_template=self, - name=op, - value=op_def) - interfaces.append(iface) - return interfaces - - def get_capability(self, name): - """Provide named capability - - :param name: name of capability - :return: capability object if found, None otherwise - """ - caps = self.get_capabilities() - if caps and name in caps.keys(): - return caps[name] -*/ diff --git a/jtosca/src/main/java/org/onap/sdc/toscaparser/api/NodeTemplate.java b/jtosca/src/main/java/org/onap/sdc/toscaparser/api/NodeTemplate.java index 4fabe38..aeae994 100644 --- a/jtosca/src/main/java/org/onap/sdc/toscaparser/api/NodeTemplate.java +++ b/jtosca/src/main/java/org/onap/sdc/toscaparser/api/NodeTemplate.java @@ -20,7 +20,17 @@ package org.onap.sdc.toscaparser.api; +import static org.onap.sdc.toscaparser.api.elements.EntityType.TOSCA_DEF; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import lombok.Getter; +import lombok.Setter; import org.onap.sdc.toscaparser.api.common.JToscaValidationIssue; +import org.onap.sdc.toscaparser.api.elements.ArtifactDef; import org.onap.sdc.toscaparser.api.elements.EntityType; import org.onap.sdc.toscaparser.api.elements.InterfacesDef; import org.onap.sdc.toscaparser.api.elements.Metadata; @@ -29,35 +39,33 @@ import org.onap.sdc.toscaparser.api.elements.RelationshipType; import org.onap.sdc.toscaparser.api.utils.CopyUtils; import org.onap.sdc.toscaparser.api.utils.ThreadLocalsHolder; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import static org.onap.sdc.toscaparser.api.elements.EntityType.TOSCA_DEF; - public class NodeTemplate extends EntityTemplate { + private static final String METADATA = "metadata"; private LinkedHashMap templates; - private LinkedHashMap customDef; private ArrayList availableRelTpls; private LinkedHashMap availableRelTypes; private LinkedHashMap related; private ArrayList relationshipTpl; private LinkedHashMap _relationships; + @Getter + @Setter private SubstitutionMappings subMappingToscaTemplate; + @Getter + @Setter private TopologyTemplate originComponentTemplate; + @Getter + @Setter private Metadata metadata; - - private static final String METADATA = "metadata"; + @Getter + private Map artifacts; public NodeTemplate(String name, LinkedHashMap ntnodeTemplates, LinkedHashMap ntcustomDef, ArrayList ntavailableRelTpls, LinkedHashMap ntavailableRelTypes) { - this(name, ntnodeTemplates, ntcustomDef, ntavailableRelTpls, - ntavailableRelTypes, null); + this(name, ntnodeTemplates, ntcustomDef, ntavailableRelTpls, ntavailableRelTypes, null); } @SuppressWarnings("unchecked") @@ -68,8 +76,7 @@ public class NodeTemplate extends EntityTemplate { LinkedHashMap ntavailableRelTypes, NodeTemplate parentNodeTemplate) { - super(name, (LinkedHashMap) ntnodeTemplates.get(name), - "node_type", ntcustomDef, parentNodeTemplate); + super(name, (LinkedHashMap) ntnodeTemplates.get(name), "node_type", ntcustomDef, parentNodeTemplate); templates = ntnodeTemplates; _validateFields((LinkedHashMap) templates.get(name)); @@ -81,6 +88,18 @@ public class NodeTemplate extends EntityTemplate { _relationships = new LinkedHashMap(); subMappingToscaTemplate = null; metadata = _metaData(); + artifacts = readArtifacts((Map) templates.get(name)); + } + + private Map readArtifacts(final Map nodetemplate) { + if (nodetemplate.get("artifacts") != null) { + final Map artifactsMap = new HashMap<>(); + ((Map) nodetemplate.get("artifacts")).forEach((name, value) -> { + artifactsMap.put(name, new ArtifactDef((Map) value)); + }); + return artifactsMap; + } + return null; } @SuppressWarnings("unchecked") @@ -126,13 +145,13 @@ public class NodeTemplate extends EntityTemplate { } if (bFound || customDef.get(node) != null) { ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE205", String.format( - "NotImplementedError: Lookup by TOSCA types is not supported. Requirement for \"%s\" can not be full-filled", - getName()))); + "NotImplementedError: Lookup by TOSCA types is not supported. Requirement for \"%s\" can not be full-filled", + getName()))); return null; } if (templates.get(node) == null) { ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE206", String.format( - "KeyError: Node template \"%s\" was not found", node))); + "KeyError: Node template \"%s\" was not found", node))); return null; } NodeTemplate relatedTpl = new NodeTemplate(node, templates, customDef, null, null); @@ -144,7 +163,8 @@ public class NodeTemplate extends EntityTemplate { if (relationship == null) { ArrayList parentReqs = ((NodeType) typeDefinition).getAllRequirements(); if (parentReqs == null) { - ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE207", "ValidationError: parent_req is null")); + ThreadLocalsHolder.getCollector() + .appendValidationIssue(new JToscaValidationIssue("JE207", "ValidationError: parent_req is null")); } else { // for(String key: req.keySet()) { // boolean bFoundRel = false; @@ -194,15 +214,15 @@ public class NodeTemplate extends EntityTemplate { relationshipString = (String) ((LinkedHashMap) relationship).get("type"); if (relationshipString != null) { if (availableRelTypes != null && !availableRelTypes.isEmpty() && - availableRelTypes.get(relationshipString) != null) { + availableRelTypes.get(relationshipString) != null) { ; } else if (!(relationshipString).startsWith(relPrfx)) { relationshipString = relPrfx + relationshipString; } } else { ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE208", String.format( - "MissingRequiredFieldError: \"relationship\" used in template \"%s\" is missing required field \"type\"", - relatedTpl.getName()))); + "MissingRequiredFieldError: \"relationship\" used in template \"%s\" is missing required field \"type\"", + relatedTpl.getName()))); } } for (RelationshipType rtype : ((NodeType) typeDefinition).getRelationship().keySet()) { @@ -320,7 +340,7 @@ public class NodeTemplate extends EntityTemplate { if (requires != null) { if (!(requires instanceof ArrayList)) { ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE209", String.format( - "TypeMismatchError: \"requirements\" of template \"%s\" are not of type \"list\"", name))); + "TypeMismatchError: \"requirements\" of template \"%s\" are not of type \"list\"", name))); } else { for (Object ro : requires) { LinkedHashMap req = (LinkedHashMap) ro; @@ -361,10 +381,10 @@ public class NodeTemplate extends EntityTemplate { DataEntity.validateDatatype("Integer", val, null, null, null); } if (occurrences.size() != 2 || - !(0 <= (int) occurrences.get(0) && (int) occurrences.get(0) <= (int) occurrences.get(1)) || - (int) occurrences.get(1) == 0) { + !(0 <= (int) occurrences.get(0) && (int) occurrences.get(0) <= (int) occurrences.get(1)) || + (int) occurrences.get(1) == 0) { ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE210", String.format( - "InvalidPropertyValueError: property has invalid value %s", occurrences.toString()))); + "InvalidPropertyValueError: property has invalid value %s", occurrences.toString()))); } } @@ -379,15 +399,14 @@ public class NodeTemplate extends EntityTemplate { } if (!bFound) { ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE211", String.format( - "UnknownFieldError: \"requirements\" of template \"%s\" contains unknown field \"%s\"", name, key))); + "UnknownFieldError: \"requirements\" of template \"%s\" contains unknown field \"%s\"", name, key))); } } } @SuppressWarnings("unchecked") private void _validateInterfaces() { - LinkedHashMap ifaces = (LinkedHashMap) - ((NodeType) typeDefinition).getValue(INTERFACES, entityTpl, false); + LinkedHashMap ifaces = (LinkedHashMap) ((NodeType) typeDefinition).getValue(INTERFACES, entityTpl, false); if (ifaces != null) { for (Map.Entry me : ifaces.entrySet()) { String iname = me.getKey(); @@ -410,7 +429,7 @@ public class NodeTemplate extends EntityTemplate { _commonValidateField(value, _collectCustomIfaceOperations(iname), "interfaces"); } else { ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE212", String.format( - "UnknownFieldError: \"interfaces\" of template \"%s\" contains unknown field %s", name, iname))); + "UnknownFieldError: \"interfaces\" of template \"%s\" contains unknown field %s", name, iname))); } } } @@ -419,8 +438,7 @@ public class NodeTemplate extends EntityTemplate { @SuppressWarnings("unchecked") private ArrayList _collectCustomIfaceOperations(String iname) { ArrayList allowedOperations = new ArrayList<>(); - LinkedHashMap nodetypeIfaceDef = (LinkedHashMap) ((NodeType) - typeDefinition).getInterfaces().get(iname); + LinkedHashMap nodetypeIfaceDef = (LinkedHashMap) ((NodeType) typeDefinition).getInterfaces().get(iname); allowedOperations.addAll(nodetypeIfaceDef.keySet()); String ifaceType = (String) nodetypeIfaceDef.get("type"); if (ifaceType != null) { @@ -503,322 +521,14 @@ public class NodeTemplate extends EntityTemplate { } if (!bFound) { ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE213", String.format( - "UnknownFieldError: Node template \"%s\" has unknown field \"%s\"", name, ntname))); + "UnknownFieldError: Node template \"%s\" has unknown field \"%s\"", name, ntname))); } } } - // getter/setter - - // multilevel nesting - public SubstitutionMappings getSubMappingToscaTemplate() { - return subMappingToscaTemplate; - } - - public void setSubMappingToscaTemplate(SubstitutionMappings sm) { - subMappingToscaTemplate = sm; - } - - public Metadata getMetaData() { - return metadata; - } - - public void setMetaData(Metadata metadata) { - this.metadata = metadata; - } - @Override public String toString() { return getName(); } - public TopologyTemplate getOriginComponentTemplate() { - return originComponentTemplate; - } - - public void setOriginComponentTemplate(TopologyTemplate originComponentTemplate) { - this.originComponentTemplate = originComponentTemplate; - } - } - -/*python - -from toscaparser.common.exception import ValidationIssueCollector -from toscaparser.common.exception import InvalidPropertyValueError -from toscaparser.common.exception import MissingRequiredFieldError -from toscaparser.common.exception import TypeMismatchError -from toscaparser.common.exception import UnknownFieldError -from toscaparser.common.exception import ValidationError -from toscaparser.dataentity import DataEntity -from toscaparser.elements.interfaces import CONFIGURE -from toscaparser.elements.interfaces import CONFIGURE_SHORTNAME -from toscaparser.elements.interfaces import INTERFACE_DEF_RESERVED_WORDS -from toscaparser.elements.interfaces import InterfacesDef -from toscaparser.elements.interfaces import LIFECYCLE -from toscaparser.elements.interfaces import LIFECYCLE_SHORTNAME -from toscaparser.elements.relationshiptype import RelationshipType -from toscaparser.entity_template import EntityTemplate -from toscaparser.relationship_template import RelationshipTemplate -from toscaparser.utils.gettextutils import _ - -log = logging.getLogger('tosca') - - -class NodeTemplate(EntityTemplate): - '''Node template from a Tosca profile.''' - def __init__(self, name, node_templates, custom_def=None, - available_rel_tpls=None, available_rel_types=None): - super(NodeTemplate, self).__init__(name, node_templates[name], - 'node_type', - custom_def) - self.templates = node_templates - self._validate_fields(node_templates[name]) - self.custom_def = custom_def - self.related = {} - self.relationship_tpl = [] - self.available_rel_tpls = available_rel_tpls - self.available_rel_types = available_rel_types - self._relationships = {} - self.sub_mapping_tosca_template = None - - @property - def relationships(self): - if not self._relationships: - requires = self.requirements - if requires and isinstance(requires, list): - for r in requires: - for r1, value in r.items(): - explicit = self._get_explicit_relationship(r, value) - if explicit: - for key, value in explicit.items(): - self._relationships[key] = value - return self._relationships - - def _get_explicit_relationship(self, req, value): - """Handle explicit relationship - - For example, - - req: - node: DBMS - relationship: tosca.relationships.HostedOn - """ - explicit_relation = {} - node = value.get('node') if isinstance(value, dict) else value - - if node: - # TO-DO(spzala) implement look up once Glance meta data is available - # to find a matching TOSCA node using the TOSCA types - msg = _('Lookup by TOSCA types is not supported. ' - 'Requirement for "%s" can not be full-filled.') % self.name - if (node in list(self.type_definition.TOSCA_DEF.keys()) - or node in self.custom_def): - ValidationIssueCollector.appendException(NotImplementedError(msg)) - return - - if node not in self.templates: - ValidationIssueCollector.appendException( - KeyError(_('Node template "%s" was not found.') % node)) - return - - related_tpl = NodeTemplate(node, self.templates, self.custom_def) - relationship = value.get('relationship') \ - if isinstance(value, dict) else None - # check if it's type has relationship defined - if not relationship: - parent_reqs = self.type_definition.get_all_requirements() - if parent_reqs is None: - ValidationIssueCollector.appendException( - ValidationError(message='parent_req is ' + - str(parent_reqs))) - else: - for key in req.keys(): - for req_dict in parent_reqs: - if key in req_dict.keys(): - relationship = (req_dict.get(key). - get('relationship')) - break - if relationship: - found_relationship_tpl = False - # apply available relationship templates if found - if self.available_rel_tpls: - for tpl in self.available_rel_tpls: - if tpl.name == relationship: - rtype = RelationshipType(tpl.type, None, - self.custom_def) - explicit_relation[rtype] = related_tpl - tpl.target = related_tpl - tpl.source = self - self.relationship_tpl.append(tpl) - found_relationship_tpl = True - # create relationship template object. - rel_prfx = self.type_definition.RELATIONSHIP_PREFIX - if not found_relationship_tpl: - if isinstance(relationship, dict): - relationship = relationship.get('type') - if relationship: - if self.available_rel_types and \ - relationship in self.available_rel_types.keys(): - pass - elif not relationship.startswith(rel_prfx): - relationship = rel_prfx + relationship - else: - ValidationIssueCollector.appendException( - MissingRequiredFieldError( - what=_('"relationship" used in template ' - '"%s"') % related_tpl.name, - required=self.TYPE)) - for rtype in self.type_definition.relationship.keys(): - if rtype.type == relationship: - explicit_relation[rtype] = related_tpl - related_tpl._add_relationship_template(req, - rtype.type, - self) - elif self.available_rel_types: - if relationship in self.available_rel_types.keys(): - rel_type_def = self.available_rel_types.\ - get(relationship) - if 'derived_from' in rel_type_def: - super_type = \ - rel_type_def.get('derived_from') - if not super_type.startswith(rel_prfx): - super_type = rel_prfx + super_type - if rtype.type == super_type: - explicit_relation[rtype] = related_tpl - related_tpl.\ - _add_relationship_template( - req, rtype.type, self) - return explicit_relation - - def _add_relationship_template(self, requirement, rtype, source): - req = requirement.copy() - req['type'] = rtype - tpl = RelationshipTemplate(req, rtype, self.custom_def, self, source) - self.relationship_tpl.append(tpl) - - def get_relationship_template(self): - return self.relationship_tpl - - def _add_next(self, nodetpl, relationship): - self.related[nodetpl] = relationship - - @property - def related_nodes(self): - if not self.related: - for relation, node in self.type_definition.relationship.items(): - for tpl in self.templates: - if tpl == node.type: - self.related[NodeTemplate(tpl)] = relation - return self.related.keys() - - def validate(self, tosca_tpl=None): - self._validate_capabilities() - self._validate_requirements() - self._validate_properties(self.entity_tpl, self.type_definition) - self._validate_interfaces() - for prop in self.get_properties_objects(): - prop.validate() - - def _validate_requirements(self): - type_requires = self.type_definition.get_all_requirements() - allowed_reqs = ["template"] - if type_requires: - for treq in type_requires: - for key, value in treq.items(): - allowed_reqs.append(key) - if isinstance(value, dict): - for key in value: - allowed_reqs.append(key) - - requires = self.type_definition.get_value(self.REQUIREMENTS, - self.entity_tpl) - if requires: - if not isinstance(requires, list): - ValidationIssueCollector.appendException( - TypeMismatchError( - what='"requirements" of template "%s"' % self.name, - type='list')) - else: - for req in requires: - for r1, value in req.items(): - if isinstance(value, dict): - self._validate_requirements_keys(value) - self._validate_requirements_properties(value) - allowed_reqs.append(r1) - self._common_validate_field(req, allowed_reqs, - 'requirements') - - def _validate_requirements_properties(self, requirements): - # TO-DO(anyone): Only occurrences property of the requirements is - # validated here. Validation of other requirement properties are being - # validated in different files. Better to keep all the requirements - # properties validation here. - for key, value in requirements.items(): - if key == 'occurrences': - self._validate_occurrences(value) - break - - def _validate_occurrences(self, occurrences): - DataEntity.validate_datatype('list', occurrences) - for value in occurrences: - DataEntity.validate_datatype('integer', value) - if len(occurrences) != 2 or not (0 <= occurrences[0] <= occurrences[1]) \ - or occurrences[1] == 0: - ValidationIssueCollector.appendException( - InvalidPropertyValueError(what=(occurrences))) - - def _validate_requirements_keys(self, requirement): - for key in requirement.keys(): - if key not in self.REQUIREMENTS_SECTION: - ValidationIssueCollector.appendException( - UnknownFieldError( - what='"requirements" of template "%s"' % self.name, - field=key)) - - def _validate_interfaces(self): - ifaces = self.type_definition.get_value(self.INTERFACES, - self.entity_tpl) - if ifaces: - for name, value in ifaces.items(): - if name in (LIFECYCLE, LIFECYCLE_SHORTNAME): - self._common_validate_field( - value, InterfacesDef. - interfaces_node_lifecycle_operations, - 'interfaces') - elif name in (CONFIGURE, CONFIGURE_SHORTNAME): - self._common_validate_field( - value, InterfacesDef. - interfaces_relationship_configure_operations, - 'interfaces') - elif name in self.type_definition.interfaces.keys(): - self._common_validate_field( - value, - self._collect_custom_iface_operations(name), - 'interfaces') - else: - ValidationIssueCollector.appendException( - UnknownFieldError( - what='"interfaces" of template "%s"' % - self.name, field=name)) - - def _collect_custom_iface_operations(self, name): - allowed_operations = [] - nodetype_iface_def = self.type_definition.interfaces[name] - allowed_operations.extend(nodetype_iface_def.keys()) - if 'type' in nodetype_iface_def: - iface_type = nodetype_iface_def['type'] - if iface_type in self.type_definition.custom_def: - iface_type_def = self.type_definition.custom_def[iface_type] - else: - iface_type_def = self.type_definition.TOSCA_DEF[iface_type] - allowed_operations.extend(iface_type_def.keys()) - allowed_operations = [op for op in allowed_operations if - op not in INTERFACE_DEF_RESERVED_WORDS] - return allowed_operations - - def _validate_fields(self, nodetemplate): - for name in nodetemplate.keys(): - if name not in self.SECTIONS and name not in self.SPECIAL_SECTIONS: - ValidationIssueCollector.appendException( - UnknownFieldError(what='Node template "%s"' % self.name, - field=name))*/ diff --git a/jtosca/src/main/java/org/onap/sdc/toscaparser/api/ToscaTemplate.java b/jtosca/src/main/java/org/onap/sdc/toscaparser/api/ToscaTemplate.java index ddb8ddb..e2ec62a 100644 --- a/jtosca/src/main/java/org/onap/sdc/toscaparser/api/ToscaTemplate.java +++ b/jtosca/src/main/java/org/onap/sdc/toscaparser/api/ToscaTemplate.java @@ -36,12 +36,11 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Predicate; - import org.onap.sdc.toscaparser.api.common.JToscaException; import org.onap.sdc.toscaparser.api.common.JToscaValidationIssue; import org.onap.sdc.toscaparser.api.common.ValidationIssueCollector; -import org.onap.sdc.toscaparser.api.elements.EntityType; import org.onap.sdc.toscaparser.api.elements.DataType; +import org.onap.sdc.toscaparser.api.elements.EntityType; import org.onap.sdc.toscaparser.api.elements.Metadata; import org.onap.sdc.toscaparser.api.extensions.ExtTools; import org.onap.sdc.toscaparser.api.parameters.Input; @@ -56,8 +55,7 @@ import org.yaml.snakeyaml.Yaml; public class ToscaTemplate extends Object { public static final int MAX_LEVELS = 20; - private static Logger log = LoggerFactory.getLogger(ToscaTemplate.class.getName()); - + private static final Logger log = LoggerFactory.getLogger(ToscaTemplate.class.getName()); // TOSCA template key names private static final String DEFINITION_VERSION = "tosca_definitions_version"; private static final String DEFAULT_NAMESPACE = "tosca_default_namespace"; @@ -78,18 +76,16 @@ public class ToscaTemplate extends Object { private static final String POLICY_TYPES = "policy_types"; private static final String GROUP_TYPES = "group_types"; private static final String REPOSITORIES = "repositories"; - - private static String SECTIONS[] = { - DEFINITION_VERSION, DEFAULT_NAMESPACE, TEMPLATE_NAME, - TOPOLOGY_TEMPLATE, TEMPLATE_AUTHOR, TEMPLATE_VERSION, - DESCRIPTION, IMPORTS, DSL_DEFINITIONS, NODE_TYPES, - RELATIONSHIP_TYPES, RELATIONSHIP_TEMPLATES, - CAPABILITY_TYPES, ARTIFACT_TYPES, DATA_TYPES, - INTERFACE_TYPES, POLICY_TYPES, GROUP_TYPES, REPOSITORIES - }; - // Sections that are specific to individual template definitions private static final String METADATA = "metadata"; + private static String SECTIONS[] = { + DEFINITION_VERSION, DEFAULT_NAMESPACE, TEMPLATE_NAME, + TOPOLOGY_TEMPLATE, TEMPLATE_AUTHOR, TEMPLATE_VERSION, + DESCRIPTION, IMPORTS, DSL_DEFINITIONS, NODE_TYPES, + RELATIONSHIP_TYPES, RELATIONSHIP_TEMPLATES, + CAPABILITY_TYPES, ARTIFACT_TYPES, DATA_TYPES, + INTERFACE_TYPES, POLICY_TYPES, GROUP_TYPES, REPOSITORIES + }; private static ArrayList SPECIAL_SECTIONS; private ExtTools exttools = new ExtTools(); @@ -152,17 +148,18 @@ public class ToscaTemplate extends Object { VALID_TEMPLATE_VERSIONS = new ArrayList<>(); VALID_TEMPLATE_VERSIONS.add("tosca_simple_yaml_1_0"); VALID_TEMPLATE_VERSIONS.add("tosca_simple_yaml_1_1"); + VALID_TEMPLATE_VERSIONS.add("tosca_simple_yaml_1_2"); + VALID_TEMPLATE_VERSIONS.add("tosca_simple_yaml_1_3"); VALID_TEMPLATE_VERSIONS.addAll(exttools.getVersions()); ADDITIONAL_SECTIONS = new LinkedHashMap<>(); SPECIAL_SECTIONS = new ArrayList<>(); SPECIAL_SECTIONS.add(METADATA); ADDITIONAL_SECTIONS.put("tosca_simple_yaml_1_0", SPECIAL_SECTIONS); ADDITIONAL_SECTIONS.put("tosca_simple_yaml_1_1", SPECIAL_SECTIONS); + ADDITIONAL_SECTIONS.put("tosca_simple_yaml_1_2", SPECIAL_SECTIONS); + ADDITIONAL_SECTIONS.put("tosca_simple_yaml_1_3", SPECIAL_SECTIONS); ADDITIONAL_SECTIONS.putAll(exttools.getSections()); - //long startTime = System.nanoTime(); - - isFile = aFile; inputPath = null; path = null; @@ -182,7 +179,7 @@ public class ToscaTemplate extends Object { if (path != null && !path.isEmpty()) { try (InputStream input = new FileInputStream(new File(path));) { //System.out.println("Loading YAML file " + path); - log.debug("ToscaTemplate Loading YAMEL file {}", path); + log.debug("ToscaTemplate Loading YAML file {}", path); Yaml yaml = new Yaml(); Object data = yaml.load(input); this.tpl = (LinkedHashMap) data; @@ -190,13 +187,13 @@ public class ToscaTemplate extends Object { log.error("ToscaTemplate - Exception loading yaml: {}", e.getMessage()); log.error("Exception", e); ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE275", - "ToscaTemplate - Exception loading yaml: -> " + e.getMessage())); + "ToscaTemplate - Exception loading yaml: -> " + e.getMessage())); return; } catch (Exception e) { log.error("ToscaTemplate - Error loading yaml, aborting -> ", e.getMessage()); log.error("Exception", e); ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE275", - "ToscaTemplate - Error loading yaml, aborting -> " + e.getMessage())); + "ToscaTemplate - Error loading yaml, aborting -> " + e.getMessage())); return; } @@ -215,7 +212,7 @@ public class ToscaTemplate extends Object { tpl = yamlDictTpl; } else { ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE244", - "ValueError: No path or yaml_dict_tpl was provided. There is nothing to parse")); + "ValueError: No path or yaml_dict_tpl was provided. There is nothing to parse")); log.debug("ToscaTemplate ValueError: No path or yaml_dict_tpl was provided. There is nothing to parse"); } @@ -264,12 +261,12 @@ public class ToscaTemplate extends Object { private TopologyTemplate _topologyTemplate() { return new TopologyTemplate( - _tplTopologyTemplate(), - _getAllCustomDefs(imports), - relationshipTypes, - parsedParams, - null, - resolveGetInput); + _tplTopologyTemplate(), + _getAllCustomDefs(imports), + relationshipTypes, + parsedParams, + null, + resolveGetInput); } private ArrayList _inputs() { @@ -314,7 +311,7 @@ public class ToscaTemplate extends Object { @SuppressWarnings("unchecked") private ArrayList _tplRepositories() { LinkedHashMap repositories = - (LinkedHashMap) tpl.get(REPOSITORIES); + (LinkedHashMap) tpl.get(REPOSITORIES); ArrayList reposit = new ArrayList<>(); if (repositories != null) { for (Map.Entry me : repositories.entrySet()) { @@ -350,7 +347,7 @@ public class ToscaTemplate extends Object { @SuppressWarnings("unchecked") private HashSet getTopologyDataTypes() { LinkedHashMap value = - (LinkedHashMap) tpl.get(DATA_TYPES); + (LinkedHashMap) tpl.get(DATA_TYPES); HashSet datatypes = new HashSet<>(); if (value != null) { customDefsFinal.putAll(value); @@ -360,7 +357,6 @@ public class ToscaTemplate extends Object { } } - return datatypes; } @@ -380,10 +376,9 @@ public class ToscaTemplate extends Object { @SuppressWarnings("unchecked") private LinkedHashMap _getAllCustomDefs(Object alImports) { - String types[] = { - IMPORTS, NODE_TYPES, CAPABILITY_TYPES, RELATIONSHIP_TYPES, - DATA_TYPES, INTERFACE_TYPES, POLICY_TYPES, GROUP_TYPES + IMPORTS, NODE_TYPES, CAPABILITY_TYPES, RELATIONSHIP_TYPES, ARTIFACT_TYPES, + DATA_TYPES, INTERFACE_TYPES, POLICY_TYPES, GROUP_TYPES }; List> imports = (List>) alImports; @@ -480,12 +475,13 @@ public class ToscaTemplate extends Object { */ private String getPath(String path, String importFileName) { String tempFullPath = (Paths.get(path).toAbsolutePath().getParent() - .toString() + File.separator + importFileName.replace("../", "")).replace('\\', '/'); + .toString() + File.separator + importFileName.replace("../", "")).replace('\\', '/'); String tempPartialPath = (Paths.get(path).toAbsolutePath().getParent().toString()).replace('\\', '/'); - if (Files.exists(Paths.get(tempFullPath))) + if (Files.exists(Paths.get(tempFullPath))) { return tempFullPath; - else + } else { return getPath(tempPartialPath, importFileName); + } } /** @@ -507,10 +503,10 @@ public class ToscaTemplate extends Object { Map.Entry val = it.next(); if (val.getValue().contains("/")) { importFileName = (Paths.get(rootPath).toAbsolutePath().getParent().toString() + File - .separator + val.getValue().replace("../", "")).replace('\\', '/'); + .separator + val.getValue().replace("../", "")).replace('\\', '/'); } else { importFileName = (Paths.get(path).toAbsolutePath().getParent().toString() + File - .separator + val.getValue().replace("../", "")).replace('\\', '/'); + .separator + val.getValue().replace("../", "")).replace('\\', '/'); } retMap.put("importFileName", importFileName); retMap.put("importRelativeName", val.getValue()); @@ -530,8 +526,8 @@ public class ToscaTemplate extends Object { * @return the list containing filtered imports */ private List> filterImportsForRecursion(List> - customImports, Map importNameDetails) { + customImports, Map importNameDetails) { for (Map map1 : customImports) { for (Map.Entry entry : map1.entrySet()) { Map innerMostMap = (Map) entry.getValue(); @@ -627,27 +623,27 @@ public class ToscaTemplate extends Object { this.processedImports = new HashSet<>(); for (Map.Entry me : nestedToscaTplsWithTopology.entrySet()) { LinkedHashMap toscaTpl = - (LinkedHashMap) me.getValue(); + (LinkedHashMap) me.getValue(); for (NodeTemplate nt : tt.getNodeTemplates()) { if (_isSubMappedNode(nt, toscaTpl)) { parsedParams = _getParamsForNestedTemplate(nt); ArrayList alim = (ArrayList) toscaTpl.get(IMPORTS); LinkedHashMap topologyTpl = - (LinkedHashMap) toscaTpl.get(TOPOLOGY_TEMPLATE); + (LinkedHashMap) toscaTpl.get(TOPOLOGY_TEMPLATE); TopologyTemplate topologyWithSubMapping = - new TopologyTemplate(topologyTpl, - _getAllCustomDefs(alim), - relationshipTypes, - parsedParams, - nt, - resolveGetInput); + new TopologyTemplate(topologyTpl, + _getAllCustomDefs(alim), + relationshipTypes, + parsedParams, + nt, + resolveGetInput); nt.setOriginComponentTemplate(topologyWithSubMapping); if (topologyWithSubMapping.getSubstitutionMappings() != null) { // Record nested topology templates in top level template //nestedToscaTemplatesWithTopology.add(topologyWithSubMapping); // Set substitution mapping object for mapped node nt.setSubMappingToscaTemplate( - topologyWithSubMapping.getSubstitutionMappings()); + topologyWithSubMapping.getSubstitutionMappings()); _handleNestedToscaTemplatesWithTopology(topologyWithSubMapping); } } @@ -689,7 +685,7 @@ public class ToscaTemplate extends Object { String sVersion = _tplVersion(); if (sVersion == null) { ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE245", String.format( - "MissingRequiredField: Template is missing required field \"%s\"", DEFINITION_VERSION))); + "MissingRequiredField: Template is missing required field \"%s\"", DEFINITION_VERSION))); } else { _validateVersion(sVersion); this.version = sVersion; @@ -706,14 +702,14 @@ public class ToscaTemplate extends Object { // check ADDITIONAL_SECTIONS if (!bFound) { if (ADDITIONAL_SECTIONS.get(version) != null && - ADDITIONAL_SECTIONS.get(version).contains(sKey)) { + ADDITIONAL_SECTIONS.get(version).contains(sKey)) { bFound = true; } } if (!bFound) { ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE246", String.format( - "UnknownFieldError: Template contains unknown field \"%s\"", - sKey))); + "UnknownFieldError: Template contains unknown field \"%s\"", + sKey))); } } } @@ -728,11 +724,9 @@ public class ToscaTemplate extends Object { } if (!bFound) { ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE247", String.format( - "InvalidTemplateVersion: \"%s\" is invalid. Valid versions are %s", - sVersion, VALID_TEMPLATE_VERSIONS.toString()))); - } else if ((!sVersion.equals("tosca_simple_yaml_1_0") && !sVersion.equals("tosca_simple_yaml_1_1"))) { + "InvalidTemplateVersion: \"%s\" is invalid. Valid versions are %s", sVersion, VALID_TEMPLATE_VERSIONS.toString()))); + } else if (!sVersion.startsWith("tosca_simple_yaml_1_")) { EntityType.updateDefinitions(sVersion); - } } @@ -756,7 +750,8 @@ public class ToscaTemplate extends Object { return csar.getTempDir() + File.separator + csar.getMainTemplate(); } } else { - ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE248", "ValueError: " + _path + " is not a valid file")); + ThreadLocalsHolder.getCollector() + .appendValidationIssue(new JToscaValidationIssue("JE248", "ValueError: " + _path + " is not a valid file")); return null; } return null; @@ -768,7 +763,8 @@ public class ToscaTemplate extends Object { if (validationIssuesCaught > 0) { List validationIssueStrings = ThreadLocalsHolder.getCollector().getValidationIssueReport(); log.trace("####################################################################################################"); - log.trace("ToscaTemplate - verifyTemplate - {} Parsing Critical{} occurred...", validationIssuesCaught, (validationIssuesCaught > 1 ? "s" : "")); + log.trace("ToscaTemplate - verifyTemplate - {} Parsing Critical{} occurred...", validationIssuesCaught, + (validationIssuesCaught > 1 ? "s" : "")); for (String s : validationIssueStrings) { log.trace("{}. CSAR name - {}", s, inputPath); } @@ -837,8 +833,8 @@ public class ToscaTemplate extends Object { private boolean _isSubMappedNode(NodeTemplate nt, LinkedHashMap toscaTpl) { // Return True if the nodetemple is substituted if (nt != null && nt.getSubMappingToscaTemplate() == null && - getSubMappingNodeType(toscaTpl).equals(nt.getType()) && - nt.getInterfaces().size() < 1) { + getSubMappingNodeType(toscaTpl).equals(nt.getType()) && + nt.getInterfaces().size() < 1) { return true; } return false; @@ -865,7 +861,7 @@ public class ToscaTemplate extends Object { // Return substitution mappings node type if (toscaTpl != null) { return TopologyTemplate.getSubMappingNodeType( - (LinkedHashMap) toscaTpl.get(TOPOLOGY_TEMPLATE)); + (LinkedHashMap) toscaTpl.get(TOPOLOGY_TEMPLATE)); } return null; } @@ -873,7 +869,7 @@ public class ToscaTemplate extends Object { public boolean hasNestedTemplates() { // Return True if the tosca template has nested templates return nestedToscaTemplatesWithTopology != null && - nestedToscaTemplatesWithTopology.size() >= 1; + nestedToscaTemplatesWithTopology.size() >= 1; } @@ -897,33 +893,33 @@ public class ToscaTemplate extends Object { @Override public String toString() { return "ToscaTemplate{" + - "exttools=" + exttools + - ", VALID_TEMPLATE_VERSIONS=" + VALID_TEMPLATE_VERSIONS + - ", ADDITIONAL_SECTIONS=" + ADDITIONAL_SECTIONS + - ", isFile=" + isFile + - ", path='" + path + '\'' + - ", inputPath='" + inputPath + '\'' + - ", parsedParams=" + parsedParams + - ", tpl=" + tpl + - ", version='" + version + '\'' + - ", imports=" + imports + - ", relationshipTypes=" + relationshipTypes + - ", metaData=" + metaData + - ", description='" + description + '\'' + - ", topologyTemplate=" + topologyTemplate + - ", repositories=" + repositories + - ", inputs=" + inputs + - ", relationshipTemplates=" + relationshipTemplates + - ", nodeTemplates=" + nodeTemplates + - ", outputs=" + outputs + - ", policies=" + policies + - ", nestedToscaTplsWithTopology=" + nestedToscaTplsWithTopology + - ", nestedToscaTemplatesWithTopology=" + nestedToscaTemplatesWithTopology + - ", graph=" + graph + - ", csarTempDir='" + csarTempDir + '\'' + - ", nestingLoopCounter=" + nestingLoopCounter + - ", dataTypes=" + dataTypes + - '}'; + "exttools=" + exttools + + ", VALID_TEMPLATE_VERSIONS=" + VALID_TEMPLATE_VERSIONS + + ", ADDITIONAL_SECTIONS=" + ADDITIONAL_SECTIONS + + ", isFile=" + isFile + + ", path='" + path + '\'' + + ", inputPath='" + inputPath + '\'' + + ", parsedParams=" + parsedParams + + ", tpl=" + tpl + + ", version='" + version + '\'' + + ", imports=" + imports + + ", relationshipTypes=" + relationshipTypes + + ", metaData=" + metaData + + ", description='" + description + '\'' + + ", topologyTemplate=" + topologyTemplate + + ", repositories=" + repositories + + ", inputs=" + inputs + + ", relationshipTemplates=" + relationshipTemplates + + ", nodeTemplates=" + nodeTemplates + + ", outputs=" + outputs + + ", policies=" + policies + + ", nestedToscaTplsWithTopology=" + nestedToscaTplsWithTopology + + ", nestedToscaTemplatesWithTopology=" + nestedToscaTemplatesWithTopology + + ", graph=" + graph + + ", csarTempDir='" + csarTempDir + '\'' + + ", nestingLoopCounter=" + nestingLoopCounter + + ", dataTypes=" + dataTypes + + '}'; } public List getInputs(boolean annotationsRequired) { diff --git a/jtosca/src/main/java/org/onap/sdc/toscaparser/api/elements/ArtifactDef.java b/jtosca/src/main/java/org/onap/sdc/toscaparser/api/elements/ArtifactDef.java new file mode 100644 index 0000000..f2713ee --- /dev/null +++ b/jtosca/src/main/java/org/onap/sdc/toscaparser/api/elements/ArtifactDef.java @@ -0,0 +1,55 @@ +/* + * - + * ============LICENSE_START======================================================= + * Copyright (C) 2022 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.onap.sdc.toscaparser.api.elements; + +import java.util.Map; +import lombok.Getter; +import lombok.NonNull; + +@Getter +public class ArtifactDef { + + @NonNull + private final String type; + @NonNull + private final String file; + private final String repository; + private final String description; + private final String deploy_path; + private final String artifact_version; + private final String checksum; + private final String checksum_algorithm; + private final Map properties; + + public ArtifactDef(final Map mapDef) { + type = (String) mapDef.get("type"); + file = (String) mapDef.get("file"); + repository = (String) mapDef.get("repository"); + description = (String) mapDef.get("description"); + deploy_path = (String) mapDef.get("deploy_path"); + artifact_version = (String) mapDef.get("artifact_version"); + checksum = (String) mapDef.get("checksum"); + checksum_algorithm = (String) mapDef.get("checksum_algorithm"); + properties = (Map) mapDef.get("properties"); + } + +} diff --git a/jtosca/src/main/java/org/onap/sdc/toscaparser/api/elements/ArtifactTypeDef.java b/jtosca/src/main/java/org/onap/sdc/toscaparser/api/elements/ArtifactTypeDef.java index 9cf8c6c..c160a32 100644 --- a/jtosca/src/main/java/org/onap/sdc/toscaparser/api/elements/ArtifactTypeDef.java +++ b/jtosca/src/main/java/org/onap/sdc/toscaparser/api/elements/ArtifactTypeDef.java @@ -21,7 +21,9 @@ package org.onap.sdc.toscaparser.api.elements; import java.util.LinkedHashMap; +import lombok.Getter; +@Getter public class ArtifactTypeDef extends StatefulEntityType { private String type; @@ -29,7 +31,6 @@ public class ArtifactTypeDef extends StatefulEntityType { private LinkedHashMap properties; private LinkedHashMap parentArtifacts; - public ArtifactTypeDef(String type, LinkedHashMap customDef) { super(type, ARTIFACT_PREFIX, customDef); @@ -76,46 +77,4 @@ public class ArtifactTypeDef extends StatefulEntityType { return null; } - public String getType() { - return type; - } - } - -/*python -class ArtifactTypeDef(StatefulEntityType): - '''TOSCA built-in artifacts type.''' - - def __init__(self, atype, custom_def=None): - super(ArtifactTypeDef, self).__init__(atype, self.ARTIFACT_PREFIX, - custom_def) - self.type = atype - self.custom_def = custom_def - self.properties = None - if self.PROPERTIES in self.defs: - self.properties = self.defs[self.PROPERTIES] - self.parent_artifacts = self._get_parent_artifacts() - - def _get_parent_artifacts(self): - artifacts = {} - parent_artif = self.parent_type.type if self.parent_type else None - if parent_artif: - while parent_artif != 'tosca.artifacts.Root': - artifacts[parent_artif] = self.TOSCA_DEF[parent_artif] - parent_artif = artifacts[parent_artif]['derived_from'] - return artifacts - - @property - def parent_type(self): - '''Return a artifact entity from which this entity is derived.''' - if not hasattr(self, 'defs'): - return None - partifact_entity = self.derived_from(self.defs) - if partifact_entity: - return ArtifactTypeDef(partifact_entity, self.custom_def) - - def get_artifact(self, name): - '''Return the definition of an artifact field by name.''' - if name in self.defs: - return self.defs[name] -*/ diff --git a/jtosca/src/main/java/org/onap/sdc/toscaparser/api/elements/TypeValidation.java b/jtosca/src/main/java/org/onap/sdc/toscaparser/api/elements/TypeValidation.java index 18dd5ca..a30e25c 100644 --- a/jtosca/src/main/java/org/onap/sdc/toscaparser/api/elements/TypeValidation.java +++ b/jtosca/src/main/java/org/onap/sdc/toscaparser/api/elements/TypeValidation.java @@ -62,6 +62,8 @@ public class TypeValidation { ArrayList vtv = new ArrayList<>(); vtv.add("tosca_simple_yaml_1_0"); vtv.add("tosca_simple_yaml_1_1"); + vtv.add("tosca_simple_yaml_1_2"); + vtv.add("tosca_simple_yaml_1_3"); ExtTools exttools = new ExtTools(); vtv.addAll(exttools.getVersions()); return vtv; diff --git a/sdc-tosca/src/main/java/org/onap/sdc/tosca/parser/elements/EntityDetails.java b/sdc-tosca/src/main/java/org/onap/sdc/tosca/parser/elements/EntityDetails.java index afdb90e..09ad6b4 100644 --- a/sdc-tosca/src/main/java/org/onap/sdc/tosca/parser/elements/EntityDetails.java +++ b/sdc-tosca/src/main/java/org/onap/sdc/tosca/parser/elements/EntityDetails.java @@ -26,7 +26,6 @@ import org.onap.sdc.tosca.parser.enums.SdcTypes; import org.onap.sdc.toscaparser.api.*; import org.onap.sdc.toscaparser.api.parameters.Input; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; @@ -132,7 +131,7 @@ public abstract class EntityDetails implements IEntityDetails { List children =((NodeTemplate) ((NodeTemplateEntityDetails)member).getEntityTemplate()) .getSubMappingToscaTemplate().getNodeTemplates(); List vfcChildren = children.stream() - .filter(c -> SdcTypes.VFC.getValue().equals(c.getMetaData().getValue(TYPE))) + .filter(c -> SdcTypes.VFC.getValue().equals(c.getMetadata().getValue(TYPE))) .collect(toList()); return !vfcChildren.isEmpty(); } diff --git a/sdc-tosca/src/main/java/org/onap/sdc/tosca/parser/elements/NodeTemplateEntityDetails.java b/sdc-tosca/src/main/java/org/onap/sdc/tosca/parser/elements/NodeTemplateEntityDetails.java index b5c1340..890a01c 100644 --- a/sdc-tosca/src/main/java/org/onap/sdc/tosca/parser/elements/NodeTemplateEntityDetails.java +++ b/sdc-tosca/src/main/java/org/onap/sdc/tosca/parser/elements/NodeTemplateEntityDetails.java @@ -45,7 +45,7 @@ public class NodeTemplateEntityDetails extends EntityDetails { @Override public Metadata getMetadata() { - return nodeTemplate.getMetaData(); + return nodeTemplate.getMetadata(); } @Override diff --git a/sdc-tosca/src/main/java/org/onap/sdc/tosca/parser/elements/queries/NodeTemplateEntityQuery.java b/sdc-tosca/src/main/java/org/onap/sdc/tosca/parser/elements/queries/NodeTemplateEntityQuery.java index 352d28d..caafa1d 100644 --- a/sdc-tosca/src/main/java/org/onap/sdc/tosca/parser/elements/queries/NodeTemplateEntityQuery.java +++ b/sdc-tosca/src/main/java/org/onap/sdc/tosca/parser/elements/queries/NodeTemplateEntityQuery.java @@ -79,9 +79,9 @@ public class NodeTemplateEntityQuery extends EntityQuery { private Stream filter(final List nodeTemplateList) { return nodeTemplateList.stream() - .filter(nt -> isSearchCriteriaMatched(nt.getMetaData(), nt.getType())) + .filter(nt -> isSearchCriteriaMatched(nt.getMetadata(), nt.getType())) .filter(nt -> getNodeTemplateType() == null || - isStringMatchingOrNull(nt.getMetaData().getValue(SdcPropertyNames.PROPERTY_NAME_TYPE), + isStringMatchingOrNull(nt.getMetadata().getValue(SdcPropertyNames.PROPERTY_NAME_TYPE), getNodeTemplateType().getValue())); } diff --git a/sdc-tosca/src/main/java/org/onap/sdc/tosca/parser/elements/queries/TopologyTemplateQuery.java b/sdc-tosca/src/main/java/org/onap/sdc/tosca/parser/elements/queries/TopologyTemplateQuery.java index 99dd7fd..88e1eac 100644 --- a/sdc-tosca/src/main/java/org/onap/sdc/tosca/parser/elements/queries/TopologyTemplateQuery.java +++ b/sdc-tosca/src/main/java/org/onap/sdc/tosca/parser/elements/queries/TopologyTemplateQuery.java @@ -66,7 +66,7 @@ public class TopologyTemplateQuery { } public Boolean isMatchingSearchCriteria(NodeTemplate nodeTemplate) { - boolean isMatched = Objects.nonNull(nodeTemplate.getMetaData()) && isSearchedTemplate(nodeTemplate.getMetaData()); + boolean isMatched = Objects.nonNull(nodeTemplate.getMetadata()) && isSearchedTemplate(nodeTemplate.getMetadata()); if(logger.isDebugEnabled()) { logger.debug("Node template {} is{} matching search criteria", nodeTemplate.getName(), isMatched ? "" : " not"); } diff --git a/sdc-tosca/src/main/java/org/onap/sdc/tosca/parser/impl/QueryProcessor.java b/sdc-tosca/src/main/java/org/onap/sdc/tosca/parser/impl/QueryProcessor.java index c703e2c..5bbd2aa 100644 --- a/sdc-tosca/src/main/java/org/onap/sdc/tosca/parser/impl/QueryProcessor.java +++ b/sdc-tosca/src/main/java/org/onap/sdc/tosca/parser/impl/QueryProcessor.java @@ -126,7 +126,7 @@ class QueryProcessor { final List topologyTemplateList = Collections.emptyList(); boolean isTopologyTemplateFound = isRecursive ? - SdcTypes.isComplex(current.getMetaData().getValue(SdcPropertyNames.PROPERTY_NAME_TYPE)) + SdcTypes.isComplex(current.getMetadata().getValue(SdcPropertyNames.PROPERTY_NAME_TYPE)) : topologyTemplateQuery.isMatchingSearchCriteria(current); if (isTopologyTemplateFound) { topologyTemplateList.add(current); @@ -135,7 +135,7 @@ class QueryProcessor { return topologyTemplateList; } } - if (SdcTypes.isComplex(current.getMetaData().getValue(SdcPropertyNames.PROPERTY_NAME_TYPE)) && + if (SdcTypes.isComplex(current.getMetadata().getValue(SdcPropertyNames.PROPERTY_NAME_TYPE)) && current.getSubMappingToscaTemplate() != null) { //search the node template inside a given topology template topologyTemplateList.addAll(current.getSubMappingToscaTemplate().getNodeTemplates() diff --git a/sdc-tosca/src/main/java/org/onap/sdc/tosca/parser/impl/SdcCsarHelperImpl.java b/sdc-tosca/src/main/java/org/onap/sdc/tosca/parser/impl/SdcCsarHelperImpl.java index 921a145..20e0582 100644 --- a/sdc-tosca/src/main/java/org/onap/sdc/tosca/parser/impl/SdcCsarHelperImpl.java +++ b/sdc-tosca/src/main/java/org/onap/sdc/tosca/parser/impl/SdcCsarHelperImpl.java @@ -7,9 +7,9 @@ * 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. @@ -20,20 +20,20 @@ package org.onap.sdc.tosca.parser.impl; -import java.util.List; +import static java.util.stream.Collectors.toList; + import java.util.ArrayList; import java.util.Arrays; -import java.util.Map; -import java.util.Map.Entry; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; -import java.util.Optional; -import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import java.util.Objects; +import java.util.Optional; import java.util.stream.Collectors; -import static java.util.stream.Collectors.toList; - import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; @@ -74,9 +74,9 @@ public class SdcCsarHelperImpl implements ISdcCsarHelper { private static final String PATH_DELIMITER = "#"; private static final String CUSTOMIZATION_UUID = "customizationUUID"; private static final String GROUPS_VF_MODULE = "org.openecomp.groups.VfModule"; + private static final Logger log = LoggerFactory.getLogger(SdcCsarHelperImpl.class.getName()); private ToscaTemplate toscaTemplate; private ConfigurationManager configurationManager; - private static Logger log = LoggerFactory.getLogger(SdcCsarHelperImpl.class.getName()); public SdcCsarHelperImpl(ToscaTemplate toscaTemplate) { this.toscaTemplate = toscaTemplate; @@ -86,137 +86,144 @@ public class SdcCsarHelperImpl implements ISdcCsarHelper { this.toscaTemplate = toscaTemplate; this.configurationManager = configurationManager; } - + @Override public List getPoliciesOfTarget(NodeTemplate nodeTemplate) { - return getPoliciesOfNodeTemplate(nodeTemplate.getName()) - .stream() - .sorted(Policy::compareTo) - .collect(toList()); + return getPoliciesOfNodeTemplate(nodeTemplate.getName()) + .stream() + .sorted(Policy::compareTo) + .collect(toList()); } - + @Override - public List getPoliciesOfOriginOfNodeTemplate(NodeTemplate nodeTemplate) { - if(StringUtils.isNotEmpty(nodeTemplate.getName())){ - return getNodeTemplateByName(nodeTemplate.getName()).getOriginComponentTemplate().getPolicies(); - } - return new ArrayList<>(); + public List getPoliciesOfOriginOfNodeTemplate(NodeTemplate nodeTemplate) { + if (StringUtils.isNotEmpty(nodeTemplate.getName())) { + return getNodeTemplateByName(nodeTemplate.getName()).getOriginComponentTemplate().getPolicies(); + } + return new ArrayList<>(); } - + @Override public List getPoliciesOfTargetByToscaPolicyType(NodeTemplate nodeTemplate, String policyTypeName) { - return getPoliciesOfNodeTemplate(nodeTemplate.getName()) - .stream() - .filter(p->p.getType().equals(policyTypeName)) - .sorted(Policy::compareTo) - .collect(toList()); + return getPoliciesOfNodeTemplate(nodeTemplate.getName()) + .stream() + .filter(p -> p.getType().equals(policyTypeName)) + .sorted(Policy::compareTo) + .collect(toList()); } - + @Override public List getPoliciesOfOriginOfNodeTemplateByToscaPolicyType(NodeTemplate nodeTemplate, String policyTypeName) { - return getPoliciesOfOriginOfNodeTemplate(nodeTemplate) - .stream() - .filter(p->p.getType().equals(policyTypeName)) - .sorted(Policy::compareTo) - .collect(toList()); + return getPoliciesOfOriginOfNodeTemplate(nodeTemplate) + .stream() + .filter(p -> p.getType().equals(policyTypeName)) + .sorted(Policy::compareTo) + .collect(toList()); } @Override public List getPolicyTargetsFromTopologyTemplate(String policyName) { - if(toscaTemplate.getNodeTemplates() == null){ - return new ArrayList<>(); - } - List targetNames = getPolicyTargets(policyName); - return toscaTemplate.getNodeTemplates().stream() - .filter(nt->targetNames.contains(nt.getName())) - .collect(toList()); - } - - @Override - public List getPolicyTargetsFromOrigin(NodeTemplate nodeTemplate, String policyName) { - if(StringUtils.isNotEmpty(nodeTemplate.getName())){ + if (toscaTemplate.getNodeTemplates() == null) { + return new ArrayList<>(); + } + List targetNames = getPolicyTargets(policyName); + return toscaTemplate.getNodeTemplates().stream() + .filter(nt -> targetNames.contains(nt.getName())) + .collect(toList()); + } + + @Override + public List getPolicyTargetsFromOrigin(NodeTemplate nodeTemplate, String policyName) { + if (StringUtils.isNotEmpty(nodeTemplate.getName())) { Optional policyOpt = getNodeTemplateByName(nodeTemplate.getName()) - .getOriginComponentTemplate() - .getPolicies() - .stream() - .filter(p -> p.getName() - .equals(policyName)) - .findFirst(); - if(policyOpt.isPresent()){ + .getOriginComponentTemplate() + .getPolicies() + .stream() + .filter(p -> p.getName() + .equals(policyName)) + .findFirst(); + if (policyOpt.isPresent()) { List targets = policyOpt.get().getTargets(); if (targets != null) { return nodeTemplate.getOriginComponentTemplate().getNodeTemplates() - .stream() - .filter(nt -> targets.contains(nt.getName())).collect(Collectors.toList()); + .stream() + .filter(nt -> targets.contains(nt.getName())).collect(Collectors.toList()); } } - } - return new ArrayList<>(); - } - - @Override - public List getPoliciesOfTopologyTemplate(){ - if(toscaTemplate.getPolicies() == null) - return new ArrayList<>(); - return toscaTemplate.getPolicies() - .stream() - .sorted(Policy::compareTo) - .collect(toList()); - } - - @Override - public List getPoliciesOfTopologyTemplateByToscaPolicyType(String policyTypeName){ - if(toscaTemplate.getPolicies() == null) - return new ArrayList<>(); - return toscaTemplate.getPolicies() - .stream() - .filter(p->p.getType().equals(policyTypeName)) - .sorted(Policy::compareTo) - .collect(toList()); - } - + } + return new ArrayList<>(); + } + + @Override + public List getPoliciesOfTopologyTemplate() { + if (toscaTemplate.getPolicies() == null) { + return new ArrayList<>(); + } + return toscaTemplate.getPolicies() + .stream() + .sorted(Policy::compareTo) + .collect(toList()); + } + + @Override + public List getPoliciesOfTopologyTemplateByToscaPolicyType(String policyTypeName) { + if (toscaTemplate.getPolicies() == null) { + return new ArrayList<>(); + } + return toscaTemplate.getPolicies() + .stream() + .filter(p -> p.getType().equals(policyTypeName)) + .sorted(Policy::compareTo) + .collect(toList()); + } + + @Override public NodeTemplate getNodeTemplateByName(String nodeTemplateName) { - if(toscaTemplate.getNodeTemplates() == null) - return null; - return toscaTemplate.getNodeTemplates() - .stream() - .filter(nt -> nt.getName().equals(nodeTemplateName)) - .findFirst().orElse(null); - } - + if (toscaTemplate.getNodeTemplates() == null) { + return null; + } + return toscaTemplate.getNodeTemplates() + .stream() + .filter(nt -> nt.getName().equals(nodeTemplateName)) + .findFirst().orElse(null); + } + private List getPoliciesOfNodeTemplate(String nodeTemplateName) { - if(toscaTemplate.getPolicies() == null) - return new ArrayList<>(); - return toscaTemplate.getPolicies() - .stream() - .filter(p -> p.getTargets()!= null && p.getTargets().contains(nodeTemplateName)) - .collect(toList()); - } - + if (toscaTemplate.getPolicies() == null) { + return new ArrayList<>(); + } + return toscaTemplate.getPolicies() + .stream() + .filter(p -> p.getTargets() != null && p.getTargets().contains(nodeTemplateName)) + .collect(toList()); + } + private List getPolicyTargets(String policyName) { - return getPolicyByName(policyName).map(Policy::getTargets).orElse(new ArrayList<>()); + return getPolicyByName(policyName).map(Policy::getTargets).orElse(new ArrayList<>()); } - + private List getGroupMembers(String groupName) { - return getGroupByName(groupName).map(Group::getMembers).orElse(new ArrayList<>()); + return getGroupByName(groupName).map(Group::getMembers).orElse(new ArrayList<>()); } - + private Optional getPolicyByName(String policyName) { - if(toscaTemplate.getPolicies() == null) - return Optional.empty(); - return toscaTemplate.getPolicies() - .stream() - .filter(p -> p.getName().equals(policyName)).findFirst(); + if (toscaTemplate.getPolicies() == null) { + return Optional.empty(); + } + return toscaTemplate.getPolicies() + .stream() + .filter(p -> p.getName().equals(policyName)).findFirst(); } - + private Optional getGroupByName(String groupName) { - if(toscaTemplate.getGroups() == null) - return Optional.empty(); - return toscaTemplate.getGroups() - .stream() - .filter(g -> g.getName().equals(groupName)).findFirst(); + if (toscaTemplate.getGroups() == null) { + return Optional.empty(); + } + return toscaTemplate.getGroups() + .stream() + .filter(g -> g.getName().equals(groupName)).findFirst(); } - + @Override //Sunny flow - covered with UT, flat and nested public String getNodeTemplatePropertyLeafValue(NodeTemplate nodeTemplate, String leafValuePath) { @@ -238,7 +245,8 @@ public class SdcCsarHelperImpl implements ISdcCsarHelper { LinkedHashMap properties = nodeTemplate.getProperties(); return PropertyUtils.processProperties(split, properties); } - + + @Override public Map> getCpPropertiesFromVfcAsObject(NodeTemplate vfc) { if (vfc == null) { log.error("getCpPropertiesFromVfc - vfc is null"); @@ -264,21 +272,22 @@ public class SdcCsarHelperImpl implements ISdcCsarHelper { return cps; } - private void findPutAllPortsProperties(Map> cps, Map props) { - if (!cps.isEmpty()) { - for (Entry> port : cps.entrySet()) { - for (Map.Entry property: props.entrySet()) { - if (property.getKey().startsWith(port.getKey())) { - String portProperty = property.getKey().replaceFirst(port.getKey() + "_", ""); - if (property.getValue() != null) { - cps.get(port.getKey()).put(portProperty, property.getValue().getValue()); - } - } - } - } - } - } + private void findPutAllPortsProperties(Map> cps, Map props) { + if (!cps.isEmpty()) { + for (Entry> port : cps.entrySet()) { + for (Map.Entry property : props.entrySet()) { + if (property.getKey().startsWith(port.getKey())) { + String portProperty = property.getKey().replaceFirst(port.getKey() + "_", ""); + if (property.getValue() != null) { + cps.get(port.getKey()).put(portProperty, property.getValue().getValue()); + } + } + } + } + } + } + @Override public Map> getCpPropertiesFromVfc(NodeTemplate vfc) { if (vfc == null) { @@ -304,20 +313,20 @@ public class SdcCsarHelperImpl implements ISdcCsarHelper { return cps; } - private void findBuildPutAllPortsProperties(Map> cps, Map props) { - if (!cps.isEmpty()) { - for (Entry> port : cps.entrySet()) { - for (Map.Entry property: props.entrySet()) { - if (property.getKey().startsWith(port.getKey())) { - Map portPaths = new HashMap<>(); - String portProperty = property.getKey().replaceFirst(port.getKey() + "_", ""); - buildPathMappedToValue(portProperty, property.getValue().getValue(), portPaths); - cps.get(port.getKey()).putAll(portPaths); - } - } - } - } - } + private void findBuildPutAllPortsProperties(Map> cps, Map props) { + if (!cps.isEmpty()) { + for (Entry> port : cps.entrySet()) { + for (Map.Entry property : props.entrySet()) { + if (property.getKey().startsWith(port.getKey())) { + Map portPaths = new HashMap<>(); + String portProperty = property.getKey().replaceFirst(port.getKey() + "_", ""); + buildPathMappedToValue(portProperty, property.getValue().getValue(), portPaths); + cps.get(port.getKey()).putAll(portPaths); + } + } + } + } + } @SuppressWarnings("unchecked") private void buildPathMappedToValue(String path, Object property, Map pathsMap) { @@ -330,7 +339,7 @@ public class SdcCsarHelperImpl implements ISdcCsarHelper { } } } else if (property instanceof List) { - for (Object item: (List)property) { + for (Object item : (List) property) { buildPathMappedToValue(path, item, pathsMap); } } else { @@ -365,7 +374,6 @@ public class SdcCsarHelperImpl implements ISdcCsarHelper { return metadata.getValue(metadataPropertyName); } - @Override //Sunny flow - covered with UT public List getServiceNodeTemplatesByType(String nodeType) { @@ -385,7 +393,6 @@ public class SdcCsarHelperImpl implements ISdcCsarHelper { return res; } - @Override public List getServiceNodeTemplates() { return toscaTemplate.getNodeTemplates(); @@ -416,12 +423,14 @@ public class SdcCsarHelperImpl implements ISdcCsarHelper { String name = nodeTemplateByCustomizationUuid.getName(); String normaliseComponentInstanceName = SdcToscaUtility.normaliseComponentInstanceName(name); List serviceLevelGroups = toscaTemplate.getTopologyTemplate().getGroups(); - log.debug("getVfModulesByVf - VF node template name {}, normalized name {}. Searching groups on service level starting with VF normalized name...", name, normaliseComponentInstanceName); + log.debug( + "getVfModulesByVf - VF node template name {}, normalized name {}. Searching groups on service level starting with VF normalized name...", + name, normaliseComponentInstanceName); if (serviceLevelGroups != null) { return serviceLevelGroups - .stream() - .filter(x -> GROUPS_VF_MODULE.equals(x.getTypeDefinition().getType()) && x.getName().startsWith(normaliseComponentInstanceName)) - .collect(toList()); + .stream() + .filter(x -> GROUPS_VF_MODULE.equals(x.getTypeDefinition().getType()) && x.getName().startsWith(normaliseComponentInstanceName)) + .collect(toList()); } } return new ArrayList<>(); @@ -448,7 +457,7 @@ public class SdcCsarHelperImpl implements ISdcCsarHelper { Input input = findFirst.get(); Object current = input.getDefault(); Object property = PropertyUtils.iterateProcessPath(2, current, split); - return property == null || property instanceof Function? null : String.valueOf(property); + return property == null || property instanceof Function ? null : String.valueOf(property); } } log.error("getServiceInputLeafValue - value not found"); @@ -464,7 +473,8 @@ public class SdcCsarHelperImpl implements ISdcCsarHelper { String[] split = getSplittedPath(inputLeafValuePath); if (split.length < 2 || !split[1].equals("default")) { - log.error("getServiceInputLeafValueOfDefaultAsObject - inputLeafValuePath should be of format #default[optionally #] "); + log.error( + "getServiceInputLeafValueOfDefaultAsObject - inputLeafValuePath should be of format #default[optionally #] "); return null; } @@ -485,7 +495,6 @@ public class SdcCsarHelperImpl implements ISdcCsarHelper { return leafValuePath.split(PATH_DELIMITER); } - @Override //Sunny flow - covered with UT public String getServiceSubstitutionMappingsTypeName() { @@ -513,7 +522,7 @@ public class SdcCsarHelperImpl implements ISdcCsarHelper { @Override //Sunny flow - covered with UT public Map getServiceMetadataProperties() { - if (toscaTemplate.getMetaData() == null){ + if (toscaTemplate.getMetaData() == null) { return null; } return new HashMap<>(toscaTemplate.getMetaData().getAllProperties()); @@ -521,7 +530,7 @@ public class SdcCsarHelperImpl implements ISdcCsarHelper { @Override public Map getServiceMetadataAllProperties() { - if (toscaTemplate.getMetaData() == null){ + if (toscaTemplate.getMetaData() == null) { return null; } return toscaTemplate.getMetaData().getAllProperties(); @@ -549,7 +558,7 @@ public class SdcCsarHelperImpl implements ISdcCsarHelper { String[] split = getSplittedPath(leafValuePath); LinkedHashMap properties = group.getProperties(); Object property = PropertyUtils.processProperties(split, properties); - return property == null || property instanceof Function? null : String.valueOf(property); + return property == null || property instanceof Function ? null : String.valueOf(property); } @Override @@ -589,8 +598,9 @@ public class SdcCsarHelperImpl implements ISdcCsarHelper { return cpList; } cpList = getNodeTemplateBySdcType(vfInstance, SdcTypes.CP); - if (cpList == null || cpList.isEmpty()) + if (cpList == null || cpList.isEmpty()) { log.debug("getCpListByVf cps not exist for vfCustomizationId {}", vfCustomizationId); + } return cpList; } @@ -602,19 +612,22 @@ public class SdcCsarHelperImpl implements ISdcCsarHelper { return new ArrayList<>(); } - if (serviceLevelVfModule == null || serviceLevelVfModule.getMetadata() == null || serviceLevelVfModule.getMetadata().getValue(SdcPropertyNames.PROPERTY_NAME_VFMODULEMODELINVARIANTUUID) == null) { - log.error("getMembersOfVfModule - vfModule or its metadata is null. Cannot match a VF group based on invariantUuid from missing metadata."); + if (serviceLevelVfModule == null || serviceLevelVfModule.getMetadata() == null + || serviceLevelVfModule.getMetadata().getValue(SdcPropertyNames.PROPERTY_NAME_VFMODULEMODELINVARIANTUUID) == null) { + log.error( + "getMembersOfVfModule - vfModule or its metadata is null. Cannot match a VF group based on invariantUuid from missing metadata."); return new ArrayList<>(); } - SubstitutionMappings substitutionMappings = vf.getSubMappingToscaTemplate(); if (substitutionMappings != null) { List groups = substitutionMappings.getGroups(); if (groups != null) { Optional findFirst = groups - .stream() - .filter(x -> (x.getMetadata() != null && serviceLevelVfModule.getMetadata().getValue(SdcPropertyNames.PROPERTY_NAME_VFMODULEMODELINVARIANTUUID).equals(x.getMetadata().getValue(SdcPropertyNames.PROPERTY_NAME_VFMODULEMODELINVARIANTUUID)))).findFirst(); + .stream() + .filter(x -> (x.getMetadata() != null && serviceLevelVfModule.getMetadata() + .getValue(SdcPropertyNames.PROPERTY_NAME_VFMODULEMODELINVARIANTUUID) + .equals(x.getMetadata().getValue(SdcPropertyNames.PROPERTY_NAME_VFMODULEMODELINVARIANTUUID)))).findFirst(); if (findFirst.isPresent()) { List members = findFirst.get().getMembers(); if (members != null) { @@ -628,7 +641,7 @@ public class SdcCsarHelperImpl implements ISdcCsarHelper { @Override public List> getNodeTemplatePairsByReqName( - List listOfReqNodeTemplates, List listOfCapNodeTemplates, String reqName) { + List listOfReqNodeTemplates, List listOfCapNodeTemplates, String reqName) { if (listOfReqNodeTemplates == null) { log.error("getNodeTemplatePairsByReqName - listOfReqNodeTemplates is null"); @@ -672,8 +685,8 @@ public class SdcCsarHelperImpl implements ISdcCsarHelper { log.error("getAllottedResources nodeTemplates not exist"); } nodeTemplates = nodeTemplates.stream().filter( - x -> x.getMetaData() != null && x.getMetaData().getValue("category").equals("Allotted Resource")) - .collect(toList()); + x -> x.getMetadata() != null && x.getMetadata().getValue("category").equals("Allotted Resource")) + .collect(toList()); if (nodeTemplates.isEmpty()) { log.debug("getAllottedResources - allotted resources not exist"); } @@ -700,58 +713,60 @@ public class SdcCsarHelperImpl implements ISdcCsarHelper { */ @Override public String getConformanceLevel() { - LinkedHashMap csarMeta = toscaTemplate.getMetaProperties("csar.meta"); - if (csarMeta == null){ - log.warn("No csar.meta file is found in CSAR - this file should hold the conformance level of the CSAR. This might be OK for older CSARs."); - if (configurationManager != null && !configurationManager.getErrorConfiguration() - .getErrorInfo("CONFORMANCE_LEVEL_ERROR").getFailOnError()){ - String csarConLevel = configurationManager.getConfiguration().getConformanceLevel().getMaxVersion(); - log.warn("csarConformanceLevel is not found in input csar; defaulting to max version {}" , csarConLevel); - return csarConLevel; - } - else { - log.warn("csarConformanceLevel is not found in input csar; returning null as no defaults defined in error configuration"); - return null; - } - } - - Object conformanceLevel = csarMeta.get("SDC-TOSCA-Definitions-Version"); - if (conformanceLevel != null){ - String confLevelStr = conformanceLevel.toString(); - log.debug("CSAR conformance level is {}", confLevelStr); - return confLevelStr; - } else { - log.error("Invalid csar.meta file - no entry found for SDC-TOSCA-Definitions-Version key. This entry should hold the conformance level."); - return null; - } - } - - - @Override - public String getNodeTemplateCustomizationUuid(NodeTemplate nt) { - String res = null; - if (nt != null && nt.getMetaData() != null){ - res = nt.getMetaData().getValue(CUSTOMIZATION_UUID); - } else { - log.error("Node template or its metadata is null"); - } - return res; - } + LinkedHashMap csarMeta = toscaTemplate.getMetaProperties("csar.meta"); + if (csarMeta == null) { + log.warn( + "No csar.meta file is found in CSAR - this file should hold the conformance level of the CSAR. This might be OK for older CSARs."); + if (configurationManager != null && !configurationManager.getErrorConfiguration() + .getErrorInfo("CONFORMANCE_LEVEL_ERROR").getFailOnError()) { + String csarConLevel = configurationManager.getConfiguration().getConformanceLevel().getMaxVersion(); + log.warn("csarConformanceLevel is not found in input csar; defaulting to max version {}", csarConLevel); + return csarConLevel; + } else { + log.warn("csarConformanceLevel is not found in input csar; returning null as no defaults defined in error configuration"); + return null; + } + } + Object conformanceLevel = csarMeta.get("SDC-TOSCA-Definitions-Version"); + if (conformanceLevel != null) { + String confLevelStr = conformanceLevel.toString(); + log.debug("CSAR conformance level is {}", confLevelStr); + return confLevelStr; + } else { + log.error("Invalid csar.meta file - no entry found for SDC-TOSCA-Definitions-Version key. This entry should hold the conformance level."); + return null; + } + } + + + @Override + public String getNodeTemplateCustomizationUuid(NodeTemplate nt) { + String res = null; + if (nt != null && nt.getMetadata() != null) { + res = nt.getMetadata().getValue(CUSTOMIZATION_UUID); + } else { + log.error("Node template or its metadata is null"); + } + return res; + } + + @Override public List getNodeTemplateBySdcType(NodeTemplate parentNodeTemplate, SdcTypes sdcType) { - return getNodeTemplateBySdcType(parentNodeTemplate, sdcType, false); + return getNodeTemplateBySdcType(parentNodeTemplate, sdcType, false); } + @Override public boolean isNodeTypeSupported(NodeTemplate nodeTemplate) { SdcTypes[] supportedTypes = SdcTypes.values(); return Arrays.stream(supportedTypes) - .anyMatch(v->nodeTemplate.getMetaData().getValue(SdcPropertyNames.PROPERTY_NAME_TYPE) - .equals(v.getValue())); + .anyMatch(v -> nodeTemplate.getMetadata().getValue(SdcPropertyNames.PROPERTY_NAME_TYPE) + .equals(v.getValue())); } - - private List getNodeTemplateBySdcType(NodeTemplate parentNodeTemplate, SdcTypes sdcType, boolean isVNF) { - - if (parentNodeTemplate == null) { + + private List getNodeTemplateBySdcType(NodeTemplate parentNodeTemplate, SdcTypes sdcType, boolean isVNF) { + + if (parentNodeTemplate == null) { log.error("getNodeTemplateBySdcType - nodeTemplate is null or empty"); return new ArrayList<>(); } @@ -766,24 +781,23 @@ public class SdcCsarHelperImpl implements ISdcCsarHelper { if (substitutionMappings != null) { List nodeTemplates = substitutionMappings.getNodeTemplates(); if (nodeTemplates != null && !nodeTemplates.isEmpty()) { - if (sdcType.equals(SdcTypes.VFC) && isVNF) { - return nodeTemplates.stream() - .filter(x -> (x.getMetaData() != null && - sdcType.getValue().equals(x.getMetaData().getValue(SdcPropertyNames.PROPERTY_NAME_TYPE))) && isVNFType(x)) - .collect(toList()); - } - else { + if (sdcType.equals(SdcTypes.VFC) && isVNF) { return nodeTemplates.stream() - .filter(x -> (x.getMetaData() != null && - sdcType.getValue().equals(x.getMetaData().getValue(SdcPropertyNames.PROPERTY_NAME_TYPE))) && !isVNFType(x)) - .collect(toList()); - } - } - else { + .filter(x -> (x.getMetadata() != null && + sdcType.getValue().equals(x.getMetadata().getValue(SdcPropertyNames.PROPERTY_NAME_TYPE))) && isVNFType(x)) + .collect(toList()); + } else { + return nodeTemplates.stream() + .filter(x -> (x.getMetadata() != null && + sdcType.getValue().equals(x.getMetadata().getValue(SdcPropertyNames.PROPERTY_NAME_TYPE))) && !isVNFType(x)) + .collect(toList()); + } + } else { log.debug("getNodeTemplateBySdcType - SubstitutionMappings' node Templates not exist"); } - } else + } else { log.debug("getNodeTemplateBySdcType - SubstitutionMappings not exist"); + } return new ArrayList<>(); } @@ -821,10 +835,10 @@ public class SdcCsarHelperImpl implements ISdcCsarHelper { return filterMap; } - - public NodeTemplate getVnfConfig(String vfCustomizationUuid) { - - if (GeneralUtility.isEmptyString(vfCustomizationUuid)) { + + public NodeTemplate getVnfConfig(String vfCustomizationUuid) { + + if (GeneralUtility.isEmptyString(vfCustomizationUuid)) { log.error("getVnfConfig - vfCustomizationId - is null or empty"); return null; } @@ -832,7 +846,7 @@ public class SdcCsarHelperImpl implements ISdcCsarHelper { List serviceVfList = getServiceVfList(); NodeTemplate vfInstance = getNodeTemplateByCustomizationUuid(serviceVfList, vfCustomizationUuid); return getNodeTemplateBySdcType(vfInstance, SdcTypes.VFC, true).stream().findAny().orElse(null); - } + } @Override public boolean hasTopology(NodeTemplate nodeTemplate) { @@ -841,8 +855,8 @@ public class SdcCsarHelperImpl implements ISdcCsarHelper { return false; } - if (nodeTemplate.getMetaData() != null) { - String type = nodeTemplate.getMetaData().getValue(SdcPropertyNames.PROPERTY_NAME_TYPE); + if (nodeTemplate.getMetadata() != null) { + String type = nodeTemplate.getMetadata().getValue(SdcPropertyNames.PROPERTY_NAME_TYPE); log.debug("hasTopology - node template {} is a {} type", nodeTemplate.getName(), type); return SdcTypes.isComplex(type); } @@ -863,14 +877,14 @@ public class SdcCsarHelperImpl implements ISdcCsarHelper { if (nodeTemplates != null && !nodeTemplates.isEmpty()) { return nodeTemplates.stream() - .filter(x -> !isVNFType(x)) - .collect(toList()); - } - else { + .filter(x -> !isVNFType(x)) + .collect(toList()); + } else { log.debug("getNodeTemplateChildren - SubstitutionMappings' node Templates not exist"); } - } else + } else { log.debug("getNodeTemplateChildren - SubstitutionMappings not exist"); + } return new ArrayList<>(); } @@ -883,7 +897,7 @@ public class SdcCsarHelperImpl implements ISdcCsarHelper { } List nodeTemplates = getServiceNodeTemplates(); - Optional findFirst = nodeTemplates.stream().filter(nt -> nt.getName().equals(nodeName)).findFirst(); + Optional findFirst = nodeTemplates.stream().filter(nt -> nt.getName().equals(nodeName)).findFirst(); return findFirst.isPresent() ? findFirst.get() : null; } @@ -895,7 +909,7 @@ public class SdcCsarHelperImpl implements ISdcCsarHelper { return null; } - return nt.getMetaData(); + return nt.getMetadata(); } @Override @@ -935,77 +949,79 @@ public class SdcCsarHelperImpl implements ISdcCsarHelper { Object property = PropertyUtils.processProperties(split, properties); return property == null || property instanceof Function ? null : String.valueOf(property); } - + @Override public ArrayList getGroupsOfOriginOfNodeTemplate(NodeTemplate nodeTemplate) { - if(StringUtils.isNotEmpty(nodeTemplate.getName())){ - return getNodeTemplateByName(nodeTemplate.getName()).getSubMappingToscaTemplate().getGroups(); - } - return new ArrayList<>(); + if (StringUtils.isNotEmpty(nodeTemplate.getName())) { + return getNodeTemplateByName(nodeTemplate.getName()).getSubMappingToscaTemplate().getGroups(); + } + return new ArrayList<>(); } @Override public ArrayList getGroupsOfTopologyTemplateByToscaGroupType(String groupType) { - if(toscaTemplate.getGroups() == null) - return new ArrayList<>(); - return (ArrayList) toscaTemplate.getGroups() - .stream() - .filter(g->g.getType().equals(groupType)) - .sorted(Group::compareTo) - .collect(toList()); - } - + if (toscaTemplate.getGroups() == null) { + return new ArrayList<>(); + } + return (ArrayList) toscaTemplate.getGroups() + .stream() + .filter(g -> g.getType().equals(groupType)) + .sorted(Group::compareTo) + .collect(toList()); + } + @Override public ArrayList getGroupsOfTopologyTemplate() { - return toscaTemplate.getGroups() == null ? new ArrayList<>() : toscaTemplate.getGroups(); + return toscaTemplate.getGroups() == null ? new ArrayList<>() : toscaTemplate.getGroups(); } - + @Override public ArrayList getGroupsOfOriginOfNodeTemplateByToscaGroupType(NodeTemplate nodeTemplate, String groupType) { - return (ArrayList) getGroupsOfOriginOfNodeTemplate(nodeTemplate) - .stream() - .filter(g->g.getType().equals(groupType)) - .sorted(Group::compareTo) - .collect(toList()); + return (ArrayList) getGroupsOfOriginOfNodeTemplate(nodeTemplate) + .stream() + .filter(g -> g.getType().equals(groupType)) + .sorted(Group::compareTo) + .collect(toList()); } @Override public List getGroupMembersFromTopologyTemplate(String groupName) { - if(toscaTemplate.getNodeTemplates() == null){ - return new ArrayList<>(); - } - List membersNames = getGroupMembers(groupName); - return toscaTemplate.getNodeTemplates().stream() - .filter(nt->membersNames.contains(nt.getName())) - .collect(toList()); + if (toscaTemplate.getNodeTemplates() == null) { + return new ArrayList<>(); + } + List membersNames = getGroupMembers(groupName); + return toscaTemplate.getNodeTemplates().stream() + .filter(nt -> membersNames.contains(nt.getName())) + .collect(toList()); } - + @Override public List getGroupMembersOfOriginOfNodeTemplate(NodeTemplate nodeTemplate, String groupName) { - ArrayList groups = getGroupsOfOriginOfNodeTemplate(nodeTemplate); - if(!groups.isEmpty()){ - Optional group = groups.stream().filter(g -> g.getName().equals(groupName)).findFirst(); - if(group.isPresent()){ - return nodeTemplate.getSubMappingToscaTemplate().getNodeTemplates().stream() - .filter(nt -> group.get().getMembers().contains(nt.getName())) - .collect(toList()); - } - } - return new ArrayList<>(); - } - + ArrayList groups = getGroupsOfOriginOfNodeTemplate(nodeTemplate); + if (!groups.isEmpty()) { + Optional group = groups.stream().filter(g -> g.getName().equals(groupName)).findFirst(); + if (group.isPresent()) { + return nodeTemplate.getSubMappingToscaTemplate().getNodeTemplates().stream() + .filter(nt -> group.get().getMembers().contains(nt.getName())) + .collect(toList()); + } + } + return new ArrayList<>(); + } + + @Override public List getServiceNodeTemplateBySdcType(SdcTypes sdcType) { - if (sdcType == null) { - log.error("getServiceNodeTemplateBySdcType - sdcType is null or empty"); - return new ArrayList<>(); - } - - TopologyTemplate topologyTemplate = toscaTemplate.getTopologyTemplate(); - return getNodeTemplateBySdcType(topologyTemplate, sdcType); + if (sdcType == null) { + log.error("getServiceNodeTemplateBySdcType - sdcType is null or empty"); + return new ArrayList<>(); + } + + TopologyTemplate topologyTemplate = toscaTemplate.getTopologyTemplate(); + return getNodeTemplateBySdcType(topologyTemplate, sdcType); } - /************************************* helper functions ***********************************/ + /************************************* helper functions ***********************************/ private boolean isVNFType(NodeTemplate nt) { return nt.getType().endsWith("VnfConfiguration"); } @@ -1014,12 +1030,12 @@ public class SdcCsarHelperImpl implements ISdcCsarHelper { private Map filterProperties(Object property, String path, FilterType filterType, String pattern, Map filterMap) { if (property instanceof Map) { - for (Map.Entry item: ((Map) property).entrySet()) { + for (Map.Entry item : ((Map) property).entrySet()) { String itemPath = path + PATH_DELIMITER + item.getKey(); filterProperties(item.getValue(), itemPath, filterType, pattern, filterMap); } } else if (property instanceof List) { - for (Object item: (List)property) { + for (Object item : (List) property) { filterProperties(item, path, filterType, pattern, filterMap); } } else { @@ -1030,7 +1046,7 @@ public class SdcCsarHelperImpl implements ISdcCsarHelper { return filterMap; } - + /************************************* helper functions ***********************************/ private List getNodeTemplateBySdcType(TopologyTemplate topologyTemplate, SdcTypes sdcType) { if (sdcType == null) { @@ -1045,8 +1061,11 @@ public class SdcCsarHelperImpl implements ISdcCsarHelper { List nodeTemplates = topologyTemplate.getNodeTemplates(); - if (nodeTemplates != null && !nodeTemplates.isEmpty()) - return nodeTemplates.stream().filter(x -> (x.getMetaData() != null && sdcType.getValue().equals(x.getMetaData().getValue(SdcPropertyNames.PROPERTY_NAME_TYPE)))).collect(toList()); + if (nodeTemplates != null && !nodeTemplates.isEmpty()) { + return nodeTemplates.stream() + .filter(x -> (x.getMetadata() != null && sdcType.getValue().equals(x.getMetadata().getValue(SdcPropertyNames.PROPERTY_NAME_TYPE)))) + .collect(toList()); + } log.debug("getNodeTemplateBySdcType - topologyTemplate's nodeTemplates not exist"); return new ArrayList<>(); @@ -1054,104 +1073,99 @@ public class SdcCsarHelperImpl implements ISdcCsarHelper { //Assumed to be unique property for the list private NodeTemplate getNodeTemplateByCustomizationUuid(List nodeTemplates, String customizationId) { - if (customizationId != null) { - Optional findFirst = nodeTemplates.stream().filter(x -> (x.getMetaData() != null && customizationId.equals(x.getMetaData().getValue(SdcPropertyNames.PROPERTY_NAME_CUSTOMIZATIONUUID)))).findFirst(); + if (customizationId != null) { + Optional findFirst = nodeTemplates.stream().filter( + x -> (x.getMetadata() != null && customizationId.equals(x.getMetadata().getValue(SdcPropertyNames.PROPERTY_NAME_CUSTOMIZATIONUUID)))) + .findFirst(); return findFirst.isPresent() ? findFirst.get() : null; - } - else { + } else { log.error("getNodeTemplateByCustomizationUuid - customizationId is null"); return null; } } - @Override - public Map> getInterfacesOf(NodeTemplate nt){ - if (nt == null) { - return null; + @Override + public Map> getInterfacesOf(NodeTemplate nt) { + if (nt == null) { + return null; + } + return nt.getAllInterfaceDetailsForNodeType(); } - return nt.getAllInterfaceDetailsForNodeType(); - } - @Override - public List getInterfaces(NodeTemplate nt){ - Map> interfaceDetails = nt.getAllInterfaceDetailsForNodeType(); - return new ArrayList<>(interfaceDetails.keySet()); - } + @Override + public List getInterfaces(NodeTemplate nt) { + Map> interfaceDetails = nt.getAllInterfaceDetailsForNodeType(); + return new ArrayList<>(interfaceDetails.keySet()); + } - @Override - public List getInterfaceDetails(NodeTemplate nt, String interfaceName){ - Map> interfaceDetails = nt.getAllInterfaceDetailsForNodeType(); - return interfaceDetails.get(interfaceName); - } + @Override + public List getInterfaceDetails(NodeTemplate nt, String interfaceName) { + Map> interfaceDetails = nt.getAllInterfaceDetailsForNodeType(); + return interfaceDetails.get(interfaceName); + } - @Override - public List getAllInterfaceOperations(NodeTemplate nt, String interfaceName){ - Map> interfaceDetails = nt.getAllInterfaceDetailsForNodeType(); - return interfaceDetails.values().stream().flatMap(List::stream).map(val -> val.getOperationName()).collect( - Collectors.toList()); - } + @Override + public List getAllInterfaceOperations(NodeTemplate nt, String interfaceName) { + Map> interfaceDetails = nt.getAllInterfaceDetailsForNodeType(); + return interfaceDetails.values().stream().flatMap(List::stream).map(val -> val.getOperationName()).collect( + Collectors.toList()); + } - @Override - public InterfacesDef getInterfaceOperationDetails(NodeTemplate nt, String interfaceName, String operationName){ - Map> interfaceDetails = nt.getAllInterfaceDetailsForNodeType(); - if(!interfaceDetails.isEmpty()){ - List interfaceDefs = interfaceDetails.get(interfaceName); - return interfaceDefs.stream().filter(val -> val.getOperationName().equals(operationName)).findFirst().orElse(null); + @Override + public InterfacesDef getInterfaceOperationDetails(NodeTemplate nt, String interfaceName, String operationName) { + Map> interfaceDetails = nt.getAllInterfaceDetailsForNodeType(); + if (!interfaceDetails.isEmpty()) { + List interfaceDefs = interfaceDetails.get(interfaceName); + return interfaceDefs.stream().filter(val -> val.getOperationName().equals(operationName)).findFirst().orElse(null); + } + return null; } - return null; - } @Override public List getPropertyLeafValueByPropertyNamePathAndNodeTemplatePath(String propertyNamePath, String nodeTemplatePath) { log.info("A new request is received: property path is [{}], node template path is [{}]", - propertyNamePath, nodeTemplatePath); + propertyNamePath, nodeTemplatePath); List propertyValuesList; if (StringUtils.isEmpty(nodeTemplatePath) || StringUtils.isEmpty(propertyNamePath)) { log.error("One of parameters is empty or null: property path is [{}], node template path is [{}]", - propertyNamePath, nodeTemplatePath); + propertyNamePath, nodeTemplatePath); propertyValuesList = Collections.emptyList(); - } - else { + } else { String[] nodeTemplates = getSplittedPath(nodeTemplatePath); propertyValuesList = getPropertyFromInternalNodeTemplate(getNodeTemplateByName(nodeTemplates[0]), 1, nodeTemplates, propertyNamePath); log.info("Found property value {} by path [{}] for node template [{}]", - propertyValuesList, propertyNamePath, nodeTemplatePath); + propertyValuesList, propertyNamePath, nodeTemplatePath); } return propertyValuesList; } private List getPropertyFromInternalNodeTemplate(NodeTemplate parent, int index, - String[] nodeTemplatePath, String propertyPath) { + String[] nodeTemplatePath, String propertyPath) { List propertyValuesList; if (parent == null) { log.error("Node template {} is not found, the request will be rejected", nodeTemplatePath[index]); propertyValuesList = Collections.emptyList(); - } - else if (nodeTemplatePath.length <= index) { + } else if (nodeTemplatePath.length <= index) { log.debug("Stop NODE TEMPLATE searching"); propertyValuesList = getSimpleOrListPropertyValue(parent, propertyPath); - } - else { + } else { log.debug("Node template {} is found with name {}", nodeTemplatePath[index], parent.getName()); NodeTemplate childNT = getChildNodeTemplateByName(parent, nodeTemplatePath[index]); if (childNT == null || !isNodeTypeSupported(childNT)) { log.error("Unsupported or not found node template named {}, the request will be rejected", - nodeTemplatePath[index]); + nodeTemplatePath[index]); propertyValuesList = Collections.emptyList(); - } - else { + } else { propertyValuesList = getPropertyFromInternalNodeTemplate(childNT, index + 1, nodeTemplatePath, - propertyPath); + propertyPath); } } return propertyValuesList; } - - private List getSimpleOrListPropertyValue(NodeTemplate nodeTemplate, String propertyPath) { List propertyValueList; String[] path = getSplittedPath(propertyPath); @@ -1161,51 +1175,48 @@ public class SdcCsarHelperImpl implements ISdcCsarHelper { //the requested property type is either simple or list of simple types PropertySchemaType propertyType = PropertySchemaType.getEnumByValue(property.getType()); if (propertyType == PropertySchemaType.LIST && - PropertyUtils.isDataPropertyType((String)property.getEntrySchema() - .get(SdcPropertyNames.PROPERTY_NAME_TYPE))) { + PropertyUtils.isDataPropertyType((String) property.getEntrySchema() + .get(SdcPropertyNames.PROPERTY_NAME_TYPE))) { //cover the case when a type of property "path[0]' is list of data types // and the requested property is an internal simple property of this data type propertyValueList = calculatePropertyValue(getNodeTemplatePropertyValueAsObject(nodeTemplate, path[0]), path, nodeTemplate.getName()); - } - else { + } else { //the requested property is simple type or list of simple types - propertyValueList = calculatePropertyValue(getNodeTemplatePropertyValueAsObject(nodeTemplate, propertyPath), null, nodeTemplate.getName()); + propertyValueList = calculatePropertyValue(getNodeTemplatePropertyValueAsObject(nodeTemplate, propertyPath), null, + nodeTemplate.getName()); } - } - else { + } else { log.error("The type of property {} on node {} is neither simple nor list of simple objects, the request will be rejected", - propertyPath, nodeTemplate.getName()); + propertyPath, nodeTemplate.getName()); propertyValueList = Collections.emptyList(); } return propertyValueList; } - private List calculatePropertyValue(Object valueAsObject, String path[], String nodeName) { + private List calculatePropertyValue(Object valueAsObject, String[] path, String nodeName) { if (valueAsObject == null || valueAsObject instanceof Map) { - log.error("The property {} either is not found on node template [{}], or it is data type, or it is not resolved get_input", path, nodeName); + log.error("The property {} either is not found on node template [{}], or it is data type, or it is not resolved get_input", path, + nodeName); return Collections.emptyList(); } if (path != null) { - return PropertyUtils.findSimplePropertyValueInListOfDataTypes((List)valueAsObject, path); + return PropertyUtils.findSimplePropertyValueInListOfDataTypes((List) valueAsObject, path); } return PropertyUtils.buildSimplePropertValueOrList(valueAsObject); } - - - private Property getNodeTemplatePropertyObjectByName(NodeTemplate nodeTemplate, String propertyName) { return nodeTemplate.getPropertiesObjects() - .stream() - .filter(p->p.getName().equals(propertyName)) - .findFirst() - .orElse(null); + .stream() + .filter(p -> p.getName().equals(propertyName)) + .findFirst() + .orElse(null); } private NodeTemplate getChildNodeTemplateByName(NodeTemplate parent, String nodeTemplateName) { return getNodeTemplateChildren(parent) .stream() - .filter(nt->nt.getName().equals(nodeTemplateName)) + .filter(nt -> nt.getName().equals(nodeTemplateName)) .findFirst().orElse(null); } @@ -1219,7 +1230,7 @@ public class SdcCsarHelperImpl implements ISdcCsarHelper { if (log.isDebugEnabled()) { log.debug("getEntity request: EntityQuery <{}>, TopologyTemplateQuery <{}>, isRecursive<{}>", - entityQuery, topologyTemplateQuery, isRecursive); + entityQuery, topologyTemplateQuery, isRecursive); } return new QueryProcessor(toscaTemplate, entityQuery, topologyTemplateQuery, isRecursive).doQuery(); } @@ -1230,12 +1241,12 @@ public class SdcCsarHelperImpl implements ISdcCsarHelper { } @Override - public List getVFModule(String cuuid){ + public List getVFModule(String cuuid) { String normalisedComponentInstanceName = SdcToscaUtility.normaliseComponentInstanceName(getVfiNameByCuuid(cuuid)); List vfModulesFromVf = getVfModulesFromVf(cuuid); List vfModulesFromService = getVfModulesFromService().stream() - .filter(v->v.getName().startsWith(normalisedComponentInstanceName)) - .collect(toList()); + .filter(v -> v.getName().startsWith(normalisedComponentInstanceName)) + .collect(toList()); addMembersToVfModuleInstances(vfModulesFromVf, vfModulesFromService); return vfModulesFromService; } @@ -1246,29 +1257,29 @@ public class SdcCsarHelperImpl implements ISdcCsarHelper { List vfModuleInstances = new ArrayList<>(); getVfModulesFromVf().forEach(vfmodule -> { - if (Objects.isNull(vfmodule.getParent())){ + if (Objects.isNull(vfmodule.getParent())) { vfModuleInstances.add(vfmodule); } else { vfModules.add(vfmodule); } }); - addMembersToVfModuleInstances(vfModules,vfModuleInstances); + addMembersToVfModuleInstances(vfModules, vfModuleInstances); return vfModuleInstances; } private void addMembersToVfModuleInstances(List vfModules, List vfModuleInstances) { - for(IEntityDetails vfModuleInstance : vfModuleInstances){ + for (IEntityDetails vfModuleInstance : vfModuleInstances) { String origGroupName = getOriginalGroupName(vfModuleInstance); setVFModuleMembers(vfModules, vfModuleInstance, origGroupName); } } private void setVFModuleMembers(List vfModules, IEntityDetails vfModuleInstance, String origGroupName) { - for (IEntityDetails vfModule : vfModules){ - if (vfModuleInstance instanceof GroupEntityDetails && vfModule.getName().equals(origGroupName)){ + for (IEntityDetails vfModule : vfModules) { + if (vfModuleInstance instanceof GroupEntityDetails && vfModule.getName().equals(origGroupName)) { List memberNodes = vfModule.getMemberNodes(); - ((GroupEntityDetails)vfModuleInstance).setMemberNodes(memberNodes); + ((GroupEntityDetails) vfModuleInstance).setMemberNodes(memberNodes); } } } @@ -1280,36 +1291,36 @@ public class SdcCsarHelperImpl implements ISdcCsarHelper { private List getVfModulesFromService() { EntityQuery entityQuery = EntityQuery.newBuilder(GROUPS_VF_MODULE) - .build(); + .build(); TopologyTemplateQuery topologyTemplateQuery = TopologyTemplateQuery.newBuilder(SdcTypes.SERVICE) - .build(); + .build(); return getEntity(entityQuery, topologyTemplateQuery, false); } private List getVfModulesFromVf(String cuuid) { EntityQuery entityQuery = EntityQuery.newBuilder(GROUPS_VF_MODULE) - .build(); + .build(); TopologyTemplateQuery topologyTemplateQuery = TopologyTemplateQuery.newBuilder(SdcTypes.VF) - .customizationUUID(cuuid) - .build(); + .customizationUUID(cuuid) + .build(); return getEntity(entityQuery, topologyTemplateQuery, false); } private List getVfModulesFromVf() { EntityQuery entityQuery = EntityQuery.newBuilder(GROUPS_VF_MODULE) - .build(); + .build(); TopologyTemplateQuery topologyTemplateQuery = TopologyTemplateQuery.newBuilder(SdcTypes.SERVICE) - .build(); + .build(); return getEntity(entityQuery, topologyTemplateQuery, true); } private String getVfiNameByCuuid(String cuuid) { EntityQuery entityQuery = EntityQuery.newBuilder(SdcTypes.VF) - .customizationUUID(cuuid) - .build(); + .customizationUUID(cuuid) + .build(); TopologyTemplateQuery topologyTemplateQuery = TopologyTemplateQuery.newBuilder(SdcTypes.SERVICE) - .build(); + .build(); return getEntity(entityQuery, topologyTemplateQuery, true).get(0).getName(); } - } +} diff --git a/sdc-tosca/src/main/java/org/onap/sdc/tosca/parser/impl/SdcToscaParserFactory.java b/sdc-tosca/src/main/java/org/onap/sdc/tosca/parser/impl/SdcToscaParserFactory.java index e24a231..b77b84c 100644 --- a/sdc-tosca/src/main/java/org/onap/sdc/tosca/parser/impl/SdcToscaParserFactory.java +++ b/sdc-tosca/src/main/java/org/onap/sdc/tosca/parser/impl/SdcToscaParserFactory.java @@ -7,9 +7,9 @@ * 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. @@ -22,36 +22,39 @@ package org.onap.sdc.tosca.parser.impl; import java.util.ArrayList; import java.util.List; - import org.onap.sdc.tosca.parser.api.ConformanceLevel; +import org.onap.sdc.tosca.parser.api.ISdcCsarHelper; import org.onap.sdc.tosca.parser.config.ConfigurationManager; import org.onap.sdc.tosca.parser.config.ErrorInfo; import org.onap.sdc.tosca.parser.config.JToscaValidationIssueInfo; import org.onap.sdc.tosca.parser.config.SdcToscaParserErrors; import org.onap.sdc.tosca.parser.enums.JToscaValidationIssueType; -import org.onap.sdc.tosca.parser.utils.GeneralUtility; -import org.onap.sdc.tosca.parser.api.ISdcCsarHelper; import org.onap.sdc.tosca.parser.exceptions.SdcToscaParserException; +import org.onap.sdc.tosca.parser.utils.GeneralUtility; import org.onap.sdc.toscaparser.api.ToscaTemplate; -import org.onap.sdc.toscaparser.api.common.JToscaValidationIssue; import org.onap.sdc.toscaparser.api.common.JToscaException; +import org.onap.sdc.toscaparser.api.common.JToscaValidationIssue; import org.onap.sdc.toscaparser.api.utils.JToscaErrorCodes; import org.onap.sdc.toscaparser.api.utils.ThreadLocalsHolder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class SdcToscaParserFactory { - private static Logger log = LoggerFactory.getLogger(SdcToscaParserFactory.class.getName()); + + private static Logger log = LoggerFactory.getLogger(SdcToscaParserFactory.class.getName()); private static ConfigurationManager configurationManager; private static volatile SdcToscaParserFactory instance; private List criticalExceptions = new ArrayList<>(); private List warningExceptions = new ArrayList<>(); private List notAnalyzadExceptions = new ArrayList<>(); - private SdcToscaParserFactory() {} + + private SdcToscaParserFactory() { + } /** * Get an SdcToscaParserFactory instance. + * * @return SdcToscaParserFactory instance. */ public static SdcToscaParserFactory getInstance() { @@ -84,7 +87,7 @@ public class SdcToscaParserFactory { /** * Get an ISdcCsarHelper object for this CSAR file. * - * @param csarPath - the absolute path to CSAR file. + * @param csarPath - the absolute path to CSAR file. * @param resolveGetInput - resolve get_input properties * @return ISdcCsarHelper object. * @throws SdcToscaParserException - in case the path or CSAR are invalid. @@ -106,25 +109,25 @@ public class SdcToscaParserFactory { validateCsarVersion(cSarConformanceLevel); try { handleErrorsByTypes(csarPath, cSarConformanceLevel); - } catch (JToscaException e) { + } catch (JToscaException e) { throwSdcToscaParserException(e); - } + } return sdcCsarHelperImpl; } } private void handleErrorsByTypes(String csarPath, String cSarConformanceLevel) throws JToscaException { clearValidationIssuesLists(); - for(JToscaValidationIssue toscaValidationIssue : ThreadLocalsHolder.getCollector().getValidationIssues().values()){ - List issueInfos = configurationManager.getJtoscaValidationIssueConfiguration().getValidationIssues().get(toscaValidationIssue.getCode()); - if(issueInfos != null && !issueInfos.isEmpty()){ - JToscaValidationIssueInfo issueInfo = null; - issueInfo = issueInfos.stream() - .filter(i-> isMatchConformanceLevel(cSarConformanceLevel,i.getSinceCsarConformanceLevel())) - .max((i1,i2) -> GeneralUtility.conformanceLevelCompare(i1.getSinceCsarConformanceLevel(), i2.getSinceCsarConformanceLevel()) ) + for (JToscaValidationIssue toscaValidationIssue : ThreadLocalsHolder.getCollector().getValidationIssues().values()) { + List issueInfos = configurationManager.getJtoscaValidationIssueConfiguration().getValidationIssues() + .get(toscaValidationIssue.getCode()); + if (issueInfos != null && !issueInfos.isEmpty()) { + final JToscaValidationIssueInfo issueInfo = issueInfos.stream() + .filter(i -> isMatchConformanceLevel(cSarConformanceLevel, i.getSinceCsarConformanceLevel())) + .max((i1, i2) -> GeneralUtility.conformanceLevelCompare(i1.getSinceCsarConformanceLevel(), i2.getSinceCsarConformanceLevel())) .orElse(null); - if(issueInfo != null){ + if (issueInfo != null) { switch (JToscaValidationIssueType.valueOf(issueInfo.getIssueType())) { case CRITICAL: criticalExceptions.add(toscaValidationIssue); @@ -135,59 +138,61 @@ public class SdcToscaParserFactory { default: break; } - }else{ + } else { notAnalyzadExceptions.add(toscaValidationIssue); } - }else{//notAnalyzed + } else {//notAnalyzed notAnalyzadExceptions.add(toscaValidationIssue); } - } - logErrors(csarPath); + } + logErrors(csarPath); } - private void clearValidationIssuesLists(){ + private void clearValidationIssuesLists() { notAnalyzadExceptions.clear(); criticalExceptions.clear(); warningExceptions.clear(); } - private void logErrors(String inputPath) throws JToscaException{ - //Warnings - int warningsCount = warningExceptions.size(); - if (warningsCount > 0) { - log.warn("####################################################################################################"); - log.warn("CSAR Warnings found! CSAR name - {}", inputPath); - log.warn("ToscaTemplate - verifyTemplate - {} Parsing Warning{} occurred...", warningsCount, (warningsCount > 1 ? "s" : "")); - for (JToscaValidationIssue info : warningExceptions) { - log.warn("JTosca Exception [{}]: {}. CSAR name - {}", info.getCode(),info.getMessage(), inputPath); - } - log.warn("####################################################################################################"); - } - //Criticals - int criticalsCount = criticalExceptions.size(); - if (criticalsCount > 0) { - log.error("####################################################################################################"); - log.error("ToscaTemplate - verifyTemplate - {} Parsing Critical{} occurred...", criticalsCount, (criticalsCount > 1 ? "s" : "")); - for (JToscaValidationIssue info : criticalExceptions) { - log.error("JTosca Exception [{}]: {}. CSAR name - {}", info.getCode(),info.getMessage(), inputPath); - } - throw new JToscaException(String.format("CSAR Validation Failed. CSAR name - {}. Please check logs for details.", inputPath), JToscaErrorCodes.CSAR_TOSCA_VALIDATION_ERROR.getValue()); - } + private void logErrors(String inputPath) throws JToscaException { + //Warnings + int warningsCount = warningExceptions.size(); + if (warningsCount > 0) { + log.warn("####################################################################################################"); + log.warn("CSAR Warnings found! CSAR name - {}", inputPath); + log.warn("ToscaTemplate - verifyTemplate - {} Parsing Warning{} occurred...", warningsCount, (warningsCount > 1 ? "s" : "")); + for (JToscaValidationIssue info : warningExceptions) { + log.warn("JTosca Exception [{}]: {}. CSAR name - {}", info.getCode(), info.getMessage(), inputPath); + } + log.warn("####################################################################################################"); + } + //Criticals + int criticalsCount = criticalExceptions.size(); + if (criticalsCount > 0) { + log.error("####################################################################################################"); + log.error("ToscaTemplate - verifyTemplate - {} Parsing Critical{} occurred...", criticalsCount, (criticalsCount > 1 ? "s" : "")); + for (JToscaValidationIssue info : criticalExceptions) { + log.error("JTosca Exception [{}]: {}. CSAR name - {}", info.getCode(), info.getMessage(), inputPath); + } + throw new JToscaException(String.format("CSAR Validation Failed. CSAR name - {}. Please check logs for details.", inputPath), + JToscaErrorCodes.CSAR_TOSCA_VALIDATION_ERROR.getValue()); + } } + public List getCriticalExceptions() { - return criticalExceptions; - } + return criticalExceptions; + } - public List getWarningExceptions() { - return warningExceptions; - } + public List getWarningExceptions() { + return warningExceptions; + } - public List getNotAnalyzadExceptions() { - return notAnalyzadExceptions; - } + public List getNotAnalyzadExceptions() { + return notAnalyzadExceptions; + } - private void validateCsarVersion(String cSarVersion) throws SdcToscaParserException { + private void validateCsarVersion(String cSarVersion) throws SdcToscaParserException { ConformanceLevel level = configurationManager.getConfiguration().getConformanceLevel(); String minVersion = level.getMinVersion(); if (cSarVersion != null) { @@ -199,7 +204,7 @@ public class SdcToscaParserFactory { } } - private boolean isMatchConformanceLevel(String ValidationIssueVersion, String cSarVersion){ + private boolean isMatchConformanceLevel(String ValidationIssueVersion, String cSarVersion) { if (ValidationIssueVersion != null && cSarVersion != null) { if ((GeneralUtility.conformanceLevelCompare(ValidationIssueVersion, cSarVersion) >= 0)) { return true; @@ -207,16 +212,17 @@ public class SdcToscaParserFactory { } return false; } + private void throwConformanceLevelException(String minVersion) throws SdcToscaParserException { ErrorInfo errorInfo = configurationManager.getErrorConfiguration().getErrorInfo(SdcToscaParserErrors.CONFORMANCE_LEVEL_ERROR.toString()); throw new SdcToscaParserException(String.format(errorInfo.getMessage(), minVersion), errorInfo.getCode()); } private void throwSdcToscaParserException(JToscaException e) throws SdcToscaParserException { - ErrorInfo errorInfo = configurationManager.getErrorConfiguration().getErrorInfo(SdcToscaParserErrors.getSdcErrorByJToscaError(JToscaErrorCodes.getByCode(e.getCode())).toString()); + ErrorInfo errorInfo = configurationManager.getErrorConfiguration() + .getErrorInfo(SdcToscaParserErrors.getSdcErrorByJToscaError(JToscaErrorCodes.getByCode(e.getCode())).toString()); throw new SdcToscaParserException(errorInfo.getMessage(), errorInfo.getCode()); } - } diff --git a/sdc-tosca/src/test/java/org/onap/sdc/impl/SdcToscaParserBasicTest.java b/sdc-tosca/src/test/java/org/onap/sdc/impl/SdcToscaParserBasicTest.java index 5b3a4d9..d348bad 100644 --- a/sdc-tosca/src/test/java/org/onap/sdc/impl/SdcToscaParserBasicTest.java +++ b/sdc-tosca/src/test/java/org/onap/sdc/impl/SdcToscaParserBasicTest.java @@ -29,7 +29,6 @@ import org.onap.sdc.tosca.parser.api.ISdcCsarHelper; import org.onap.sdc.tosca.parser.exceptions.SdcToscaParserException; import org.onap.sdc.tosca.parser.impl.SdcToscaParserFactory; - public class SdcToscaParserBasicTest extends BaseSetupExtension { public static final String VF_CUSTOMIZATION_UUID = "56179cd8-de4a-4c38-919b-bbc4452d2d73"; @@ -60,7 +59,7 @@ public class SdcToscaParserBasicTest extends BaseSetupExtension { static ISdcCsarHelper csarHelperServiceAnnotations; static ISdcCsarHelper csarHelperServiceAdiodAnnotations; static ISdcCsarHelper csarHelperServiceNetworkCloud; - static Map>> fdntCsarHelper_Data; + static Map>> fdntCsarHelper_Data; @Override void setup() throws SdcToscaParserException { @@ -94,9 +93,7 @@ public class SdcToscaParserBasicTest extends BaseSetupExtension { fdntCsarHelper_Data = new HashMap<>() { { - HashMap> FDNT; - - FDNT = new HashMap<>(); + final Map> FDNT = new HashMap<>(); FDNT.put("VF Name", Arrays.asList("FDNT 1")); FDNT.put("capabilities", Arrays.asList( "dnt_fw_rhrg.binding_DNT_FW_INT_DNS_TRUSTED_RVMI", @@ -155,19 +152,13 @@ public class SdcToscaParserBasicTest extends BaseSetupExtension { } protected ISdcCsarHelper getCsarHelper(String path) throws SdcToscaParserException { - System.out.println("Parsing CSAR " + path + "..."); - String fileStr1 = SdcToscaParserBasicTest.class.getClassLoader().getResource(path).getFile(); - File file1 = new File(fileStr1); - ISdcCsarHelper sdcCsarHelper = factory.getSdcCsarHelper(file1.getAbsolutePath()); - return sdcCsarHelper; + return getCsarHelper(path, true); } protected ISdcCsarHelper getCsarHelper(String path, boolean resolveGetInput) throws SdcToscaParserException { System.out.println("Parsing CSAR " + path + "..."); - String fileStr1 = SdcToscaParserBasicTest.class.getClassLoader().getResource(path).getFile(); - File file1 = new File(fileStr1); - ISdcCsarHelper sdcCsarHelper = factory.getSdcCsarHelper(file1.getAbsolutePath(), resolveGetInput); - return sdcCsarHelper; + return factory.getSdcCsarHelper(new File(SdcToscaParserBasicTest.class.getClassLoader().getResource(path).getFile()).getAbsolutePath(), + resolveGetInput); } @Override diff --git a/sdc-tosca/src/test/java/org/onap/sdc/impl/ToscaParserArtifactsTest.java b/sdc-tosca/src/test/java/org/onap/sdc/impl/ToscaParserArtifactsTest.java new file mode 100644 index 0000000..c8d5367 --- /dev/null +++ b/sdc-tosca/src/test/java/org/onap/sdc/impl/ToscaParserArtifactsTest.java @@ -0,0 +1,80 @@ +/* + * - + * ============LICENSE_START======================================================= + * Copyright (C) 2022 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.onap.sdc.impl; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.net.URL; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.onap.sdc.tosca.parser.api.ISdcCsarHelper; +import org.onap.sdc.tosca.parser.impl.SdcToscaParserFactory; +import org.onap.sdc.toscaparser.api.NodeTemplate; +import org.onap.sdc.toscaparser.api.elements.ArtifactDef; + +class ToscaParserArtifactsTest { + + private static final String TEST_ARTIFACTS_FILENAME = "csars/resource-VspWithArtifacts.csar"; + private static final String TEST_ARTIFACT_TYPE = "tosca.artifacts.asd.deploymentItem"; + private static final List ARTIFACTS_NAME_LIST = Arrays.asList("sampleapp-db", "sampleapp-services"); + private static ISdcCsarHelper helper = null; + + @BeforeAll + public static void setup() throws Exception { + final URL resource = ToscaParserArtifactsTest.class.getClassLoader().getResource(TEST_ARTIFACTS_FILENAME); + if (resource != null) { + helper = SdcToscaParserFactory.getInstance().getSdcCsarHelper(resource.getFile()); + } + assertNotNull(helper); + } + + @Test + void testGetArtifacts() { + final NodeTemplate nodeTemplate = helper.getServiceNodeTemplates().get(0); + assertNotNull(nodeTemplate); + final Map artifacts = nodeTemplate.getArtifacts(); + assertNotNull(artifacts); + assertEquals(2, artifacts.size()); + artifacts.entrySet().forEach(entry -> { + assertTrue(ARTIFACTS_NAME_LIST.contains(entry.getKey())); + final ArtifactDef artifactDef = entry.getValue(); + assertNotNull(artifactDef); + final String artifactTypeDefType = artifactDef.getType(); + assertNotNull(artifactTypeDefType); + assertEquals(TEST_ARTIFACT_TYPE, artifactTypeDefType); + final Map properties = artifactDef.getProperties(); + assertNotNull(properties); + assertFalse(properties.isEmpty()); + final String file = artifactDef.getFile(); + assertNotNull(file); + assertTrue(file.startsWith("../Artifacts/Deployment/HELM/sampleapp-")); + }); + + } + +} diff --git a/sdc-tosca/src/test/java/org/onap/sdc/impl/ToscaParserNodeTemplateTest.java b/sdc-tosca/src/test/java/org/onap/sdc/impl/ToscaParserNodeTemplateTest.java index 408693b..d0b8579 100644 --- a/sdc-tosca/src/test/java/org/onap/sdc/impl/ToscaParserNodeTemplateTest.java +++ b/sdc-tosca/src/test/java/org/onap/sdc/impl/ToscaParserNodeTemplateTest.java @@ -563,7 +563,7 @@ import org.onap.sdc.toscaparser.api.parameters.Input; public void testGetVnfConfig() { NodeTemplate vnfConfig = nfodCsarHlper.getVnfConfig("9bb2ef82-f8f6-4391-bc71-db063f15bf57"); assertNotNull(vnfConfig); - assertEquals("vnfConfiguration", vnfConfig.getMetaData().getValue("name")); + assertEquals("vnfConfiguration", vnfConfig.getMetadata().getValue("name")); } @Test @@ -657,9 +657,9 @@ import org.onap.sdc.toscaparser.api.parameters.Input; children.sort(Comparator.comparing(NodeTemplate::getName)); assertEquals("DNT_FW_RSG_SI_1", children.get(1).getName()); - assertEquals("VFC", children.get(1).getMetaData().getValue("type")); + assertEquals("VFC", children.get(1).getMetadata().getValue("type")); assertEquals("DNT_PORT", children.get(2).getName()); - assertEquals("CP", children.get(2).getMetaData().getValue("type")); + assertEquals("CP", children.get(2).getMetadata().getValue("type")); } @Test @@ -671,7 +671,7 @@ import org.onap.sdc.toscaparser.api.parameters.Input; vfChildren.sort(Comparator.comparing(NodeTemplate::getName)); assertEquals("VFC1 DUMMY", vfChildren.get(0).getName()); assertEquals("VF_VNF", vfChildren.get(1).getName()); - assertEquals("CVFC", vfChildren.get(1).getMetaData().getValue("type")); + assertEquals("CVFC", vfChildren.get(1).getMetadata().getValue("type")); List vfcChildren = nestedVfcCsarHlper.getNodeTemplateChildren(vfChildren.get(1)); @@ -697,7 +697,7 @@ import org.onap.sdc.toscaparser.api.parameters.Input; public void testGetVnfConfigGetProperties() { NodeTemplate vnfConfig = nfodCsarHlper.getVnfConfig("9bb2ef82-f8f6-4391-bc71-db063f15bf57"); assertNotNull(vnfConfig); - assertEquals("vnfConfiguration", vnfConfig.getMetaData().getValue("name")); + assertEquals("vnfConfiguration", vnfConfig.getMetadata().getValue("name")); String manufacturer_reference_number = nfodCsarHlper.getNodeTemplatePropertyLeafValue(vnfConfig, "allowed_flavors#ATT_part_12345_for_FortiGate-VM00#vendor_info#manufacturer_reference_number"); String num_cpus = nfodCsarHlper.getNodeTemplatePropertyLeafValue(vnfConfig, "allowed_flavors#ATT_part_67890_for_FortiGate-VM01#compute_flavor#num_cpus"); @@ -728,7 +728,7 @@ import org.onap.sdc.toscaparser.api.parameters.Input; public void testNewGetVnfConfigGetProperties() { NodeTemplate vnfConfig = nfodNEWCsarHlper.getVnfConfig("a6587663-b27f-4e88-8a86-604604302ce6"); assertNotNull(vnfConfig); - assertEquals("vnfConfiguration", vnfConfig.getMetaData().getValue("name")); + assertEquals("vnfConfiguration", vnfConfig.getMetadata().getValue("name")); //Deployment flavor 1 String manufacturer_reference_number = nfodNEWCsarHlper.getNodeTemplatePropertyLeafValue(vnfConfig, "allowed_flavors#123456#vendor_info#manufacturer_reference_number"); @@ -770,7 +770,7 @@ import org.onap.sdc.toscaparser.api.parameters.Input; NodeTemplate nodeTemplate = fdntCsarHelper.getServiceNodeTemplateByNodeName("FDNT 1"); assertNotNull(nodeTemplate); assertEquals(nodeTemplate.getName(), "FDNT 1"); - assertEquals(nodeTemplate.getMetaData().getValue("type"), "VF"); + assertEquals(nodeTemplate.getMetadata().getValue("type"), "VF"); } @Test @@ -942,7 +942,7 @@ import org.onap.sdc.toscaparser.api.parameters.Input; NodeTemplate nodeTemplate = QAServiceForToscaParserTests.getServiceNodeTemplateByNodeName("VF_1_V_port_1 0"); assertNotNull(nodeTemplate); assertEquals(nodeTemplate.getName(), "VF_1_V_port_1 0"); - assertEquals(nodeTemplate.getMetaData().getValue("type"), "VF"); + assertEquals(nodeTemplate.getMetadata().getValue("type"), "VF"); } @Test @@ -950,7 +950,7 @@ import org.onap.sdc.toscaparser.api.parameters.Input; NodeTemplate nodeTemplate = QAServiceForToscaParserTests.getServiceNodeTemplateByNodeName("ExtVL 0"); assertNotNull(nodeTemplate); assertEquals(nodeTemplate.getName(), "ExtVL 0"); - assertEquals(nodeTemplate.getMetaData().getValue("type"), "VL"); + assertEquals(nodeTemplate.getMetadata().getValue("type"), "VL"); } @Test @@ -958,7 +958,7 @@ import org.onap.sdc.toscaparser.api.parameters.Input; NodeTemplate nodeTemplate = QAServiceForToscaParserTests.getServiceNodeTemplateByNodeName("ExtCP 0"); assertNotNull(nodeTemplate); assertEquals(nodeTemplate.getName(), "ExtCP 0"); - assertEquals(nodeTemplate.getMetaData().getValue("type"), "CP"); + assertEquals(nodeTemplate.getMetadata().getValue("type"), "CP"); } @Test @@ -966,7 +966,7 @@ import org.onap.sdc.toscaparser.api.parameters.Input; NodeTemplate nodeTemplate = QAServiceForToscaParserTests.getServiceNodeTemplateByNodeName("PNF TEST 0"); assertNotNull(nodeTemplate); assertEquals(nodeTemplate.getName(), "PNF TEST 0"); - assertEquals(nodeTemplate.getMetaData().getValue("type"), "PNF"); + assertEquals(nodeTemplate.getMetadata().getValue("type"), "PNF"); } //QA region getServiceNodeTemplateBySdcType tests @@ -975,7 +975,7 @@ import org.onap.sdc.toscaparser.api.parameters.Input; public void getServiceNodeTemplateBySdcType_VF() { List vfList = QAServiceForToscaParserTests.getServiceNodeTemplatesByType("org.openecomp.resource.vf.Vf1VPort1"); assertEquals(2, vfList.size()); - assertEquals("VF_1_V_port_1", vfList.get(0).getMetaData().getValue("name")); + assertEquals("VF_1_V_port_1", vfList.get(0).getMetadata().getValue("name")); } // endregion Added by QA - Continue with testings of resolve get_input @@ -1006,7 +1006,7 @@ import org.onap.sdc.toscaparser.api.parameters.Input; NodeTemplate nodeTemplate = csarHelperServiceWithCrs.getServiceNodeTemplateByNodeName("chaya best cr 0"); List crCpChildren = csarHelperServiceWithCrs.getNodeTemplateBySdcType(nodeTemplate, SdcTypes.CP); assertEquals(crCpChildren.get(0).getName(), "ContrailPort 0"); - assertEquals(crCpChildren.get(0).getMetaData().getValue("type"), SdcTypes.CP.name()); + assertEquals(crCpChildren.get(0).getMetadata().getValue("type"), SdcTypes.CP.name()); } @Test diff --git a/sdc-tosca/src/test/java/org/onap/sdc/tosca/parser/elements/queries/TopologyTemplateQueryTest.java b/sdc-tosca/src/test/java/org/onap/sdc/tosca/parser/elements/queries/TopologyTemplateQueryTest.java index 018675a..9656fb5 100644 --- a/sdc-tosca/src/test/java/org/onap/sdc/tosca/parser/elements/queries/TopologyTemplateQueryTest.java +++ b/sdc-tosca/src/test/java/org/onap/sdc/tosca/parser/elements/queries/TopologyTemplateQueryTest.java @@ -54,7 +54,7 @@ public class TopologyTemplateQueryTest { public void templateIsFoundByTypeOnly() { TopologyTemplateQuery topologyTemplateQuery = TopologyTemplateQuery.newBuilder(SdcTypes.SERVICE) .build(); - when(nodeTemplate.getMetaData()).thenReturn(metadata); + when(nodeTemplate.getMetadata()).thenReturn(metadata); when(metadata.getValue(SdcPropertyNames.PROPERTY_NAME_TYPE)).thenReturn(SdcTypes.SERVICE.getValue()); assertTrue(topologyTemplateQuery.isMatchingSearchCriteria(nodeTemplate)); } @@ -63,7 +63,7 @@ public class TopologyTemplateQueryTest { public void templateIsNotFoundWhenMetadataIsNull() { TopologyTemplateQuery topologyTemplateQuery = TopologyTemplateQuery.newBuilder(SdcTypes.VF) .build(); - when(nodeTemplate.getMetaData()).thenReturn(null); + when(nodeTemplate.getMetadata()).thenReturn(null); assertFalse(topologyTemplateQuery.isMatchingSearchCriteria(nodeTemplate)); } @@ -71,7 +71,7 @@ public class TopologyTemplateQueryTest { public void templateIsFoundIfItIsService() { TopologyTemplateQuery topologyTemplateQuery = TopologyTemplateQuery.newBuilder(SdcTypes.SERVICE) .build(); - when(nodeTemplate.getMetaData()).thenReturn(metadata); + when(nodeTemplate.getMetadata()).thenReturn(metadata); when(metadata.getValue(SdcPropertyNames.PROPERTY_NAME_TYPE)).thenReturn(SdcTypes.SERVICE.getValue()); assertTrue(topologyTemplateQuery.isMatchingSearchCriteria(nodeTemplate)); } @@ -81,7 +81,7 @@ public class TopologyTemplateQueryTest { TopologyTemplateQuery topologyTemplateQuery = TopologyTemplateQuery.newBuilder(SdcTypes.CVFC) .customizationUUID("345") .build(); - when(nodeTemplate.getMetaData()).thenReturn(metadata); + when(nodeTemplate.getMetadata()).thenReturn(metadata); when(metadata.getValue(SdcPropertyNames.PROPERTY_NAME_TYPE)).thenReturn(SdcTypes.CVFC.getValue()); when(metadata.getValue(SdcPropertyNames.PROPERTY_NAME_CUSTOMIZATIONUUID)).thenReturn("345"); assertTrue(topologyTemplateQuery.isMatchingSearchCriteria(nodeTemplate)); @@ -91,7 +91,7 @@ public class TopologyTemplateQueryTest { public void templateIsNotFoundWhenTypeIsNotMatchedAndCuuidIsNotSet() { TopologyTemplateQuery topologyTemplateQuery = TopologyTemplateQuery.newBuilder(SdcTypes.CVFC) .build(); - when(nodeTemplate.getMetaData()).thenReturn(metadata); + when(nodeTemplate.getMetadata()).thenReturn(metadata); when(metadata.getValue(SdcPropertyNames.PROPERTY_NAME_TYPE)).thenReturn(SdcTypes.VF.getValue()); assertFalse(topologyTemplateQuery.isMatchingSearchCriteria(nodeTemplate)); } @@ -101,7 +101,7 @@ public class TopologyTemplateQueryTest { TopologyTemplateQuery topologyTemplateQuery = TopologyTemplateQuery.newBuilder(SdcTypes.VF) .customizationUUID("2345") .build(); - when(nodeTemplate.getMetaData()).thenReturn(metadata); + when(nodeTemplate.getMetadata()).thenReturn(metadata); when(metadata.getValue(SdcPropertyNames.PROPERTY_NAME_CUSTOMIZATIONUUID)).thenReturn(null); when(metadata.getValue(SdcPropertyNames.PROPERTY_NAME_TYPE)).thenReturn(SdcTypes.VF.getValue()); assertFalse(topologyTemplateQuery.isMatchingSearchCriteria(nodeTemplate)); @@ -111,7 +111,7 @@ public class TopologyTemplateQueryTest { public void templateIsFoundWhenTypeIsMatchedAndCuuidIsNullInMetadata() { TopologyTemplateQuery topologyTemplateQuery = TopologyTemplateQuery.newBuilder(SdcTypes.VF) .build(); - when(nodeTemplate.getMetaData()).thenReturn(metadata); + when(nodeTemplate.getMetadata()).thenReturn(metadata); when(metadata.getValue(SdcPropertyNames.PROPERTY_NAME_CUSTOMIZATIONUUID)).thenReturn(null); when(metadata.getValue(SdcPropertyNames.PROPERTY_NAME_TYPE)).thenReturn(SdcTypes.VF.getValue()); assertTrue(topologyTemplateQuery.isMatchingSearchCriteria(nodeTemplate)); @@ -122,7 +122,7 @@ public class TopologyTemplateQueryTest { TopologyTemplateQuery topologyTemplateQuery = TopologyTemplateQuery.newBuilder(SdcTypes.CVFC) .customizationUUID("345") .build(); - when(nodeTemplate.getMetaData()).thenReturn(metadata); + when(nodeTemplate.getMetadata()).thenReturn(metadata); when(metadata.getValue(SdcPropertyNames.PROPERTY_NAME_TYPE)).thenReturn(SdcTypes.CVFC.getValue()); when(metadata.getValue(SdcPropertyNames.PROPERTY_NAME_CUSTOMIZATIONUUID)).thenReturn("345"); assertTrue(topologyTemplateQuery.isMatchingSearchCriteria(nodeTemplate)); @@ -133,7 +133,7 @@ public class TopologyTemplateQueryTest { TopologyTemplateQuery topologyTemplateQuery = TopologyTemplateQuery.newBuilder(SdcTypes.CR) .customizationUUID("345") .build(); - when(nodeTemplate.getMetaData()).thenReturn(metadata); + when(nodeTemplate.getMetadata()).thenReturn(metadata); when(metadata.getValue(SdcPropertyNames.PROPERTY_NAME_TYPE)).thenReturn(SdcTypes.CVFC.getValue()); assertFalse(topologyTemplateQuery.isMatchingSearchCriteria(nodeTemplate)); } diff --git a/sdc-tosca/src/test/java/org/onap/sdc/tosca/parser/impl/ToscaParserNodeTemplateMockTest.java b/sdc-tosca/src/test/java/org/onap/sdc/tosca/parser/impl/ToscaParserNodeTemplateMockTest.java index e58d863..533b8ee 100644 --- a/sdc-tosca/src/test/java/org/onap/sdc/tosca/parser/impl/ToscaParserNodeTemplateMockTest.java +++ b/sdc-tosca/src/test/java/org/onap/sdc/tosca/parser/impl/ToscaParserNodeTemplateMockTest.java @@ -185,7 +185,7 @@ public class ToscaParserNodeTemplateMockTest { @Test public void verifyNodeTypeIsNotSupported() { - when(nodeTemplate.getMetaData()).thenReturn(metadata); + when(nodeTemplate.getMetadata()).thenReturn(metadata); when(metadata.getValue(SdcPropertyNames.PROPERTY_NAME_TYPE)).thenReturn("VFC-TEST"); ISdcCsarHelper sdcCsarHelper = new SdcCsarHelperImpl(toscaTemplate); diff --git a/sdc-tosca/src/test/resources/csars/resource-VspWithArtifacts.csar b/sdc-tosca/src/test/resources/csars/resource-VspWithArtifacts.csar new file mode 100644 index 0000000000000000000000000000000000000000..b4402d0525a0aa7e89c091badded9d76cd5556f2 GIT binary patch literal 70481 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