class TOSCAException(Exception):
class UnsupportedTypeError(TOSCAException):
class MissingRequiredFieldError(TOSCAException):
class UnknownFieldError(TOSCAException):
class TypeMismatchError(TOSCAException):
class InvalidNodeTypeError(TOSCAException):
class InvalidTypeError(TOSCAException):
class InvalidTypeAdditionalRequirementsError(TOSCAException):
class RangeValueError(TOSCAException):
class InvalidSchemaError(TOSCAException):
class ValidationError(TOSCAException):
class UnknownInputError(TOSCAException):
class UnknownOutputError(TOSCAException):
class MissingRequiredInputError(TOSCAException):
class MissingRequiredParameterError(TOSCAException):
class MissingDefaultValueError(TOSCAException):
class MissingRequiredOutputError(TOSCAException):
class InvalidPropertyValueError(TOSCAException):
class InvalidTemplateVersion(TOSCAException):
class InvalidTOSCAVersionPropertyException(TOSCAException):
class URLException(TOSCAException):
class ToscaExtImportError(TOSCAException):
class ToscaExtAttributeError(TOSCAException):
class InvalidGroupTargetException(TOSCAException):
class ExceptionCollector(object):
    _FATAL_EXCEPTION_FORMAT_ERRORS = False
    def __init__(self, **kwargs):
    def __str__(self):
    def generate_inv_schema_property_error(self, attr, value, valid_values):
    def set_fatal_format_exception(flag):
    def clear():
    def start():
    def stop():
    def contains(exception):
    def appendException(exception):
    def exceptionsCaught():
    def getTraceString(traceList):
    def getExceptionReportEntry(exception, full=True):
    def getExceptions():
    def getExceptionsReport(full=True):
    def assertExceptionMessage(exception, message): class ImportsLoader(object):

    IMPORTS_SECTION = (FILE, REPOSITORY, NAMESPACE_URI, NAMESPACE_PREFIX) = \
                      ('file', 'repository', 'namespace_uri',
                       'namespace_prefix')

    def __init__(self, importslist, path, type_definition_list=None,
                 tpl=None):
        self.importslist = importslist
        self.custom_defs = {}
        self.nested_tosca_tpls = []
        self.nested_imports = {}
        if not path and not tpl:
            msg = _('Input tosca template is not provided.')
            log.warning(msg)
            ExceptionCollector.appendException(ValidationError(message=msg))
        self.path = path
        self.repositories = {}
        if tpl and tpl.get('repositories'):
            self.repositories = tpl.get('repositories')
        self.type_definition_list = []
        if type_definition_list:
            if isinstance(type_definition_list, list):
                self.type_definition_list = type_definition_list
            else:
                self.type_definition_list.append(type_definition_list)
        self._validate_and_load_imports()

    def get_custom_defs(self):
        return self.custom_defs

    def get_nested_tosca_tpls(self):
        return self.nested_tosca_tpls

    def get_nested_imports(self):
        return self.nested_imports

    def _validate_and_load_imports(self):
        imports_names = set()

        if not self.importslist:
            msg = _('"imports" keyname is defined without including '
                    'templates.')
            log.error(msg)
            ExceptionCollector.appendException(ValidationError(message=msg))
            return

        for import_def in self.importslist:
            if isinstance(import_def, dict):
                for import_name, import_uri in import_def.items():
                    if import_name in imports_names:
                        msg = (_('Duplicate import name "%s" was found.') %
                               import_name)
                        log.error(msg)
                        ExceptionCollector.appendException(
                            ValidationError(message=msg))
                    imports_names.add(import_name)

                    full_file_name, custom_type = self._load_import_template(
                        import_name, import_uri)
                    namespace_prefix = None
                    if isinstance(import_uri, dict):
                        namespace_prefix = import_uri.get(
                            self.NAMESPACE_PREFIX)
                    if custom_type:
                        TypeValidation(custom_type, import_def)
                        self._update_custom_def(custom_type, namespace_prefix)
            else:  # old style of imports
                full_file_name, custom_type = self._load_import_template(
                    None, import_def)
                if custom_type:
                    TypeValidation(
                        custom_type, import_def)
                    self._update_custom_def(custom_type, None)

            if custom_type and 'imports' in custom_type.keys():
                self.nested_imports.update(
                    {full_file_name: custom_type['imports']})
            self._update_nested_tosca_tpls(full_file_name, custom_type)

    def _update_custom_def(self, custom_type, namespace_prefix):
        outer_custom_types = {}
        for type_def in self.type_definition_list:
            outer_custom_types = custom_type.get(type_def)
            if outer_custom_types:
                if type_def == "imports":
                    for i in self.custom_defs.get('imports', []):
                        if i not in outer_custom_types:
                            outer_custom_types.append(i)
                    self.custom_defs.update({'imports': outer_custom_types})
                else:
                    if namespace_prefix:
                        prefix_custom_types = {}
                        for type_def_key in outer_custom_types.keys():
                            namespace_prefix_to_key = (namespace_prefix +
                                                       "." + type_def_key)
                            prefix_custom_types[namespace_prefix_to_key] = \
                                outer_custom_types[type_def_key]
                        self.custom_defs.update(prefix_custom_types)
                    else:
                        self.custom_defs.update(outer_custom_types)

    def _update_nested_tosca_tpls(self, full_file_name, custom_tpl):
        if full_file_name and custom_tpl:
            topo_tpl = {full_file_name: custom_tpl}
            self.nested_tosca_tpls.append(topo_tpl)

    def _validate_import_keys(self, import_name, import_uri_def):
        if self.FILE not in import_uri_def.keys():
            log.warning('Missing keyname "file" in import "%(name)s".'
                        % {'name': import_name})
            ExceptionCollector.appendException(
                MissingRequiredFieldError(
                    what='Import of template "%s"' % import_name,
                    required=self.FILE))
        for key in import_uri_def.keys():
            if key not in self.IMPORTS_SECTION:
                log.warning('Unknown keyname "%(key)s" error in '
                            'imported definition "%(def)s".'
                            % {'key': key, 'def': import_name})
                ExceptionCollector.appendException(
                    UnknownFieldError(
                        what='Import of template "%s"' % import_name,
                        field=key))

    def _load_import_template(self, import_name, import_uri_def):
        """Handle custom types defined in imported template files

        This method loads the custom type definitions referenced in "imports"
        section of the TOSCA YAML template by determining whether each import
        is specified via a file reference (by relative or absolute path) or a
        URL reference.

        Possibilities:
        +----------+--------+------------------------------+
        | template | import | comment                      |
        +----------+--------+------------------------------+
        | file     | file   | OK                           |
        | file     | URL    | OK                           |
        | preparsed| file   | file must be a full path     |
        | preparsed| URL    | OK                           |
        | URL      | file   | file must be a relative path |
        | URL      | URL    | OK                           |
        +----------+--------+------------------------------+
        """
        short_import_notation = False
        if isinstance(import_uri_def, dict):
            self._validate_import_keys(import_name, import_uri_def)
            file_name = import_uri_def.get(self.FILE)
            repository = import_uri_def.get(self.REPOSITORY)
            repos = self.repositories.keys()
            if repository is not None:
                if repository not in repos:
                    ExceptionCollector.appendException(
                        InvalidPropertyValueError(
                            what=_('Repository is not found in "%s"') % repos))
        else:
            file_name = import_uri_def
            repository = None
            short_import_notation = True

        if not file_name:
            msg = (_('A template file name is not provided with import '
                     'definition "%(import_name)s".')
                   % {'import_name': import_name})
            log.error(msg)
            ExceptionCollector.appendException(ValidationError(message=msg))
            return None, None

        if toscaparser.utils.urlutils.UrlUtils.validate_url(file_name):
            return file_name, YAML_LOADER(file_name, False)
        elif not repository:
            import_template = None
            if self.path:
                if toscaparser.utils.urlutils.UrlUtils.validate_url(self.path):
                    if os.path.isabs(file_name):
                        msg = (_('Absolute file name "%(name)s" cannot be '
                                 'used in a URL-based input template '
                                 '"%(template)s".')
                               % {'name': file_name, 'template': self.path})
                        log.error(msg)
                        ExceptionCollector.appendException(ImportError(msg))
                        return None, None
                    import_template = toscaparser.utils.urlutils.UrlUtils.\
                        join_url(self.path, file_name)
                    a_file = False
                else:
                    a_file = True
                    main_a_file = os.path.isfile(self.path)

                    if main_a_file:
                        if os.path.isfile(file_name):
                            import_template = file_name
                        else:
                            full_path = os.path.join(
                                os.path.dirname(os.path.abspath(self.path)),
                                file_name)
                            if os.path.isfile(full_path):
                                import_template = full_path
                            else:
                                file_path = file_name.rpartition("/")
                                dir_path = os.path.dirname(os.path.abspath(
                                    self.path))
                                if file_path[0] != '' and dir_path.endswith(
                                        file_path[0]):
                                    import_template = dir_path + "/" +\
                                        file_path[2]
                                    if not os.path.isfile(import_template):
                                        msg = (_('"%(import_template)s" is '
                                                 'not a valid file')
                                               % {'import_template':
                                                  import_template})
                                        log.error(msg)
                                        ExceptionCollector.appendException
                                        (ValueError(msg))
            else:  # template is pre-parsed
                if os.path.isabs(file_name) and os.path.isfile(file_name):
                    a_file = True
                    import_template = file_name
                else:
                    msg = (_('Relative file name "%(name)s" cannot be used '
                             'in a pre-parsed input template.')
                           % {'name': file_name})
                    log.error(msg)
                    ExceptionCollector.appendException(ImportError(msg))
                    return None, None

            if not import_template:
                log.error('Import "%(name)s" is not valid.' %
                          {'name': import_uri_def})
                ExceptionCollector.appendException(
                    ImportError(_('Import "%s" is not valid.') %
                                import_uri_def))
                return None, None
            return import_template, YAML_LOADER(import_template, a_file)

        if short_import_notation:
            log.error('Import "%(name)s" is not valid.' % import_uri_def)
            ExceptionCollector.appendException(
                ImportError(_('Import "%s" is not valid.') % import_uri_def))
            return None, None

        full_url = ""
        if repository:
            if self.repositories:
                for repo_name, repo_def in self.repositories.items():
                    if repo_name == repository:
                        # Remove leading, ending spaces and strip
                        # the last character if "/"
                        repo_url = ((repo_def['url']).strip()).rstrip("//")
                        full_url = repo_url + "/" + file_name

            if not full_url:
                msg = (_('referenced repository "%(n_uri)s" in import '
                         'definition "%(tpl)s" not found.')
                       % {'n_uri': repository, 'tpl': import_name})
                log.error(msg)
                ExceptionCollector.appendException(ImportError(msg))
                return None, None

        if toscaparser.utils.urlutils.UrlUtils.validate_url(full_url):
            return full_url, YAML_LOADER(full_url, False)
        else:
            msg = (_('repository url "%(n_uri)s" is not valid in import '
                     'definition "%(tpl)s".')
                   % {'n_uri': repo_url, 'tpl': import_name})
            log.error(msg)
            ExceptionCollector.appendException(ImportError(msg)) 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:
            # TODO(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 node "%(node)s" for "%(name)s"'
                    ' can not be full-filled.') \
                % {'node': node, 'name': self.name}
            if (node in list(self.type_definition.TOSCA_DEF.keys())
               or node in self.custom_def):
                ExceptionCollector.appendException(NotImplementedError(msg))
                return

            if node not in self.templates:
                ExceptionCollector.appendException(
                    KeyError(_('Node template "%(node)s" was not found'
                               ' in "%(name)s".')
                             % {'node': node, 'name': self.name}))
                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:
                    ExceptionCollector.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:
                            ExceptionCollector.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, relationship, 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):
                ExceptionCollector.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):
        # TODO(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):
            ExceptionCollector.appendException(
                InvalidPropertyValueError(what=(occurrences)))

    def _validate_requirements_keys(self, requirement):
        for key in requirement.keys():
            if key not in self.REQUIREMENTS_SECTION:
                ExceptionCollector.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')
                else:
                    interfaces = self.type_definition.interfaces
                    if interfaces is None:
                        interfaces = dict()
                    if name in interfaces.keys():
                        self._common_validate_field(
                            value,
                            self._collect_custom_iface_operations(name),
                            'interfaces')
                    else:
                        ExceptionCollector.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:
                ExceptionCollector.appendException(
                    UnknownFieldError(what='Node template "%s"' % self.name,
                                      field=name)) class Input(object):

    INPUTFIELD = (TYPE, DESCRIPTION, DEFAULT, CONSTRAINTS, REQUIRED, STATUS,
                  ENTRY_SCHEMA) = ('type', 'description', 'default',
                                   'constraints', 'required', 'status',
                                   'entry_schema')

    def __init__(self, name, schema_dict):
        self.name = name
        self.schema = Schema(name, schema_dict)

        self._validate_field()
        self.validate_type(self.type)

    @property
    def type(self):
        return self.schema.type

    @property
    def required(self):
        return self.schema.required

    @property
    def description(self):
        return self.schema.description

    @property
    def default(self):
        return self.schema.default

    @property
    def constraints(self):
        return self.schema.constraints

    @property
    def status(self):
        return self.schema.status

    def validate(self, value=None):
        if value is not None:
            self._validate_value(value)

    def _validate_field(self):
        for name in self.schema.schema:
            if name not in self.INPUTFIELD:
                ExceptionCollector.appendException(
                    UnknownFieldError(what='Input "%s"' % self.name,
                                      field=name))

    def validate_type(self, input_type):
        if input_type not in Schema.PROPERTY_TYPES:
            ExceptionCollector.appendException(
                ValueError(_('Invalid type "%s".') % type))

    # TODO(anyone) Need to test for any built-in datatype not just network
    # that is, tosca.datatypes.* and not assume tosca.datatypes.network.*
    # TODO(anyone) Add support for tosca.datatypes.Credential
    def _validate_value(self, value):
        tosca = EntityType.TOSCA_DEF
        datatype = None
        if self.type in tosca:
            datatype = tosca[self.type]
        elif EntityType.DATATYPE_NETWORK_PREFIX + self.type in tosca:
            datatype = tosca[EntityType.DATATYPE_NETWORK_PREFIX + self.type]

        DataEntity.validate_datatype(self.type, value, None, datatype) class Output(object):

    OUTPUTFIELD = (DESCRIPTION, VALUE) = ('description', 'value')

    def __init__(self, name, attrs):
        self.name = name
        self.attrs = attrs

    @property
    def description(self):
        return self.attrs.get(self.DESCRIPTION)

    @property
    def value(self):
        return self.attrs.get(self.VALUE)

    def validate(self):
        self._validate_field()

    def _validate_field(self):
        if not isinstance(self.attrs, dict):
            ExceptionCollector.appendException(
                MissingRequiredFieldError(what='Output "%s"' % self.name,
                                          required=self.VALUE))
        if self.value is None:
            ExceptionCollector.appendException(
                MissingRequiredFieldError(what='Output "%s"' % self.name,
                                          required=self.VALUE))
        for name in self.attrs:
            if name not in self.OUTPUTFIELD:
                ExceptionCollector.appendException(
                    UnknownFieldError(what='Output "%s"' % self.name,
                                      field=name)) class Policy(EntityTemplate):
    '''Policies defined in Topology template.'''
    def __init__(self, name, policy, targets=None, targets_type=None,
                 custom_def=None):
        super(Policy, self).__init__(name,
                                     policy,
                                     'policy_type',
                                     custom_def)
        self.meta_data = None
        if self.METADATA in policy:
            self.meta_data = policy.get(self.METADATA)
            validateutils.validate_map(self.meta_data)
        self.targets_list = targets
        self.targets_type = targets_type
        self.triggers = self._triggers(policy.get(TRIGGERS))
        self.reservation = self._reservation(policy.get(RESERVATION))
        self.properties = None
        if 'properties' in policy:
            self.properties = policy['properties']
        self._validate_keys()

    @property
    def targets(self):
        return self.entity_tpl.get('targets')

    @property
    def description(self):
        return self.entity_tpl.get('description')

    @property
    def metadata(self):
        return self.entity_tpl.get('metadata')

    def get_targets_type(self):
        return self.targets_type

    def get_targets_list(self):
        return self.targets_list

    def _triggers(self, triggers):
        triggerObjs = []
        if triggers:
            for name, trigger_tpl in triggers.items():
                triggersObj = Triggers(name, trigger_tpl)
                triggerObjs.append(triggersObj)
        return triggerObjs

    def _reservation(self, reservation):
        reservationObjs = []
        if reservation:
            reservationObj = Reservation(reservation)
            reservationObjs.append(reservationObj)
        return reservationObjs

    def _validate_keys(self):
        for key in self.entity_tpl.keys():
            if key not in SECTIONS:
                ExceptionCollector.appendException(
                    UnknownFieldError(what='Policy "%s"' % self.name,
                                      field=key))

    def validate(self):
        self._validate_properties(self.entity_tpl, self.type_definition)
        for prop in self.get_properties_objects():
            prop.validate() class RelationshipTemplate(EntityTemplate):
    '''Relationship template.'''
    def __init__(self, relationship_template, name, custom_def=None,
                 target=None, source=None):
        super(RelationshipTemplate, self).__init__(name,
                                                   relationship_template,
                                                   'relationship_type',
                                                   custom_def)
        self.name = name.lower()
        self.target = target
        self.source = source

    def get_properties_objects(self):
        '''Return properties objects for this template.'''
        if self._properties is None:
            self._properties = self._create_relationship_properties()
        return self._properties

    def _create_relationship_properties(self):
        props = []
        properties = {}
        relationship = self.entity_tpl.get('relationship')

        if not relationship:
            for value in self.entity_tpl.values():
                if isinstance(value, dict):
                    relationship = value.get('relationship')
                    break

        if relationship:
            properties = self.type_definition.get_value(self.PROPERTIES,
                                                        relationship) or {}
        if not properties:
            properties = self.entity_tpl.get(self.PROPERTIES) or {}

        if properties:
            for name, value in properties.items():
                props_def = self.type_definition.get_properties_def()
                if props_def and name in props_def:
                    if name in properties.keys():
                        value = properties.get(name)
                    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 validate(self):
        self._validate_properties(self.entity_tpl, self.type_definition) class Repository(object):
    def __init__(self, repositories, values):
        self.name = repositories
        self.reposit = values
        if isinstance(self.reposit, dict):
            if 'url' not in self.reposit.keys():
                ExceptionCollector.appendException(
                    MissingRequiredFieldError(what=_('Repository "%s"')
                                              % self.name, required='url'))
            self.url = self.reposit['url']
        self.load_and_validate(self.name, self.reposit)

    def load_and_validate(self, val, reposit_def):
        self.keyname = val
        if isinstance(reposit_def, dict):
            for key in reposit_def.keys():
                if key not in SECTIONS:
                    ExceptionCollector.appendException(
                        UnknownFieldError(what=_('repositories "%s"')
                                          % self.keyname, field=key))

            if URL in reposit_def.keys():
                reposit_url = reposit_def.get(URL)
                url_val = toscaparser.utils.urlutils.UrlUtils.\
                    validate_url(reposit_url)
                if url_val is not True:
                    ExceptionCollector.appendException(
                        URLException(what=_('repsositories "%s" Invalid Url')
                                     % self.keyname)) class Reservation(EntityTemplate):

    '''Reservation defined in policies of topology template'''

    def __init__(self, reservation_tpl):
        self.reservation_tpl = reservation_tpl
        self._validate_keys()
        self._validate_missing_field()

    def _validate_keys(self):
        for key in self.reservation_tpl.keys():
            if key not in SECTIONS:
                raise UnknownFieldError(what='Reservation', field=key)

    def _validate_missing_field(self):
        for key in SECTIONS:
            if key not in self.reservation_tpl.keys():
                raise MissingRequiredFieldError(what='Reservation',
                                                required=key) class TestCase(testscenarios.TestWithScenarios, testtools.TestCase):

    """Test case base class for all unit tests."""

    def setUp(self):
        """Run before each test method to initialize test environment."""

        super(TestCase, self).setUp()
        test_timeout = os.environ.get('OS_TEST_TIMEOUT', 0)
        try:
            test_timeout = int(test_timeout)
        except ValueError:
            # If timeout value is invalid do not set a timeout.
            test_timeout = 0
        if test_timeout > 0:
            self.useFixture(fixtures.Timeout(test_timeout, gentle=True))

        self.useFixture(fixtures.NestedTempfile())
        self.useFixture(fixtures.TempHomeDir())

        if os.environ.get('OS_STDOUT_CAPTURE') in _TRUE_VALUES:
            stdout = self.useFixture(fixtures.StringStream('stdout')).stream
            self.useFixture(fixtures.MonkeyPatch('sys.stdout', stdout))
        if os.environ.get('OS_STDERR_CAPTURE') in _TRUE_VALUES:
            stderr = self.useFixture(fixtures.StringStream('stderr')).stream
            self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr))

        self.log_fixture = self.useFixture(fixtures.FakeLogger())

    def _load_template(self, filename):
        """Load a Tosca template from tests data folder.

        :param filename: Tosca template file name to load.
        :return: ToscaTemplate
        """
        return ToscaTemplate(os.path.join(
            os.path.dirname(os.path.abspath(__file__)),
            'data',
            filename)) class TopologyTemplate(object):

    '''Load the template data.'''
    def __init__(self, template, custom_defs,
                 rel_types=None, parsed_params=None,
                 sub_mapped_node_template=None):
        self.tpl = template
        self.sub_mapped_node_template = sub_mapped_node_template
        if self.tpl:
            self.custom_defs = custom_defs
            self.rel_types = rel_types
            self.parsed_params = parsed_params
            self._validate_field()
            self.description = self._tpl_description()
            self.inputs = self._inputs()
            self.relationship_templates = self._relationship_templates()
            self.nodetemplates = self._nodetemplates()
            self.outputs = self._outputs()
            if hasattr(self, 'nodetemplates'):
                self.graph = ToscaGraph(self.nodetemplates)
            self.groups = self._groups()
            self.policies = self._policies()
            self._process_intrinsic_functions()
            self.substitution_mappings = self._substitution_mappings()

    def _inputs(self):
        inputs = []
        for name, attrs in self._tpl_inputs().items():
            input = Input(name, attrs)
            if self.parsed_params and name in self.parsed_params:
                input.validate(self.parsed_params[name])
            else:
                default = input.default
                if default:
                    input.validate(default)
            if (self.parsed_params and input.name not in self.parsed_params
                or self.parsed_params is None) and input.required \
                    and input.default is None:
                log.warning('The required parameter %s '
                            'is not provided' % input.name)

            inputs.append(input)
        return inputs

    def _nodetemplates(self):
        nodetemplates = []
        tpls = self._tpl_nodetemplates()
        if tpls:
            for name in tpls:
                tpl = NodeTemplate(name, tpls, self.custom_defs,
                                   self.relationship_templates,
                                   self.rel_types)
                if (tpl.type_definition and
                    (tpl.type in tpl.type_definition.TOSCA_DEF or
                     (tpl.type not in tpl.type_definition.TOSCA_DEF and
                      bool(tpl.custom_def)))):
                    tpl.validate(self)
                    nodetemplates.append(tpl)
        return nodetemplates

    def _relationship_templates(self):
        rel_templates = []
        tpls = self._tpl_relationship_templates()
        for name in tpls:
            tpl = RelationshipTemplate(tpls[name], name, self.custom_defs)
            rel_templates.append(tpl)
        return rel_templates

    def _outputs(self):
        outputs = []
        for name, attrs in self._tpl_outputs().items():
            output = Output(name, attrs)
            output.validate()
            outputs.append(output)
        return outputs

    def _substitution_mappings(self):
        tpl_substitution_mapping = self._tpl_substitution_mappings()
        # if tpl_substitution_mapping and self.sub_mapped_node_template:
        if tpl_substitution_mapping:
            return SubstitutionMappings(tpl_substitution_mapping,
                                        self.nodetemplates,
                                        self.inputs,
                                        self.outputs,
                                        self.sub_mapped_node_template,
                                        self.custom_defs)

    def _policies(self):
        policies = []
        for policy in self._tpl_policies():
            for policy_name, policy_tpl in policy.items():
                target_list = policy_tpl.get('targets')
                target_objects = []
                targets_type = "groups"
                if target_list and len(target_list) >= 1:
                    target_objects = self._get_policy_groups(target_list)
                    if not target_objects:
                        targets_type = "node_templates"
                        target_objects = self._get_group_members(target_list)
                policyObj = Policy(policy_name, policy_tpl,
                                   target_objects, targets_type,
                                   self.custom_defs)
                # If the policyObj.type is defined in TOSCA_definition_1_0.yaml
                # or is defined as a custom definition, validate the properties
                # before adding it to the policies list.
                if (policyObj.type_definition and
                    (policyObj.type in policyObj.type_definition.TOSCA_DEF or
                     (policyObj.type not in policyObj.type_definition.TOSCA_DEF
                      and bool(policyObj.custom_def)))):
                    policyObj.validate()
                    policies.append(policyObj)
        return policies

    def _groups(self):
        groups = []
        member_nodes = None
        for group_name, group_tpl in self._tpl_groups().items():
            member_names = group_tpl.get('members')
            if member_names is not None:
                DataEntity.validate_datatype('list', member_names)
                if len(member_names) < 1 or \
                        len(member_names) != len(set(member_names)):
                    exception.ExceptionCollector.appendException(
                        exception.InvalidGroupTargetException(
                            message=_('Member nodes "%s" should be >= 1 '
                                      'and not repeated') % member_names))
                else:
                    member_nodes = self._get_group_members(member_names)
            group = Group(group_name, group_tpl,
                          member_nodes,
                          self.custom_defs)
            groups.append(group)
        return groups

    def _get_group_members(self, member_names):
        member_nodes = []
        self._validate_group_members(member_names)
        for member in member_names:
            for node in self.nodetemplates:
                if node.name == member:
                    member_nodes.append(node)
        return member_nodes

    def _get_policy_groups(self, member_names):
        member_groups = []
        for member in member_names:
            for group in self.groups:
                if group.name == member:
                    member_groups.append(group)
        return member_groups

    def _validate_group_members(self, members):
        node_names = []
        for node in self.nodetemplates:
            node_names.append(node.name)
        for member in members:
            if member not in node_names:
                exception.ExceptionCollector.appendException(
                    exception.InvalidGroupTargetException(
                        message=_('Target member "%s" is not found in '
                                  'node_templates') % member))

    # topology template can act like node template
    # it is exposed by substitution_mappings.
    def nodetype(self):
        return self.substitution_mappings.node_type \
            if self.substitution_mappings else None

    def capabilities(self):
        return self.substitution_mappings.capabilities \
            if self.substitution_mappings else None

    def requirements(self):
        return self.substitution_mappings.requirements \
            if self.substitution_mappings else None

    def _tpl_description(self):
        description = self.tpl.get(DESCRIPTION)
        if description:
            return description.rstrip()

    def _tpl_inputs(self):
        inputs = self.tpl.get(INPUTS) or {}
        if not isinstance(inputs, dict):
            exception.ExceptionCollector.appendException(
                exception.TypeMismatchError(what=INPUTS, type="dict"))
        return inputs

    def _tpl_nodetemplates(self):
        nodetemplates = self.tpl.get(NODE_TEMPLATES)
        if nodetemplates and not isinstance(nodetemplates, dict):
            exception.ExceptionCollector.appendException(
                exception.TypeMismatchError(what=NODE_TEMPLATES, type="dict"))
        return nodetemplates

    def _tpl_relationship_templates(self):
        relationship_templates = self.tpl.get(RELATIONSHIP_TEMPLATES) or {}
        if not isinstance(relationship_templates, dict):
            exception.ExceptionCollector.appendException(
                exception.TypeMismatchError(what=RELATIONSHIP_TEMPLATES,
                                            type="dict"))
        return relationship_templates

    def _tpl_outputs(self):
        outputs = self.tpl.get(OUTPUTS) or {}
        if not isinstance(outputs, dict):
            exception.ExceptionCollector.appendException(
                exception.TypeMismatchError(what=OUTPUTS, type="dict"))
        return outputs

    def _tpl_substitution_mappings(self):
        substitution_mappings = self.tpl.get(SUBSTITUION_MAPPINGS) or {}
        if not isinstance(substitution_mappings, dict):
            exception.ExceptionCollector.appendException(
                exception.TypeMismatchError(what=SUBSTITUION_MAPPINGS,
                                            type="dict"))
        return substitution_mappings

    def _tpl_groups(self):
        groups = self.tpl.get(GROUPS) or {}
        if not isinstance(groups, dict):
            exception.ExceptionCollector.appendException(
                exception.TypeMismatchError(what=GROUPS, type="dict"))
        return groups

    def _tpl_policies(self):
        policies = self.tpl.get(POLICIES) or []
        if not isinstance(policies, list):
            exception.ExceptionCollector.appendException(
                exception.TypeMismatchError(what=POLICIES, type="list"))
        return policies

    def _validate_field(self):
        for name in self.tpl:
            if name not in SECTIONS:
                exception.ExceptionCollector.appendException(
                    exception.UnknownFieldError(what='Template', field=name))

    def _process_intrinsic_functions(self):
        """Process intrinsic functions

        Current implementation processes functions within node template
        properties, requirements, interfaces inputs and template outputs.
        """
        if hasattr(self, 'nodetemplates'):
            for node_template in self.nodetemplates:
                for prop in node_template.get_properties_objects():
                    prop.value = functions.get_function(self,
                                                        node_template,
                                                        prop.value)
                for interface in node_template.interfaces:
                    if interface.inputs:
                        for name, value in interface.inputs.items():
                            interface.inputs[name] = functions.get_function(
                                self,
                                node_template,
                                value)
                if node_template.requirements and \
                   isinstance(node_template.requirements, list):
                    for req in node_template.requirements:
                        rel = req
                        for req_name, req_item in req.items():
                            if isinstance(req_item, dict):
                                rel = req_item.get('relationship')
                                break
                        if rel and 'properties' in rel:
                            for key, value in rel['properties'].items():
                                rel['properties'][key] = \
                                    functions.get_function(self,
                                                           req,
                                                           value)
                if node_template.get_capabilities_objects():
                    for cap in node_template.get_capabilities_objects():
                        if cap.get_properties_objects():
                            for prop in cap.get_properties_objects():
                                propvalue = functions.get_function(
                                    self,
                                    node_template,
                                    prop.value)
                                if isinstance(propvalue, functions.GetInput):
                                    propvalue = propvalue.result()
                                    for p, v in cap._properties.items():
                                        if p == prop.name:
                                            cap._properties[p] = propvalue
                for rel, node in node_template.relationships.items():
                    rel_tpls = node.relationship_tpl
                    if rel_tpls:
                        for rel_tpl in rel_tpls:
                            for interface in rel_tpl.interfaces:
                                if interface.inputs:
                                    for name, value in \
                                            interface.inputs.items():
                                        interface.inputs[name] = \
                                            functions.get_function(self,
                                                                   rel_tpl,
                                                                   value)
        for output in self.outputs:
            func = functions.get_function(self, self.outputs, output.value)
            if isinstance(func, functions.GetAttribute):
                output.attrs[output.VALUE] = func

    @classmethod
    def get_sub_mapping_node_type(cls, topology_tpl):
        if topology_tpl and isinstance(topology_tpl, dict):
            submap_tpl = topology_tpl.get(SUBSTITUION_MAPPINGS)
            return SubstitutionMappings.get_node_type(submap_tpl) class ToscaTemplate(object):
    exttools = ExtTools()

    MAIN_TEMPLATE_VERSIONS = ['tosca_simple_yaml_1_0',
                              'tosca_simple_yaml_1_2']

    VALID_TEMPLATE_VERSIONS = MAIN_TEMPLATE_VERSIONS + exttools.get_versions()

    ADDITIONAL_SECTIONS = {'tosca_simple_yaml_1_0': SPECIAL_SECTIONS,
                           'tosca_simple_yaml_1_2': 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):

        ExceptionCollector.start()
        self.a_file = a_file
        self.input_path = None
        self.path = None
        self.tpl = None
        self.nested_tosca_tpls_with_topology = {}
        self.nested_tosca_templates_with_topology = []
        if path:
            self.input_path = path
            self.path = self._get_path(path)
            if self.path:
                self.tpl = YAML_LOADER(self.path, self.a_file)
            if yaml_dict_tpl:
                msg = (_('Both path and yaml_dict_tpl arguments were '
                         'provided. Using path and ignoring yaml_dict_tpl.'))
                log.info(msg)
                print(msg)
        else:
            if yaml_dict_tpl:
                self.tpl = yaml_dict_tpl
            else:
                ExceptionCollector.appendException(
                    ValueError(_('No path or yaml_dict_tpl was provided. '
                                 'There is nothing to parse.')))

        if self.tpl:
            self.parsed_params = parsed_params
            self._validate_field()
            self.version = self._tpl_version()
            self.relationship_types = self._tpl_relationship_types()
            self.description = self._tpl_description()
            self.topology_template = self._topology_template()
            self.repositories = self._tpl_repositories()
            if self.topology_template.tpl:
                self.inputs = self._inputs()
                self.relationship_templates = self._relationship_templates()
                self.nodetemplates = self._nodetemplates()
                self.outputs = self._outputs()
                self.policies = self._policies()
                self._handle_nested_tosca_templates_with_topology()
                self.graph = ToscaGraph(self.nodetemplates)

        ExceptionCollector.stop()
        self.verify_template()

    def _topology_template(self):
        return TopologyTemplate(self._tpl_topology_template(),
                                self._get_all_custom_defs(),
                                self.relationship_types,
                                self.parsed_params,
                                None)

    def _inputs(self):
        return self.topology_template.inputs

    def _nodetemplates(self):
        return self.topology_template.nodetemplates

    def _relationship_templates(self):
        return self.topology_template.relationship_templates

    def _outputs(self):
        return self.topology_template.outputs

    def _tpl_version(self):
        return self.tpl.get(DEFINITION_VERSION)

    def _tpl_description(self):
        desc = self.tpl.get(DESCRIPTION)
        if desc:
            return desc.rstrip()

    def _tpl_imports(self):
        return self.tpl.get(IMPORTS)

    def _tpl_repositories(self):
        repositories = self.tpl.get(REPOSITORIES)
        reposit = []
        if repositories:
            for name, val in repositories.items():
                reposits = Repository(name, val)
                reposit.append(reposits)
        return reposit

    def _tpl_relationship_types(self):
        custom_rel, _ = self._get_custom_types(RELATIONSHIP_TYPES)
        return custom_rel

    def _tpl_relationship_templates(self):
        topology_template = self._tpl_topology_template()
        return topology_template.get(RELATIONSHIP_TEMPLATES)

    def _tpl_topology_template(self):
        return self.tpl.get(TOPOLOGY_TEMPLATE)

    def _policies(self):
        return self.topology_template.policies

    def _get_all_custom_defs(self, imports=None, path=None):
        types = [IMPORTS, NODE_TYPES, CAPABILITY_TYPES, RELATIONSHIP_TYPES,
                 DATA_TYPES, INTERFACE_TYPES, POLICY_TYPES, GROUP_TYPES]
        custom_defs_final = {}

        custom_defs, nested_imports = self._get_custom_types(
            types, imports, path)
        if custom_defs:
            custom_defs_final.update(custom_defs)
            if nested_imports:
                for a_file, nested_import in nested_imports.items():
                    import_defs = self._get_all_custom_defs(
                        nested_import, a_file)
                    custom_defs_final.update(import_defs)

        # As imports are not custom_types, removing from the dict
        custom_defs_final.pop(IMPORTS, None)
        return custom_defs_final

    def _get_custom_types(self, type_definitions, imports=None,
                          path=None):
        """Handle custom types defined in imported template files

        This method loads the custom type definitions referenced in "imports"
        section of the TOSCA YAML template.
        """

        custom_defs = {}
        nested_imports = None
        type_defs = []
        if not isinstance(type_definitions, list):
            type_defs.append(type_definitions)
        else:
            type_defs = type_definitions

        if not imports:
            imports = self._tpl_imports()
        if not path:
            path = self.path

        if imports:
            custom_service = toscaparser.imports.\
                ImportsLoader(imports, path, type_defs, self.tpl)

            nested_tosca_tpls = custom_service.get_nested_tosca_tpls()
            self._update_nested_tosca_tpls_with_topology(nested_tosca_tpls)

            nested_imports = custom_service.get_nested_imports()
            custom_defs = custom_service.get_custom_defs()
            if not custom_defs:
                return None, None

        # Handle custom types defined in current template file
        for type_def in type_defs:
            if type_def != IMPORTS:
                inner_custom_types = self.tpl.get(type_def) or {}
                if inner_custom_types:
                    custom_defs.update(inner_custom_types)
        return custom_defs, nested_imports

    def _update_nested_tosca_tpls_with_topology(self, nested_tosca_tpls):
        for tpl in nested_tosca_tpls:
            filename, tosca_tpl = list(tpl.items())[0]
            if (tosca_tpl.get(TOPOLOGY_TEMPLATE) and
                filename not in list(
                    self.nested_tosca_tpls_with_topology.keys())):
                self.nested_tosca_tpls_with_topology.update(tpl)

    def _handle_nested_tosca_templates_with_topology(self):
        for fname, tosca_tpl in self.nested_tosca_tpls_with_topology.items():
            for nodetemplate in self.nodetemplates:
                if self._is_sub_mapped_node(nodetemplate, tosca_tpl):
                    parsed_params = self._get_params_for_nested_template(
                        nodetemplate)
                    topology_tpl = tosca_tpl.get(TOPOLOGY_TEMPLATE)
                    topology_with_sub_mapping = TopologyTemplate(
                        topology_tpl,
                        self._get_all_custom_defs(),
                        self.relationship_types,
                        parsed_params,
                        nodetemplate)
                    if topology_with_sub_mapping.substitution_mappings:
                        # Record nested topo templates in top level template
                        self.nested_tosca_templates_with_topology.\
                            append(topology_with_sub_mapping)
                        # Set substitution mapping object for mapped node
                        nodetemplate.sub_mapping_tosca_template = \
                            topology_with_sub_mapping.substitution_mappings

    def _validate_field(self):
        version = self._tpl_version()
        if not version:
            ExceptionCollector.appendException(
                MissingRequiredFieldError(what='Template',
                                          required=DEFINITION_VERSION))
        else:
            self._validate_version(version)
            self.version = version

        for name in self.tpl:
            if (name not in SECTIONS and
               name not in self.ADDITIONAL_SECTIONS.get(version, ())):
                ExceptionCollector.appendException(
                    UnknownFieldError(what='Template', field=name))

    def _validate_version(self, version):
        if version not in self.VALID_TEMPLATE_VERSIONS:
            ExceptionCollector.appendException(
                InvalidTemplateVersion(
                    what=version,
                    valid_versions='", "'. join(self.VALID_TEMPLATE_VERSIONS)))
        else:
            if version not in self.MAIN_TEMPLATE_VERSIONS:
                update_definitions(version)

    def _get_path(self, path):
        if path.lower().endswith('.yaml') or path.lower().endswith('.yml'):
            return path
        elif path.lower().endswith(('.zip', '.csar')):
            # a CSAR archive
            csar = CSAR(path, self.a_file)
            if csar.validate():
                csar.decompress()
                self.a_file = True  # the file has been decompressed locally
                return os.path.join(csar.temp_dir, csar.get_main_template())
        else:
            ExceptionCollector.appendException(
                ValueError(_('"%(path)s" is not a valid file.')
                           % {'path': path}))

    def verify_template(self):
        if ExceptionCollector.exceptionsCaught():
            if self.input_path:
                raise 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(
                    message=_('\nThe pre-parsed input failed validation with '
                              'the following error(s): \n\n\t') +
                    '\n\t'.join(ExceptionCollector.getExceptionsReport()))
        else:
            if self.input_path:
                msg = (_('The input "%(path)s" successfully passed '
                         'validation.') % {'path': self.input_path})
            else:
                msg = _('The pre-parsed input successfully passed validation.')

            log.info(msg)

    def _is_sub_mapped_node(self, nodetemplate, tosca_tpl):
        """Return True if the nodetemple is substituted."""
        # NOTE(ueha): Since condition "not nodetemplate.sub_mapping_tosca_\
        #             template" was deleted as a fix for bug/1883220, there is
        #             some possibility of breaking something on translator side
        #             that current tests not coverd.
        #             And this enhancement does not align with TOSCA standard
        #             but needed for ETSI NFV-SOL 001.
        if (nodetemplate and
                self.get_sub_mapping_node_type(tosca_tpl) == nodetemplate.type
                and len(nodetemplate.interfaces) < 1):
            return True
        else:
            return False

    def _get_params_for_nested_template(self, nodetemplate):
        """Return total params for nested_template."""
        parsed_params = deepcopy(self.parsed_params) \
            if self.parsed_params else {}
        if nodetemplate:
            for pname in nodetemplate.get_properties():
                parsed_params.update({pname:
                                      nodetemplate.get_property_value(pname)})
        return parsed_params

    def get_sub_mapping_node_type(self, tosca_tpl):
        """Return substitution mappings node type."""
        if tosca_tpl:
            return TopologyTemplate.get_sub_mapping_node_type(
                tosca_tpl.get(TOPOLOGY_TEMPLATE))

    def _has_substitution_mappings(self):
        """Return True if the template has valid substitution mappings."""
        return self.topology_template is not None and \
            self.topology_template.substitution_mappings is not None

    def has_nested_templates(self):
        """Return True if the tosca template has nested templates."""
        return self.nested_tosca_templates_with_topology is not None and \
            len(self.nested_tosca_templates_with_topology) >= 1 class Triggers(EntityTemplate):

    '''Triggers defined in policies of topology template'''

    def __init__(self, name, trigger_tpl):
        self.name = name
        self.trigger_tpl = trigger_tpl
        self._validate_keys()
        self._validate_condition()
        self._validate_input()

    def get_description(self):
        return self.trigger_tpl['description']

    def get_event(self):
        return self.trigger_tpl['event_type']

    def get_schedule(self):
        return self.trigger_tpl['schedule']

    def get_target_filter(self):
        return self.trigger_tpl['target_filter']

    def get_condition(self):
        return self.trigger_tpl['condition']

    def get_action(self):
        return self.trigger_tpl['action']

    def _validate_keys(self):
        for key in self.trigger_tpl.keys():
            if key not in SECTIONS:
                ExceptionCollector.appendException(
                    UnknownFieldError(what='Triggers "%s"' % self.name,
                                      field=key))

    def _validate_condition(self):
        for key in self.get_condition():
            if key not in CONDITION_KEYNAMES:
                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 [GRANULARITY, EVALUATIONS]:
                validateutils.validate_integer(value)
            elif key == THRESHOLD:
                validateutils.validate_numeric(value)
            elif key in [METRIC, AGGREGATION_METHOD]:
                validateutils.validate_string(value) def _(msg):
    # type: (object) -> object
    return _t.gettext(msg)
