diff options
Diffstat (limited to 'nfvparser/toscaparser')
33 files changed, 579 insertions, 66 deletions
diff --git a/nfvparser/toscaparser/__init__.py b/nfvparser/toscaparser/__init__.py index d9745ed..63ddcd4 100644 --- a/nfvparser/toscaparser/__init__.py +++ b/nfvparser/toscaparser/__init__.py @@ -14,4 +14,4 @@ import pbr.version __version__ = pbr.version.VersionInfo( - 'tosca-parser').version_string() + 'nfv-toscaparser').version_string() diff --git a/nfvparser/toscaparser/capabilities.py b/nfvparser/toscaparser/capabilities.py index c23ef72..1708fd8 100644 --- a/nfvparser/toscaparser/capabilities.py +++ b/nfvparser/toscaparser/capabilities.py @@ -16,10 +16,11 @@ from toscaparser.properties import Property class Capability(object): '''TOSCA built-in capabilities type.''' - def __init__(self, name, properties, definition): + def __init__(self, name, properties, definition, custom_def): self.name = name self._properties = properties self.definition = definition + self.custom_def = custom_def def get_properties_objects(self): '''Return a list of property objects.''' @@ -30,7 +31,8 @@ class Capability(object): props_def = self.definition.get_properties_def() if props_def and name in props_def: properties.append(Property(name, value, - props_def[name].schema)) + props_def[name].schema, + self.custom_def)) return properties def get_properties(self): diff --git a/nfvparser/toscaparser/common/exception.py b/nfvparser/toscaparser/common/exception.py index 13ccabd..67a9f7f 100644 --- a/nfvparser/toscaparser/common/exception.py +++ b/nfvparser/toscaparser/common/exception.py @@ -20,7 +20,7 @@ import traceback from toscaparser.utils.gettextutils import _ -log = logging.getLogger(__name__) +log = logging.getLogger('tosca') class TOSCAException(Exception): @@ -206,6 +206,13 @@ class ExceptionCollector(object): raise exception @staticmethod + def removeException(exception_type): + if ExceptionCollector.collecting and ExceptionCollector.exceptions: + for i, e in enumerate(ExceptionCollector.exceptions): + if isinstance(e, exception_type): + del ExceptionCollector.exceptions[i] + + @staticmethod def exceptionsCaught(): return len(ExceptionCollector.exceptions) > 0 diff --git a/nfvparser/toscaparser/elements/TOSCA_definition_1_0.yaml b/nfvparser/toscaparser/elements/TOSCA_definition_1_0.yaml index 9f3369e..57c9bf9 100644 --- a/nfvparser/toscaparser/elements/TOSCA_definition_1_0.yaml +++ b/nfvparser/toscaparser/elements/TOSCA_definition_1_0.yaml @@ -339,6 +339,26 @@ node_types: relationship: tosca.relationships.network.LinksTo node: tosca.nodes.network.Network + tosca.nodes.network.FloatingIP: + derived_from: tosca.nodes.Root + description: > + The TOSCA FloatingIP node represents a floating IP that can associate to a Port. + properties: + floating_network: + type: string + required: true + floating_ip_address: + type: string + required: false + port_id: + type: string + required: false + requirements: + - link: + capability: tosca.capabilities.network.Linkable + relationship: tosca.relationships.network.LinksTo + node: tosca.nodes.network.Port + tosca.nodes.ObjectStorage: derived_from: tosca.nodes.Root description: > @@ -928,6 +948,11 @@ policy_types: description: The TOSCA Policy Type definition that is used to govern scaling of TOSCA nodes or groups of nodes. + tosca.policies.Monitoring: + derived_from: tosca.policies.Root + description: The TOSCA Policy Type definition that is used to govern + monitoring of TOSCA nodes or groups of nodes. + tosca.policies.Update: derived_from: tosca.policies.Root description: The TOSCA Policy Type definition that is used to govern diff --git a/nfvparser/toscaparser/elements/capabilitytype.py b/nfvparser/toscaparser/elements/capabilitytype.py index 5fa9661..23c5afc 100644 --- a/nfvparser/toscaparser/elements/capabilitytype.py +++ b/nfvparser/toscaparser/elements/capabilitytype.py @@ -25,7 +25,7 @@ class CapabilityTypeDef(StatefulEntityType): self.nodetype = ntype self.properties = None self.custom_def = custom_def - if self.PROPERTIES in self.defs: + if self.defs and self.PROPERTIES in self.defs: self.properties = self.defs[self.PROPERTIES] self.parent_capabilities = self._get_parent_capabilities(custom_def) diff --git a/nfvparser/toscaparser/elements/nodetype.py b/nfvparser/toscaparser/elements/nodetype.py index 7f3da2d..4889ed8 100644 --- a/nfvparser/toscaparser/elements/nodetype.py +++ b/nfvparser/toscaparser/elements/nodetype.py @@ -83,8 +83,6 @@ class NodeType(StatefulEntityType): captype = value['capability'] value = (self. _get_node_type_by_cap(captype)) - # _get_node_type_by_cap(key, captype)) - # relation = self._get_relation(key, value) keyword = key node_type = value rtype = RelationshipType(relation, keyword, self.custom_def) diff --git a/nfvparser/toscaparser/elements/policytype.py b/nfvparser/toscaparser/elements/policytype.py index a922d26..82aed0a 100644 --- a/nfvparser/toscaparser/elements/policytype.py +++ b/nfvparser/toscaparser/elements/policytype.py @@ -113,7 +113,7 @@ class PolicyType(StatefulEntityType): for entry_schema, entry_schema_type in meta_data.items(): if isinstance(entry_schema_type, dict) and not \ - entry_schema_type.get('type') == 'string': + entry_schema_type.get('type') == 'string': ExceptionCollector.appendException( InvalidTypeError(what='"%s" defined in policy for ' 'metadata "%s"' diff --git a/nfvparser/toscaparser/elements/statefulentitytype.py b/nfvparser/toscaparser/elements/statefulentitytype.py index 2f221b3..28ee697 100644 --- a/nfvparser/toscaparser/elements/statefulentitytype.py +++ b/nfvparser/toscaparser/elements/statefulentitytype.py @@ -46,7 +46,8 @@ class StatefulEntityType(EntityType): elif custom_def and entitytype in list(custom_def.keys()): self.defs = custom_def[entitytype] else: - self.defs = None + # avoid errors if self.defs = none + self.defs = {} ExceptionCollector.appendException( InvalidTypeError(what=entitytype)) self.type = entitytype diff --git a/nfvparser/toscaparser/elements/tosca_type_validation.py b/nfvparser/toscaparser/elements/tosca_type_validation.py index 82b0b46..89a6a03 100644 --- a/nfvparser/toscaparser/elements/tosca_type_validation.py +++ b/nfvparser/toscaparser/elements/tosca_type_validation.py @@ -23,13 +23,14 @@ class TypeValidation(object): DATA_TYPES, ARTIFACT_TYPES, GROUP_TYPES, RELATIONSHIP_TYPES, CAPABILITY_TYPES, INTERFACE_TYPES, POLICY_TYPES, - TOPOLOGY_TEMPLATE) = \ + TOPOLOGY_TEMPLATE, METADATA) = \ ('tosca_definitions_version', 'description', 'imports', 'dsl_definitions', 'node_types', 'repositories', 'data_types', 'artifact_types', 'group_types', 'relationship_types', 'capability_types', - 'interface_types', 'policy_types', 'topology_template') - VALID_TEMPLATE_VERSIONS = ['tosca_simple_yaml_1_0'] + 'interface_types', 'policy_types', 'topology_template', 'metadata') + VALID_TEMPLATE_VERSIONS = ['tosca_simple_yaml_1_0', + 'tosca_simple_yaml_1_1'] exttools = ExtTools() VALID_TEMPLATE_VERSIONS.extend(exttools.get_versions()) diff --git a/nfvparser/toscaparser/entity_template.py b/nfvparser/toscaparser/entity_template.py index cc3d620..d454ac0 100644 --- a/nfvparser/toscaparser/entity_template.py +++ b/nfvparser/toscaparser/entity_template.py @@ -164,7 +164,7 @@ class EntityTemplate(object): if 'properties' in props and props['properties']: properties.update(props['properties']) - cap = Capability(name, properties, c) + cap = Capability(name, properties, c, self.custom_def) capability.append(cap) return capability diff --git a/nfvparser/toscaparser/functions.py b/nfvparser/toscaparser/functions.py index d498229..b64f1bc 100644 --- a/nfvparser/toscaparser/functions.py +++ b/nfvparser/toscaparser/functions.py @@ -158,6 +158,8 @@ class GetAttribute(Function): # then check the req or caps attr = self._find_req_or_cap_attribute(self.args[1], self.args[2]) + if not attr: + return value_type = attr.schema['type'] if len(self.args) > index: @@ -236,8 +238,8 @@ class GetAttribute(Function): target_node = self._find_node_template(target_name) target_type = target_node.type_definition for capability in target_type.get_capabilities_objects(): - if capability.type in \ - hosted_on_rel['valid_target_types']: + if capability.inherits_from( + hosted_on_rel['valid_target_types']): if self._attribute_exists_in_type(target_type): return target_node return self._find_host_containing_attribute( @@ -412,6 +414,8 @@ class GetProperty(Function): def _find_req_or_cap_property(self, req_or_cap, property_name): node_tpl = self._find_node_template(self.args[0]) + if node_tpl is None: + return None # Find property in node template's requirements for r in node_tpl.requirements: for req, node_name in r.items(): @@ -429,7 +433,8 @@ class GetProperty(Function): def _get_capability_property(self, node_template, capability_name, - property_name): + property_name, + throw_errors=True): """Gets a node template capability property.""" caps = node_template.get_capabilities() if caps and capability_name in caps.keys(): @@ -438,7 +443,7 @@ class GetProperty(Function): props = cap.get_properties() if props and property_name in props.keys(): property = props[property_name].value - if not property: + if property is None and throw_errors: ExceptionCollector.appendException( KeyError(_('Property "%(prop)s" was not found in ' 'capability "%(cap)s" of node template ' @@ -448,12 +453,15 @@ class GetProperty(Function): 'ntpl1': node_template.name, 'ntpl2': self.context.name})) return property - msg = _('Requirement/Capability "{0}" referenced from node template ' - '"{1}" was not found in node template "{2}".').format( - capability_name, - self.context.name, - node_template.name) - ExceptionCollector.appendException(KeyError(msg)) + if throw_errors: + msg = _('Requirement/Capability "{0}" referenced from ' + 'node template "{1}" was not found in node template' + ' "{2}".').format(capability_name, + self.context.name, + node_template.name) + ExceptionCollector.appendException(KeyError(msg)) + else: + return None def _find_property(self, property_name): node_tpl = self._find_node_template(self.args[0]) @@ -475,7 +483,18 @@ class GetProperty(Function): return self.context # enable the HOST value in the function if node_template_name == HOST: - return self._find_host_containing_property() + node = self._find_host_containing_property() + if node is None: + ExceptionCollector.appendException( + KeyError(_( + "Property '{0}' not found in capability/requirement" + " '{1}' referenced from node template {2}"). + format(self.args[2], + self.args[1], + self.context.name))) + return None + else: + return node if node_template_name == TARGET: if not isinstance(self.context.type_definition, RelationshipType): ExceptionCollector.appendException( @@ -498,7 +517,9 @@ class GetProperty(Function): ExceptionCollector.appendException( KeyError(_( 'Node template "{0}" was not found.' - ).format(node_template_name))) + ' referenced from node template {1}' + ).format(node_template_name, + self.context.name))) def _get_index_value(self, value, index): if isinstance(value, list): @@ -555,9 +576,19 @@ class GetProperty(Function): target_node = self._find_node_template(target_name) target_type = target_node.type_definition for capability in target_type.get_capabilities_objects(): - if capability.type in hosted_on_rel['valid_target_types']: + if capability.inherits_from( + hosted_on_rel['valid_target_types']): if self._property_exists_in_type(target_type): return target_node + # If requirement was not found, look in node + # template's capabilities + if (len(self.args) > 2 and + self._get_capability_property(target_node, + self.args[1], + self.args[2], + False) + is not None): + return target_node return self._find_host_containing_property( target_name) return None diff --git a/nfvparser/toscaparser/parameters.py b/nfvparser/toscaparser/parameters.py index 787db00..d2d2eb3 100644 --- a/nfvparser/toscaparser/parameters.py +++ b/nfvparser/toscaparser/parameters.py @@ -32,9 +32,10 @@ class Input(object): 'constraints', 'required', 'status', 'entry_schema') - def __init__(self, name, schema_dict): + def __init__(self, name, schema_dict, custom_defs=None): self.name = name self.schema = Schema(name, schema_dict) + self.custom_defs = custom_defs or {} self._validate_field() self.validate_type(self.type) @@ -75,7 +76,8 @@ class Input(object): field=name)) def validate_type(self, input_type): - if input_type not in Schema.PROPERTY_TYPES: + if input_type not in Schema.PROPERTY_TYPES and \ + input_type not in self.custom_defs: ExceptionCollector.appendException( ValueError(_('Invalid type "%s".') % type)) @@ -89,6 +91,8 @@ class Input(object): datatype = tosca[self.type] elif EntityType.DATATYPE_NETWORK_PREFIX + self.type in tosca: datatype = tosca[EntityType.DATATYPE_NETWORK_PREFIX + self.type] + elif self.type in self.custom_defs: + datatype = self.custom_defs DataEntity.validate_datatype(self.type, value, None, datatype) diff --git a/nfvparser/toscaparser/shell.py b/nfvparser/toscaparser/shell.py index 1d98f1a..88c7473 100644 --- a/nfvparser/toscaparser/shell.py +++ b/nfvparser/toscaparser/shell.py @@ -12,9 +12,11 @@ import argparse +import logging import os import sys +from toscaparser.common.exception import ValidationError from toscaparser.tosca_template import ToscaTemplate from toscaparser.utils.gettextutils import _ import toscaparser.utils.urlutils @@ -38,6 +40,8 @@ e.g. --template-file=toscaparser/tests/data/CSAR/csar_hello_world.zip """ +log = logging.getLogger("tosca.model") + class ParserShell(object): @@ -49,41 +53,67 @@ class ParserShell(object): required=True, help=_('YAML template or CSAR file to parse.')) + parser.add_argument('-nrpv', dest='no_required_paras_check', + action='store_true', default=False, + help=_('Ignore input parameter validation ' + 'when parse template.')) + + parser.add_argument('--debug', dest='debug_mode', + action='store_true', default=False, + help=_('debug mode for print more details ' + 'other than raise exceptions when ' + 'errors happen as possible')) + return parser def main(self, argv): parser = self.get_parser(argv) (args, extra_args) = parser.parse_known_args(argv) path = args.template_file + nrpv = args.no_required_paras_check + debug = args.debug_mode + if os.path.isfile(path): - self.parse(path) + self.parse(path, no_required_paras_check=nrpv, debug_mode=debug) elif toscaparser.utils.urlutils.UrlUtils.validate_url(path): - self.parse(path, False) + self.parse(path, False, + no_required_paras_check=nrpv, + debug_mode=debug) else: raise ValueError(_('"%(path)s" is not a valid file.') % {'path': path}) - def parse(self, path, a_file=True): - output = None - tosca = ToscaTemplate(path, None, a_file) - - version = tosca.version - if tosca.version: + def parse(self, path, a_file=True, no_required_paras_check=False, + debug_mode=False): + nrpv = no_required_paras_check + try: + tosca = ToscaTemplate(path, None, a_file, + no_required_paras_check=nrpv, + debug_mode=debug_mode) + except ValidationError as e: + log.error(e.message) + if debug_mode: + print(e.message) + else: + raise e + + version = tosca.version if tosca else "unknown" + if tosca and tosca.version: print("\nversion: " + version) - if hasattr(tosca, 'description'): + if tosca and hasattr(tosca, 'description'): description = tosca.description if description: print("\ndescription: " + description) - if hasattr(tosca, 'inputs'): + if tosca and hasattr(tosca, 'inputs'): inputs = tosca.inputs if inputs: print("\ninputs:") for input in inputs: print("\t" + input.name) - if hasattr(tosca, 'nodetemplates'): + if tosca and hasattr(tosca, 'nodetemplates'): nodetemplates = tosca.nodetemplates if nodetemplates: print("\nnodetemplates:") @@ -102,7 +132,7 @@ class ParserShell(object): for trigger in policy.triggers: print("\ttrigger name:" + trigger.name)''' - if hasattr(tosca, 'outputs'): + if tosca and hasattr(tosca, 'outputs'): outputs = tosca.outputs if outputs: print("\noutputs:") diff --git a/nfvparser/toscaparser/tests/data/custom_types/container_cap_child.yaml b/nfvparser/toscaparser/tests/data/custom_types/container_cap_child.yaml new file mode 100644 index 0000000..1df09dd --- /dev/null +++ b/nfvparser/toscaparser/tests/data/custom_types/container_cap_child.yaml @@ -0,0 +1,33 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: > + Define a capability class that inherits from tosca.capabilities.Container + +capability_types: + + tosca.capabilities.ContainerChild: + derived_from: tosca.capabilities.Container + +node_types: + + tosca.nodes.SomeNode: + derived_from: tosca.nodes.Root + properties: + some_prop: + type: string + requirements: + - host_child: + capability: tosca.capabilities.ContainerChild + node: tosca.nodes.SomeNode2 + relationship: tosca.relationships.HostedOn + + tosca.nodes.SomeNode2: + derived_from: tosca.nodes.Root + capabilities: + host_child: + type: tosca.capabilities.ContainerChild + requirements: + - host: + capability: tosca.capabilities.Container + node: tosca.nodes.Compute + relationship: tosca.relationships.HostedOn diff --git a/nfvparser/toscaparser/tests/data/custom_types/custom_cap.yaml b/nfvparser/toscaparser/tests/data/custom_types/custom_cap.yaml new file mode 100644 index 0000000..018bcf6 --- /dev/null +++ b/nfvparser/toscaparser/tests/data/custom_types/custom_cap.yaml @@ -0,0 +1,22 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +capability_types: + + tosca.capabilities.SomeCap: + derived_from: tosca.capabilities.Container + +node_types: + + tosca.nodes.NodeWithReq: + derived_from: tosca.nodes.SoftwareComponent + requirements: + - host: + capability: tosca.capabilities.SomeCap + relationship: tosca.relationships.HostedOn + occurrences: [1, 1] + + tosca.nodes.NodeWithCap: + derived_from: tosca.nodes.SoftwareComponent + capabilities: + host: + type: tosca.capabilities.SomeCap diff --git a/nfvparser/toscaparser/tests/data/custom_types/nested_test_kibana.yaml b/nfvparser/toscaparser/tests/data/custom_types/nested_test_kibana.yaml new file mode 100644 index 0000000..c8675c6 --- /dev/null +++ b/nfvparser/toscaparser/tests/data/custom_types/nested_test_kibana.yaml @@ -0,0 +1,3 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 +imports: + - custom_types/kibana.yaml diff --git a/nfvparser/toscaparser/tests/data/custom_types/node_with_cap.yaml b/nfvparser/toscaparser/tests/data/custom_types/node_with_cap.yaml index b17513f..332f830 100644 --- a/nfvparser/toscaparser/tests/data/custom_types/node_with_cap.yaml +++ b/nfvparser/toscaparser/tests/data/custom_types/node_with_cap.yaml @@ -6,7 +6,7 @@ description: > capability_types: tosca.capabilities.SomeCap: - derived_from: tosca.capabilities.Root + derived_from: tosca.capabilities.Container properties: type: type: string @@ -19,6 +19,11 @@ node_types: tosca.nodes.SomeNode: derived_from: tosca.nodes.Root + properties: + some_prop: + type: string + required: false + default: some requirements: - some_req: capability: tosca.capabilities.SomeCap diff --git a/nfvparser/toscaparser/tests/data/custom_types/wordpress.yml b/nfvparser/toscaparser/tests/data/custom_types/wordpress.yml new file mode 100644 index 0000000..5899ed9 --- /dev/null +++ b/nfvparser/toscaparser/tests/data/custom_types/wordpress.yml @@ -0,0 +1,19 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +node_types: + tosca.nodes.WebApplication.WordPress: + derived_from: tosca.nodes.WebApplication + requirements: + - database_endpoint: + capability: tosca.capabilities.Endpoint.Database + node: tosca.nodes.Database + relationship: tosca.relationships.ConnectsTo + interfaces: + Standard: + inputs: + wp_db_name: + type: string + wp_db_user: + type: string + wp_db_password: + type: string diff --git a/nfvparser/toscaparser/tests/data/functions/test_container_cap_child.yaml b/nfvparser/toscaparser/tests/data/functions/test_container_cap_child.yaml new file mode 100644 index 0000000..84118c8 --- /dev/null +++ b/nfvparser/toscaparser/tests/data/functions/test_container_cap_child.yaml @@ -0,0 +1,28 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: > + TOSCA simple profile to test the get attribute function with HOST parameter + using a node that has as capability a child class of Container + +imports: + - ../custom_types/container_cap_child.yaml + +topology_template: + + node_templates: + + test_node: + type: tosca.nodes.SomeNode + properties: + some_prop: { get_attribute: [ HOST, public_address ] } + requirements: + - host_child: test_node2 + + test_node2: + type: tosca.nodes.SomeNode2 + requirements: + - host: server + + server: + type: tosca.nodes.Compute + diff --git a/nfvparser/toscaparser/tests/data/functions/test_get_prop_cap_bool.yaml b/nfvparser/toscaparser/tests/data/functions/test_get_prop_cap_bool.yaml new file mode 100644 index 0000000..d9c4c1c --- /dev/null +++ b/nfvparser/toscaparser/tests/data/functions/test_get_prop_cap_bool.yaml @@ -0,0 +1,37 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: TOSCA test for boolean properties + +node_types: + + tosca.nodes.SoftwareComponentTest: + derived_from: tosca.nodes.SoftwareComponent + properties: + some_prop: + type: boolean + + tosca.nodes.ComputeTest: + derived_from: tosca.nodes.Compute + capabilities: + endpoint: + type: tosca.capabilities.Endpoint + +topology_template: + + node_templates: + + software: + type: tosca.nodes.SoftwareComponentTest + properties: + some_prop: { get_property: [ HOST, endpoint, secure ] } + requirements: + - host: server + + server: + type: tosca.nodes.ComputeTest + capabilities: + endpoint: + properties: + network_name: PUBLIC + secure: false + diff --git a/nfvparser/toscaparser/tests/data/functions/test_get_prop_cap_host.yaml b/nfvparser/toscaparser/tests/data/functions/test_get_prop_cap_host.yaml new file mode 100644 index 0000000..7fcb4a7 --- /dev/null +++ b/nfvparser/toscaparser/tests/data/functions/test_get_prop_cap_host.yaml @@ -0,0 +1,25 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: TOSCA test for the IM + +imports: + - test: ../custom_types/node_with_cap.yaml + +topology_template: + + node_templates: + + some_node: + type: tosca.nodes.SomeNode + properties: + some_prop: { get_property: [ HOST, some_req, type ] } + requirements: + - some_req: server + + server: + type: tosca.nodes.NodeWithCap + capabilities: + some_req: + properties: + type: someval + diff --git a/nfvparser/toscaparser/tests/data/policies/tosca_policy_template.yaml b/nfvparser/toscaparser/tests/data/policies/tosca_policy_template.yaml index 47f7870..4c18d9d 100644 --- a/nfvparser/toscaparser/tests/data/policies/tosca_policy_template.yaml +++ b/nfvparser/toscaparser/tests/data/policies/tosca_policy_template.yaml @@ -75,6 +75,32 @@ topology_template: inputs: strategy: LEAST_USED implementation: Senlin.webhook() + high_cpu_usage: + description: trigger + meter_name: cpu_util + condition: + constraint: utilization greater_than 60% + threshold: 60 + period: 600 + evaluations: 1 + method: average + comparison_operator: gt + metadata: SG1 + action: [SP1] + + low_cpu_usage: + description: trigger + meter_name: cpu_util + condition: + constraint: utilization less_than 20% + threshold: 20 + period: 600 + evaluations: 1 + method: average + comparison_operator: gt + metadata: SG1 + action: [SP1] + - my_groups_placement: type: mycompany.mytypes.myScalingPolicy targets: [ webserver_group ] diff --git a/nfvparser/toscaparser/tests/data/test_custom_capabilty.yaml b/nfvparser/toscaparser/tests/data/test_custom_capabilty.yaml new file mode 100644 index 0000000..03a8a07 --- /dev/null +++ b/nfvparser/toscaparser/tests/data/test_custom_capabilty.yaml @@ -0,0 +1,23 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: TOSCA simple profile to test a custom defined capability + +imports: + - custom_types/custom_cap.yaml + +topology_template: + + node_templates: + + node_req: + type: tosca.nodes.NodeWithReq + requirements: + - host: node_cap + + node_cap: + type: tosca.nodes.NodeWithCap + requirements: + - host: server + + server: + type: tosca.nodes.Compute diff --git a/nfvparser/toscaparser/tests/data/test_instance_nested_imports.yaml b/nfvparser/toscaparser/tests/data/test_instance_nested_imports.yaml index 6aa9307..a130e49 100644 --- a/nfvparser/toscaparser/tests/data/test_instance_nested_imports.yaml +++ b/nfvparser/toscaparser/tests/data/test_instance_nested_imports.yaml @@ -5,6 +5,7 @@ description: > imports: - wordpress: custom_types/nested_test_wordpress.yaml + - custom_types/nested_test_kibana.yaml topology_template: @@ -20,3 +21,6 @@ topology_template: logstash: type: tosca.nodes.SoftwareComponent.Logstash + + kibana: + type: tosca.nodes.SoftwareComponent.Kibana diff --git a/nfvparser/toscaparser/tests/data/topology_template/definitions.yaml b/nfvparser/toscaparser/tests/data/topology_template/definitions.yaml index ba5eac1..300bb8a 100644 --- a/nfvparser/toscaparser/tests/data/topology_template/definitions.yaml +++ b/nfvparser/toscaparser/tests/data/topology_template/definitions.yaml @@ -49,7 +49,7 @@ node_types: derived_from: tosca.nodes.Database example.SomeApp: - derived_from: tosca.nodes.SoftwareComponent + derived_from: tosca.nodes.WebApplication properties: admin_user: type: string diff --git a/nfvparser/toscaparser/tests/data/tosca_helloworld_with_version_1_1.yaml b/nfvparser/toscaparser/tests/data/tosca_helloworld_with_version_1_1.yaml new file mode 100644 index 0000000..eeb5f7b --- /dev/null +++ b/nfvparser/toscaparser/tests/data/tosca_helloworld_with_version_1_1.yaml @@ -0,0 +1,23 @@ +tosca_definitions_version: tosca_simple_yaml_1_1 + +description: Template for deploying a single server with predefined properties. + +topology_template: + node_templates: + my_server: + type: tosca.nodes.Compute + capabilities: + # Host container properties + host: + properties: + num_cpus: 2 + disk_size: 10 GB + mem_size: 512 MB + # Guest Operating System properties + os: + properties: + # host Operating System image properties + architecture: x86_64 + type: Linux + distribution: RHEL + version: 6.5 diff --git a/nfvparser/toscaparser/tests/test_functions.py b/nfvparser/toscaparser/tests/test_functions.py index fa60140..2e1d71e 100644 --- a/nfvparser/toscaparser/tests/test_functions.py +++ b/nfvparser/toscaparser/tests/test_functions.py @@ -188,6 +188,26 @@ class IntrinsicFunctionsTest(TestCase): self.assertIsInstance(source_port, functions.GetProperty) self.assertEqual(3306, source_port.result()) + def test_get_prop_cap_host(self): + tosca_tpl = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "data/functions/test_get_prop_cap_host.yaml") + some_node = self._get_node('some_node', + ToscaTemplate(tosca_tpl)) + some_prop = some_node.get_properties()['some_prop'] + self.assertIsInstance(some_prop.value, functions.GetProperty) + self.assertEqual('someval', some_prop.value.result()) + + def test_get_prop_cap_bool(self): + tosca_tpl = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "data/functions/test_get_prop_cap_bool.yaml") + some_node = self._get_node('software', + ToscaTemplate(tosca_tpl)) + some_prop = some_node.get_properties()['some_prop'] + self.assertIsInstance(some_prop.value, functions.GetProperty) + self.assertEqual(False, some_prop.value.result()) + class GetAttributeTest(TestCase): @@ -318,6 +338,10 @@ class GetAttributeTest(TestCase): self.assertIsNotNone(self._load_template( 'functions/test_get_implicit_attribute.yaml')) + def test_get_attribute_capability_inheritance(self): + self.assertIsNotNone(self._load_template( + 'functions/test_container_cap_child.yaml')) + class ConcatTest(TestCase): diff --git a/nfvparser/toscaparser/tests/test_topology_template.py b/nfvparser/toscaparser/tests/test_topology_template.py index 3aabc9b..a0d6dc3 100644 --- a/nfvparser/toscaparser/tests/test_topology_template.py +++ b/nfvparser/toscaparser/tests/test_topology_template.py @@ -91,7 +91,7 @@ class TopologyTemplateTest(TestCase): tpl_name = "app" expected_type = "example.SomeApp" expected_properties = ['admin_user', 'pool_size'] - expected_capabilities = ['feature', 'message_receiver'] + expected_capabilities = ['app_endpoint', 'feature', 'message_receiver'] expected_requirements = [{'host': {'node': 'websrv'}}] expected_relationshp = ['tosca.relationships.HostedOn'] expected_host = ['websrv'] diff --git a/nfvparser/toscaparser/tests/test_toscatpl.py b/nfvparser/toscaparser/tests/test_toscatpl.py index fac8687..fd8ee90 100644 --- a/nfvparser/toscaparser/tests/test_toscatpl.py +++ b/nfvparser/toscaparser/tests/test_toscatpl.py @@ -216,6 +216,10 @@ class ToscaTemplateTest(TestCase): tosca_tpl = self._load_template('test_no_outputs_in_template.yaml') self.assertEqual(0, len(tosca_tpl.outputs)) + def test_template_file_with_suffix_yml(self): + tosca_tpl = self._load_template('custom_types/wordpress.yml') + self.assertIsNotNone(tosca_tpl) + def test_relationship_interface(self): template = ToscaTemplate(self.tosca_elk_tpl) for node_tpl in template.nodetemplates: @@ -510,7 +514,8 @@ class ToscaTemplateTest(TestCase): os.path.dirname(os.path.abspath(__file__)), "data/test_instance_nested_imports.yaml") tosca = ToscaTemplate(tosca_tpl) - expected_custom_types = ['tosca.nodes.WebApplication.WordPress', + expected_custom_types = ['tosca.nodes.SoftwareComponent.Kibana', + 'tosca.nodes.WebApplication.WordPress', 'test_namespace_prefix.Rsyslog', 'Test2ndRsyslogType', 'test_2nd_namespace_prefix.Rsyslog', @@ -661,6 +666,17 @@ class ToscaTemplateTest(TestCase): self.assertEqual(tosca.version, "tosca_simple_yaml_1_0") + def test_yaml_dict_tpl_with_version_1_1(self): + test_tpl = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "data/tosca_helloworld_with_version_1_1.yaml") + + yaml_dict_tpl = toscaparser.utils.yamlparser.load_yaml(test_tpl) + + tosca = ToscaTemplate(yaml_dict_tpl=yaml_dict_tpl) + + self.assertEqual(tosca.version, "tosca_simple_yaml_1_1") + def test_yaml_dict_tpl_with_params_and_url_import(self): test_tpl = os.path.join( os.path.dirname(os.path.abspath(__file__)), diff --git a/nfvparser/toscaparser/tests/test_toscatplvalidation.py b/nfvparser/toscaparser/tests/test_toscatplvalidation.py index 911867f..a8b1590 100644 --- a/nfvparser/toscaparser/tests/test_toscatplvalidation.py +++ b/nfvparser/toscaparser/tests/test_toscatplvalidation.py @@ -101,6 +101,7 @@ class ToscaTemplateValidationTest(TestCase): _('The template version "tosca_simple_yaml_1_10 in ' 'custom_types/imported_sample.yaml" is invalid. ' 'Valid versions are "tosca_simple_yaml_1_0, ' + 'tosca_simple_yaml_1_1, ' 'tosca_simple_profile_for_nfv_1_0_0".')) exception.ExceptionCollector.assertExceptionMessage( exception.UnknownFieldError, @@ -446,6 +447,18 @@ heat-translator/master/translator/tests/data/custom_types/wordpress.yaml self.assertTrue(custom_defs.get("mycompany.tosca.nodes." "WebApplication.WordPress")) + def test_imports_file_with_suffix_yml(self): + tpl_snippet = ''' + imports: + - custom_types/wordpress.yml + ''' + path = 'toscaparser/tests/data/tosca_elk.yaml' + custom_defs = self._imports_content_test(tpl_snippet, + path, + "node_types") + self.assertTrue(custom_defs.get("tosca.nodes." + "WebApplication.WordPress")) + def test_import_error_file_uri(self): tpl_snippet = ''' imports: @@ -1581,7 +1594,7 @@ heat-translator/master/translator/tests/data/custom_types/wordpress.yaml lambda: Policy(name, policies[name], None, None)) self.assertEqual(expectedmessage, err.__str__()) - def test_policy_trigger_valid_keyname(self): + def test_policy_trigger_valid_keyname_senlin_resources(self): tpl_snippet = ''' triggers: - resize_compute: @@ -1610,7 +1623,28 @@ heat-translator/master/translator/tests/data/custom_types/wordpress.yaml name = list(triggers.keys())[0] Triggers(name, triggers[name]) - def test_policy_trigger_invalid_keyname(self): + def test_policy_trigger_valid_keyname_heat_resources(self): + tpl_snippet = ''' + triggers: + - high_cpu_usage: + description: trigger + meter_name: cpu_util + condition: + constraint: utilization greater_than 60% + threshold: 60 + period: 600 + evaluations: 1 + method: average + comparison_operator: gt + metadata: SG1 + action: [SP1] + ''' + triggers = (toscaparser.utils.yamlparser. + simple_parse(tpl_snippet))['triggers'][0] + name = list(triggers.keys())[0] + Triggers(name, triggers[name]) + + def test_policy_trigger_invalid_keyname_senlin_resources(self): tpl_snippet = ''' triggers: - resize_compute: @@ -1646,6 +1680,34 @@ heat-translator/master/translator/tests/data/custom_types/wordpress.yaml lambda: Triggers(name, triggers[name])) self.assertEqual(expectedmessage, err.__str__()) + def test_policy_trigger_invalid_keyname_heat_resources(self): + tpl_snippet = ''' + triggers: + - high_cpu_usage: + description: trigger + meter_name: cpu_util + condition: + constraint: utilization greater_than 60% + threshold: 60 + period: 600 + evaluations: 1 + method: average + comparison_operator: gt + metadata1: SG1 + action: [SP1] + ''' + triggers = (toscaparser.utils.yamlparser. + simple_parse(tpl_snippet))['triggers'][0] + name = list(triggers.keys())[0] + expectedmessage = _( + 'Triggers "high_cpu_usage" contains unknown field ' + '"metadata1". Refer to the definition ' + 'to verify valid values.') + err = self.assertRaises( + exception.UnknownFieldError, + lambda: Triggers(name, triggers[name])) + self.assertEqual(expectedmessage, err.__str__()) + def test_policy_missing_required_keyname(self): tpl_snippet = ''' policies: diff --git a/nfvparser/toscaparser/topology_template.py b/nfvparser/toscaparser/topology_template.py index 4571fe7..080108e 100644 --- a/nfvparser/toscaparser/topology_template.py +++ b/nfvparser/toscaparser/topology_template.py @@ -66,7 +66,7 @@ class TopologyTemplate(object): def _inputs(self): inputs = [] for name, attrs in self._tpl_inputs().items(): - input = Input(name, attrs) + input = Input(name, attrs, self.custom_defs) if self.parsed_params and name in self.parsed_params: input.validate(self.parsed_params[name]) else: diff --git a/nfvparser/toscaparser/tosca_template.py b/nfvparser/toscaparser/tosca_template.py index f48078f..b0df424 100644 --- a/nfvparser/toscaparser/tosca_template.py +++ b/nfvparser/toscaparser/tosca_template.py @@ -18,6 +18,9 @@ from copy import deepcopy from toscaparser.common.exception import ExceptionCollector from toscaparser.common.exception import InvalidTemplateVersion from toscaparser.common.exception import MissingRequiredFieldError +from toscaparser.common.exception import MissingRequiredInputError +from toscaparser.common.exception import MissingRequiredOutputError +from toscaparser.common.exception import MissingRequiredParameterError from toscaparser.common.exception import UnknownFieldError from toscaparser.common.exception import ValidationError from toscaparser.elements.entity_type import update_definitions @@ -55,17 +58,21 @@ YAML_LOADER = toscaparser.utils.yamlparser.load_yaml class ToscaTemplate(object): exttools = ExtTools() - VALID_TEMPLATE_VERSIONS = ['tosca_simple_yaml_1_0'] + VALID_TEMPLATE_VERSIONS = ['tosca_simple_yaml_1_0', + 'tosca_simple_yaml_1_1'] VALID_TEMPLATE_VERSIONS.extend(exttools.get_versions()) - ADDITIONAL_SECTIONS = {'tosca_simple_yaml_1_0': SPECIAL_SECTIONS} + ADDITIONAL_SECTIONS = {'tosca_simple_yaml_1_0': SPECIAL_SECTIONS, + 'tosca_simple_yaml_1_1': SPECIAL_SECTIONS} ADDITIONAL_SECTIONS.update(exttools.get_sections()) '''Load the template data.''' def __init__(self, path=None, parsed_params=None, a_file=True, - yaml_dict_tpl=None, sub_mapped_node_template=None): + yaml_dict_tpl=None, sub_mapped_node_template=None, + no_required_paras_check=False, + debug_mode=False): if sub_mapped_node_template is None: ExceptionCollector.start() self.a_file = a_file @@ -75,6 +82,9 @@ class ToscaTemplate(object): self.sub_mapped_node_template = sub_mapped_node_template self.nested_tosca_tpls_with_topology = {} self.nested_tosca_templates_with_topology = [] + self.no_required_paras_check = no_required_paras_check + self.debug_mode = debug_mode + if path: self.input_path = path self.path = self._get_path(path) @@ -233,16 +243,36 @@ class ToscaTemplate(object): if self._is_sub_mapped_node(nodetemplate, tosca_tpl): parsed_params = self._get_params_for_nested_template( nodetemplate) - nested_template = ToscaTemplate( - path=fname, parsed_params=parsed_params, - yaml_dict_tpl=tosca_tpl, - sub_mapped_node_template=nodetemplate) - if nested_template._has_substitution_mappings(): + + cache_exeptions = deepcopy(ExceptionCollector.exceptions) + cache_exeptions_state = \ + deepcopy(ExceptionCollector.collecting) + nested_template = None + try: + nrpv = self.no_required_paras_check + nested_template = ToscaTemplate( + path=fname, parsed_params=parsed_params, + sub_mapped_node_template=nodetemplate, + no_required_paras_check=nrpv, + debug_mode=self.debug_mode) + except ValidationError as e: + log.error(e.message) + if self.debug_mode: + print(e.message) + else: + raise e + + ExceptionCollector.exceptions = deepcopy(cache_exeptions) + ExceptionCollector.collecting = \ + deepcopy(cache_exeptions_state) + + if nested_template and \ + nested_template._has_substitution_mappings(): # Record the nested templates in top level template self.nested_tosca_templates_with_topology.\ append(nested_template) # Set the substitution toscatemplate for mapped node - nodetemplate.sub_mapping_tosca_template = \ + nodetemplate.substitution_mapped = \ nested_template def _validate_field(self): @@ -268,11 +298,12 @@ class ToscaTemplate(object): what=version, valid_versions=', '. join(self.VALID_TEMPLATE_VERSIONS))) else: - if version != 'tosca_simple_yaml_1_0': + if (version != 'tosca_simple_yaml_1_0' and + version != 'tosca_simple_yaml_1_1'): update_definitions(version) def _get_path(self, path): - if path.lower().endswith('.yaml'): + if path.lower().endswith('.yaml') or path.lower().endswith('.yml'): return path elif path.lower().endswith(('.zip', '.csar')): # a CSAR archive @@ -287,18 +318,37 @@ class ToscaTemplate(object): % {'path': path})) def verify_template(self): + if self.no_required_paras_check: + ExceptionCollector.removeException( + MissingRequiredParameterError) + ExceptionCollector.removeException( + MissingRequiredInputError) + ExceptionCollector.removeException( + MissingRequiredOutputError) if ExceptionCollector.exceptionsCaught(): if self.input_path: - raise ValidationError( + exceptions = ValidationError( message=(_('\nThe input "%(path)s" failed validation with ' 'the following error(s): \n\n\t') % {'path': self.input_path}) + '\n\t'.join(ExceptionCollector.getExceptionsReport())) else: - raise ValidationError( + exceptions = ValidationError( message=_('\nThe pre-parsed input failed validation with ' 'the following error(s): \n\n\t') + '\n\t'.join(ExceptionCollector.getExceptionsReport())) + if not self.debug_mode: + raise exceptions + else: + if self.sub_mapped_node_template: + msg = _('======== nested service template ======== ') + + else: + msg = _('======== main service template ======== ') + print(msg) + print(exceptions.message) + log.error(msg) + log.error(exceptions.message) else: if self.input_path: msg = (_('The input "%(path)s" successfully passed ' diff --git a/nfvparser/toscaparser/triggers.py b/nfvparser/toscaparser/triggers.py index 9edeef4..8e47eee 100644 --- a/nfvparser/toscaparser/triggers.py +++ b/nfvparser/toscaparser/triggers.py @@ -16,12 +16,16 @@ import logging from toscaparser.common.exception import ExceptionCollector from toscaparser.common.exception import UnknownFieldError from toscaparser.entity_template import EntityTemplate - -SECTIONS = (DESCRIPTION, EVENT, SCHEDULE, TARGET_FILTER, CONDITION, ACTION) = \ - ('description', 'event_type', 'schedule', - 'target_filter', 'condition', 'action') -CONDITION_KEYNAMES = (CONTRAINT, PERIOD, EVALUATIONS, METHOD) = \ - ('constraint', 'period', 'evaluations', 'method') +from toscaparser.utils import validateutils + +SECTIONS = (DESCRIPTION, EVENT, SCHEDULE, METER_NAME, METADATA, + TARGET_FILTER, CONDITION, ACTION) = \ + ('description', 'event_type', 'schedule', 'meter_name', + 'metadata', 'target_filter', 'condition', 'action') +CONDITION_KEYNAMES = (CONSTRAINT, PERIOD, EVALUATIONS, METHOD, + THRESHOLD, COMPARISON_OPERATOR) = \ + ('constraint', 'period', 'evaluations', + 'method', 'threshold', 'comparison_operator') log = logging.getLogger('tosca') @@ -34,6 +38,7 @@ class Triggers(EntityTemplate): self.trigger_tpl = trigger_tpl self._validate_keys() self._validate_condition() + self._validate_input() def get_description(self): return self.trigger_tpl['description'] @@ -66,3 +71,12 @@ class Triggers(EntityTemplate): ExceptionCollector.appendException( UnknownFieldError(what='Triggers "%s"' % self.name, field=key)) + + def _validate_input(self): + for key, value in self.get_condition().items(): + if key in [PERIOD, EVALUATIONS]: + validateutils.validate_integer(value) + elif key == THRESHOLD: + validateutils.validate_numeric(value) + elif key in [METER_NAME, METHOD]: + validateutils.validate_string(value) |