import os
import toscaparser.utils.yamlparser
from toscaparser.common import exception
from toscaparser.imports import ImportsLoader
from toscaparser.nodetemplate import NodeTemplate
from toscaparser.parameters import Input
from toscaparser.parameters import Output
from toscaparser.policy import Policy
from toscaparser.relationship_template import RelationshipTemplate
from toscaparser.repositories import Repository
from toscaparser.reservation import Reservation
from toscaparser.tests.base import TestCase
from toscaparser.topology_template import TopologyTemplate
from toscaparser.tosca_template import ToscaTemplate
from toscaparser.triggers import Triggers
from toscaparser.utils.gettextutils import _
        self._single_node_template_content_test(tpl_snippet_metadata_inline)

    def test_policy_valid_keynames(self):
        tpl_snippet = '''
        policies:
          - servers_placement:
              type: tosca.policies.Placement
              description: Apply placement policy to servers
              metadata: { user1: 1001, user2: 1002 }
              targets: [ serv1, serv2 ]
        '''
        policies = (toscaparser.utils.yamlparser.
                    simple_parse(tpl_snippet))['policies'][0]
        name = list(policies.keys())[0]
        Policy(name, policies[name], None, None)

    def test_policy_invalid_keyname(self):
        tpl_snippet = '''
        policies:
          - servers_placement:
              type: tosca.policies.Placement
              testkey: testvalue
        '''
        policies = (toscaparser.utils.yamlparser.
                    simple_parse(tpl_snippet))['policies'][0]
        name = list(policies.keys())[0]

        expectedmessage = _('Policy "servers_placement" contains '
                            'unknown field "testkey". Refer to the '
                            'definition to verify valid values.')
        err = self.assertRaises(
            exception.UnknownFieldError,
            lambda: Policy(name, policies[name], None, None))
        self.assertEqual(expectedmessage, err.__str__())

    def test_policy_trigger_valid_keyname_senlin_resources(self):
        tpl_snippet = '''
        triggers:
         - resize_compute:
             description: trigger
             event_type: tosca.events.resource.utilization
             schedule:
               start_time: "2015-05-07T07:00:00Z"
               end_time: "2015-06-07T07:00:00Z"
             target_filter:
               node: master-container
               requirement: host
               capability: Container
             condition:
               constraint: { greater_than: 50 }
               granularity: 60
               evaluations: 1
               aggregation_method : mean
             action:
               resize: # Operation name
                inputs:
                 strategy: LEAST_USED
                 implementation: Senlin.webhook()
        '''
        triggers = (toscaparser.utils.yamlparser.
                    simple_parse(tpl_snippet))['triggers'][0]
        name = list(triggers.keys())[0]
        Triggers(name, triggers[name])

    def test_policy_trigger_valid_keyname_heat_resources(self):
        tpl_snippet = '''
        triggers:
         - high_cpu_usage:
             description: trigger
             metric: cpu_util
             condition:
               constraint: utilization greater_than 60%
               threshold: 60
               granularity: 600
               evaluations: 1
               aggregation_method: mean
               resource_type: instance
               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:
             description: trigger
             event_type: tosca.events.resource.utilization
             schedule:
               start_time: "2015-05-07T07:00:00Z"
               end_time: "2015-06-07T07:00:00Z"
             target_filter1:
               node: master-container
               requirement: host
               capability: Container
             condition:
               constraint: utilization greater_than 50%
               granularity1: 60
               evaluations: 1
               aggregation_method: mean
               resource_type: instance
             action:
               resize: # Operation name
                inputs:
                 strategy: LEAST_USED
                 implementation: Senlin.webhook()
        '''
        triggers = (toscaparser.utils.yamlparser.
                    simple_parse(tpl_snippet))['triggers'][0]
        name = list(triggers.keys())[0]
        expectedmessage = _(
            'Triggers "resize_compute" contains unknown field '
            '"target_filter1". Refer to the definition '
            'to verify valid values.')
        err = self.assertRaises(
            exception.UnknownFieldError,

            lambda: Triggers(name, triggers[name]))