Example #1
0
    def __init__(self, entry_schema, constraints, value, aspect):  # pylint: disable=unused-argument
        str_value = str(value)
        match = re.match(self.REGEX, str_value)  # pylint: disable=no-member
        if match is None:
            raise ValueError(
                'scalar must be formatted as <scalar> <unit>: %s' %
                safe_repr(value))

        self.factor = float(match.group('scalar'))
        if self.factor < 0:
            raise ValueError('scalar is negative: %s' % safe_repr(self.factor))

        self.unit = match.group('unit')

        unit_lower = self.unit.lower()
        unit_size = None
        for k, v in self.UNITS.iteritems():  # pylint: disable=no-member
            if k.lower() == unit_lower:
                self.unit = k
                unit_size = v
                break
        if unit_size is None:
            raise ValueError('scalar specified with unsupported unit: %s' %
                             safe_repr(self.unit))

        self.value = self.TYPE(self.factor * unit_size)  # pylint: disable=no-member
Example #2
0
 def report(message, constraint):
     context.validation.report(
         'value %s %s per constraint in "%s": %s' %
         (message, safe_repr(constraint), presentation._name
          or presentation._container._name, safe_repr(value)),
         locator=presentation._locator,
         level=Issue.BETWEEN_FIELDS)
def capability_definition_or_type_validator(field, presentation, context):
    """
    Makes sure refers to either a capability assignment name in the node template referred to by the
    ``node`` field or a general capability type.

    Used with the :func:`field_validator` decorator for the ``capability`` field in
    :class:`RequirementAssignment`.
    """

    field.default_validate(presentation, context)

    value = getattr(presentation, field.name)
    if value is not None:
        node, node_variant = presentation._get_node(context)
        capability_variant = presentation._get_capability(context)[1]

        if capability_variant == 'capability_assignment':
            capability_definition_validator(field, presentation, context,
                                            value, node, node_variant)
        elif capability_variant == 'capability_type':
            capability_type_validator(field, presentation, context, value,
                                      node, node_variant)
        else:
            context.validation.report(
                'requirement "%s" refers to an unknown capability definition name or '\
                'type in "%s": %s'
                % (presentation._name, presentation._container._fullname, safe_repr(value)),
                locator=presentation._get_child_locator(field.name), level=Issue.BETWEEN_TYPES)
def parse_modelable_entity_name(context, presentation, name, index, value):
    value = parse_string_expression(context, presentation, name, index, 'the modelable entity name',
                                    value)
    if value == 'SELF':
        the_self, _ = parse_self(presentation)
        if the_self is None:
            raise invalid_modelable_entity_name(name, index, value, presentation._locator,
                                                'a node template or a relationship template')
    elif value == 'HOST':
        _, self_variant = parse_self(presentation)
        if self_variant != 'node_template':
            raise invalid_modelable_entity_name(name, index, value, presentation._locator,
                                                'a node template')
    elif (value == 'SOURCE') or (value == 'TARGET'):
        _, self_variant = parse_self(presentation)
        if self_variant != 'relationship_template':
            raise invalid_modelable_entity_name(name, index, value, presentation._locator,
                                                'a relationship template')
    elif isinstance(value, basestring):
        node_templates = \
            context.presentation.get('service_template', 'topology_template', 'node_templates') \
            or {}
        relationship_templates = \
            context.presentation.get('service_template', 'topology_template',
                                     'relationship_templates') \
            or {}
        if (value not in node_templates) and (value not in relationship_templates):
            raise InvalidValueError(
                'function "{0}" parameter {1:d} is not a valid modelable entity name: {2}'
                .format(name, index + 1, safe_repr(value)),
                locator=presentation._locator, level=Issue.BETWEEN_TYPES)
    return value
Example #5
0
def constraint_clause_pattern_validator(field, presentation, context):
    """
    Makes sure that the value is a valid regular expression.

    Used with the :func:`field_validator` decorator for the ``pattern`` field in
    :class:`ConstraintClause`.
    """

    field.default_validate(presentation, context)

    value = getattr(presentation, field.name)
    if value is not None:
        try:
            # From TOSCA 1.0 3.5.2.1:
            #
            # "Note: Future drafts of this specification will detail the use of regular expressions
            # and reference an appropriate standardized grammar."
            #
            # So we will just use Python's.
            re.compile(value)
        except re.error as e:
            context.validation.report(
                u'constraint "{0}" is not a valid regular expression in "{1}": {2}'
                .format(field.name, presentation._fullname, safe_repr(value)),
                locator=presentation._get_child_locator(field.name),
                level=Issue.FIELD,
                exception=e)
Example #6
0
    def validator(field, presentation, context):
        field.default_validate(presentation, context)

        value = getattr(presentation, field.name)
        if value is not None:
            # Test for circular definitions
            container_data_type = get_container_data_type(presentation)
            if (container_data_type is not None) and (container_data_type._name
                                                      == value):
                context.validation.report(
                    'type of property "%s" creates a circular value hierarchy: %s'
                    % (presentation._fullname, safe_repr(value)),
                    locator=presentation._get_child_locator('type'),
                    level=Issue.BETWEEN_TYPES)

            # Can be a complex data type
            if get_type_by_name(context, value, 'data_types') is not None:
                return True

            # Can be a primitive data type
            if get_primitive_data_type(value) is None:
                report_issue_for_unknown_type(context, presentation, type_name,
                                              field.name)

        return False
Example #7
0
def _report_invalid_mapping_format(context, presentation, field):
    context.validation.report(
        u'substitution mapping {field} "{field_name}" is not a list of 2 strings: {value}'.format(
            field=field,
            field_name=presentation._name,
            value=safe_repr(presentation._raw)),
        locator=presentation._locator, level=Issue.FIELD)
Example #8
0
    def __init__(self, context, presentation, argument):
        self.locator = presentation._locator

        if (not isinstance(
                argument, list)) or (len(argument) < 2) or (len(argument) > 4):
            raise InvalidValueError(
                u'function "get_artifact" argument must be a list of 2 to 4 parameters: {0}'
                .format(safe_repr(argument)),
                locator=self.locator)

        self.modelable_entity_name = parse_string_expression(
            context, presentation, 'get_artifact', 0, 'modelable entity name',
            argument[0])
        self.artifact_name = parse_string_expression(context, presentation,
                                                     'get_artifact', 1,
                                                     'the artifact name',
                                                     argument[1])
        if len(argument) > 2:
            self.location = parse_string_expression(
                context, presentation, 'get_artifact', 2,
                'the location or "LOCAL_FILE"', argument[2])
        else:
            self.location = None
        if len(argument) > 3:
            self.remove = parse_bool(context, presentation, 'get_artifact', 3,
                                     'the removal flag', argument[3])
        else:
            self.remove = None
Example #9
0
def capability_definition_or_type_validator(field, presentation, context):
    """
    Makes sure refers to either a capability assignment name in the node template referred to by the
    :code:`node` field or a general capability type.

    If the value refers to a capability type, make sure the :code:`node` field was not assigned.

    Used with the :func:`field_validator` decorator for the :code:`capability` field in
    :class:`RequirementAssignment`.
    """

    field.default_validate(presentation, context)

    value = getattr(presentation, field.name)
    if value is not None:
        node, node_variant = presentation._get_node(context)
        if node_variant == 'node_template':
            capabilities = node._get_capabilities(context)
            if value in capabilities:
                return

        capability_types = context.presentation.get('service_template',
                                                    'capability_types')
        if (capability_types is not None) and (value in capability_types):
            if node is not None:
                context.validation.report(
                    '"%s" refers to a capability type even though "node" has a value in "%s"'
                    % (presentation._name, presentation._container._fullname),
                    locator=presentation._get_child_locator(field.name),
                    level=Issue.BETWEEN_FIELDS)
            return

        if node_variant == 'node_template':
            context.validation.report(
                'requirement "%s" refers to an unknown capability definition name or capability'
                ' type in "%s": %s' %
                (presentation._name, presentation._container._fullname,
                 safe_repr(value)),
                locator=presentation._get_child_locator(field.name),
                level=Issue.BETWEEN_TYPES)
        else:
            context.validation.report(
                'requirement "%s" refers to an unknown capability type in "%s": %s'
                % (presentation._name, presentation._container._fullname,
                   safe_repr(value)),
                locator=presentation._get_child_locator(field.name),
                level=Issue.BETWEEN_TYPES)
Example #10
0
def validate_substitution_mappings_requirement(context, presentation):
    # Validate that the requirement in substitution_mapping is defined in the substitution node type
    print "Requiremnt presetation -->", presentation.__dict__
    print "*******Requirement container -->", presentation._container.__dict__
    substitution_node_type = presentation._container._get_type(context)
    print ("Substitution mapping reqruiement node type -------------------> ", substitution_node_type)
    print ("Substitution mapping node requirement type dict-------------------> ", substitution_node_type.__dict__)
    print ("Substitution mapping node requirement type container -------------------> ", substitution_node_type.__dict__['_container'])
    if substitution_node_type is None:
        return
    for req_name, req in substitution_node_type._get_requirements(context):
        if req_name == presentation._name:
            substitution_type_requirement = req
            break
    else:
        context.validation.report(
            u'substitution mapping requirement "{0}" is not declared in node type "{1}"'.format(
                presentation._name, substitution_node_type._name),
            locator=presentation._locator, level=Issue.BETWEEN_TYPES)
        return

    if not _validate_mapping_format(presentation):
        _report_invalid_mapping_format(context, presentation, field='requirement')
        return

    # Validate that the mapped requirement is defined in the corresponding node template
    node_template = _get_node_template(context, presentation)
    if node_template is None:
        _report_missing_node_template(context, presentation, field='requirement')
        return
    mapped_requirement_name = presentation._raw[1]
    for req_name, req in node_template._get_requirements(context):
        if req_name == mapped_requirement_name:
            node_template_requirement = req
            break
    else:
        context.validation.report(
            u'substitution mapping requirement "{0}" refers to an unknown requirement of node '
            u'template "{1}": {mapped_requirement_name}'.format(
                presentation._name, node_template._name,
                mapped_requirement_name=safe_repr(mapped_requirement_name)),
            locator=presentation._locator, level=Issue.BETWEEN_TYPES)
        return

    # Validate that the requirement's capability type in substitution_mapping is derived from the
    # requirement's capability type in the corresponding node template
    substitution_type_requirement_capability_type = \
        substitution_type_requirement._get_capability_type(context)
    node_template_requirement_capability_type = \
        node_template_requirement._get_capability(context)[0]
    if not substitution_type_requirement_capability_type._is_descendant(
            context, node_template_requirement_capability_type):
        context.validation.report(
            u'substitution mapping requirement "{0}" of capability type "{1}" is not a descendant '
            u'of the mapped node template capability type "{2}"'.format(
                presentation._name,
                substitution_type_requirement_capability_type._name,
                node_template_requirement_capability_type._name),
            locator=presentation._locator, level=Issue.BETWEEN_TYPES)
Example #11
0
def _report_missing_node_template(context, presentation, field):
    context.validation.report(
        u'substitution mappings {field} "{node_template_mapping}" '
        u'refers to an unknown node template: {node_template_name}'.format(
            field=field,
            node_template_mapping=presentation._name,
            node_template_name=safe_repr(presentation._raw[0])),
        locator=presentation._locator, level=Issue.FIELD)
def invalid_value(name, index, the_type, explanation, value, locator):
    return InvalidValueError('function "{0}" {1} is not {2}{3}: {4}'.format(
        name, 'parameter {0:d}'.format(index +
                                       1) if index is not None else 'argument',
        the_type,
        ', {0}'.format(explanation) if explanation is not None else '',
        safe_repr(value)),
                             locator=locator,
                             level=Issue.FIELD)
Example #13
0
def invalid_value(name, index, the_type, explanation, value, locator):
    return InvalidValueError(
        'function "%s" %s is not %s%s: %s' %
        (name, ('parameter %d' %
                (index + 1)) if index is not None else 'argument', the_type,
         (', %s' % explanation) if explanation is not None else '',
         safe_repr(value)),
        locator=locator,
        level=Issue.FIELD)
Example #14
0
def report_issue_for_bad_format(context, presentation, the_type, value, aspect, e):
    if aspect == 'default':
        aspect = '"default" value'
    elif aspect is not None:
        aspect = '"%s" aspect' % aspect

    if aspect is not None:
        context.validation.report('%s for field "%s" is not a valid "%s": %s'
                                  % (aspect, presentation._name or presentation._container._name,
                                     get_data_type_name(the_type), safe_repr(value)),
                                  locator=presentation._locator, level=Issue.BETWEEN_FIELDS,
                                  exception=e)
    else:
        context.validation.report('field "%s" is not a valid "%s": %s'
                                  % (presentation._name or presentation._container._name,
                                     get_data_type_name(the_type), safe_repr(value)),
                                  locator=presentation._locator, level=Issue.BETWEEN_FIELDS,
                                  exception=e)
Example #15
0
def constraint_clause_in_range_validator(field, presentation, context):
    """
    Makes sure that the value is a list with exactly two elements, that both lower bound contains a
    valid value for the container type, and that the upper bound is either "UNBOUNDED" or a valid
    value for the container type.

    Used with the :func:`field_validator` decorator for the ``in_range`` field in
    :class:`ConstraintClause`.
    """

    field.default_validate(presentation, context)

    values = getattr(presentation, field.name)
    if isinstance(values, list):
        # Make sure list has exactly two elements
        if len(values) == 2:
            lower, upper = values
            the_type = presentation._get_type(context)

            # Lower bound must be coercible
            lower = coerce_value(context, presentation, the_type, None, None,
                                 lower, field.name)

            if upper != 'UNBOUNDED':
                # Upper bound be coercible
                upper = coerce_value(context, presentation, the_type, None,
                                     None, upper, field.name)

                # Second "in_range" value must be greater than first
                if (lower is not None) and (upper
                                            is not None) and (lower >= upper):
                    context.validation.report(
                        'upper bound of "in_range" constraint is not greater than the lower bound'
                        ' in "%s": %s <= %s' %
                        (presentation._container._fullname, safe_repr(lower),
                         safe_repr(upper)),
                        locator=presentation._locator,
                        level=Issue.FIELD)
        else:
            context.validation.report(
                'constraint "%s" is not a list of exactly 2 elements in "%s"' %
                (field.name, presentation._fullname),
                locator=presentation._get_child_locator(field.name),
                level=Issue.FIELD)
Example #16
0
def report_issue_for_bad_format(context, presentation, the_type, value, aspect, e):
    if aspect == 'default':
        aspect = '"default" value'
    elif aspect is not None:
        aspect = u'"{0}" aspect'.format(aspect)

    if aspect is not None:
        context.validation.report(u'{0} for field "{1}" is not a valid "{2}": {3}'
                                  .format(aspect,
                                          presentation._name or presentation._container._name,
                                          get_data_type_name(the_type), safe_repr(value)),
                                  locator=presentation._locator, level=Issue.BETWEEN_FIELDS,
                                  exception=e)
    else:
        context.validation.report(u'field "{0}" is not a valid "{1}": {2}'
                                  .format(presentation._name or presentation._container._name,
                                          get_data_type_name(the_type), safe_repr(value)),
                                  locator=presentation._locator, level=Issue.BETWEEN_FIELDS,
                                  exception=e)
Example #17
0
 def _validate(self, context):
     super(CapabilityDefinition, self)._validate(context)
     occurrences = self.occurrences
     if (occurrences is not None) and ((occurrences.value[0] < 0) or \
         ((occurrences.value[1] != 'UNBOUNDED') and (occurrences.value[1] < 0))):
         context.validation.report(
             'capability definition "{0}" occurrences range includes negative integers: {1}'
             .format(self._name, safe_repr(occurrences)),
             locator=self._locator,
             level=Issue.BETWEEN_TYPES)
Example #18
0
def validate_data_type_name(context, presentation):
    """
    Makes sure the complex data type's name is not that of a built-in type.
    """

    name = presentation._name
    if get_primitive_data_type(name) is not None:
        context.validation.report('data type name is that of a built-in type: %s'
                                  % safe_repr(name),
                                  locator=presentation._locator, level=Issue.BETWEEN_TYPES)
Example #19
0
def validate_format(context, presentation, name):
    if (not isinstance(presentation._raw, list)) or (len(presentation._raw) != 2) \
        or (not isinstance(presentation._raw[0], basestring)) \
        or (not isinstance(presentation._raw[1], basestring)):
        context.validation.report(
            'substitution mappings %s "%s" is not a list of 2 strings: %s' %
            (name, presentation._name, safe_repr(presentation._raw)),
            locator=presentation._locator,
            level=Issue.FIELD)
        return False
    return True
Example #20
0
def policy_targets_validator(field, presentation, context):
    """
    Makes sure that the field's elements refer to either node templates or groups, and that
    they match the node types and group types declared in the policy type.

    Used with the :func:`field_validator` decorator for the ``targets`` field in
    :class:`PolicyTemplate`.
    """

    field.default_validate(presentation, context)

    values = getattr(presentation, field.name)
    if values is not None:
        for value in values:
            node_templates = \
                context.presentation.get('service_template', 'topology_template',
                                         'node_templates') \
                or {}
            groups = context.presentation.get('service_template', 'topology_template', 'groups') \
                or {}
            if (value not in node_templates) and (value not in groups):
                report_issue_for_unknown_type(context, presentation,
                                              'node template or group',
                                              field.name, value)

            policy_type = presentation._get_type(context)
            if policy_type is None:
                break

            node_types, group_types = policy_type._get_targets(context)

            is_valid = False

            if value in node_templates:
                our_node_type = node_templates[value]._get_type(context)
                for node_type in node_types:
                    if node_type._is_descendant(context, our_node_type):
                        is_valid = True
                        break

            elif value in groups:
                our_group_type = groups[value]._get_type(context)
                for group_type in group_types:
                    if group_type._is_descendant(context, our_group_type):
                        is_valid = True
                        break

            if not is_valid:
                context.validation.report(
                    'policy definition target does not match either a node type or a group type'
                    ' declared in the policy type in "%s": %s' %
                    (presentation._name, safe_repr(value)),
                    locator=presentation._locator,
                    level=Issue.BETWEEN_TYPES)
 def getter(field, presentation, context=None):
     raw = field.default_get(presentation, context)
     if raw is not None:
         try:
             return cls(None, None, raw, None)
         except ValueError as e:
             raise InvalidValueError('%s is not a valid "%s" in "%s": %s' %
                                     (field.full_name, field.full_cls_name,
                                      presentation._name, safe_repr(raw)),
                                     cause=e,
                                     locator=field.get_locator(raw))
Example #22
0
def coerce_value(context,
                 presentation,
                 the_type,
                 entry_schema,
                 constraints,
                 value,
                 aspect=None):  # pylint: disable=too-many-return-statements
    """
    Returns the value after it's coerced to its type, reporting validation errors if it cannot be
    coerced.

    Supports both complex data types and primitives.

    Data types can use the ``coerce_value`` extension to hook their own specialized function.
    If the extension is present, we will delegate to that hook.
    """

    # TODO: should support models as well as presentations

    is_function, func = get_function(context, presentation, value)
    if is_function:
        return func

    if the_type is None:
        return value

    if the_type == None.__class__:
        if value is not None:
            context.validation.report(
                'field "%s" is of type "null" but has a non-null value: %s' %
                (presentation._name, safe_repr(value)),
                locator=presentation._locator,
                level=Issue.BETWEEN_FIELDS)
            return None

    # Delegate to 'coerce_value' extension
    if hasattr(the_type, '_get_extension'):
        coerce_value_fn_name = the_type._get_extension('coerce_value')
        if coerce_value_fn_name is not None:
            if value is None:
                return None
            coerce_value_fn = import_fullname(coerce_value_fn_name)
            return coerce_value_fn(context, presentation, the_type,
                                   entry_schema, constraints, value, aspect)

    if hasattr(the_type, '_coerce_value'):
        # Delegate to '_coerce_value' (likely a DataType instance)
        return the_type._coerce_value(context, presentation, entry_schema,
                                      constraints, value, aspect)

    # Coerce to primitive type
    return coerce_to_primitive(context, presentation, the_type, constraints,
                               value, aspect)
Example #23
0
    def __init__(self, entry_schema, constraints, value, aspect):  # pylint: disable=unused-argument
        if not isinstance(value, list):
            raise ValueError('range value is not a list: %s' %
                             safe_repr(value))
        if len(value) != 2:
            raise ValueError(
                'range value does not have exactly 2 elements: %s' %
                safe_repr(value))

        def is_int(v):
            return isinstance(v, int) and (not isinstance(v, bool)
                                           )  # In Python bool is an int

        if not is_int(value[0]):
            raise ValueError(
                'lower bound of range is not a valid integer: %s' %
                safe_repr(value[0]))

        if value[1] != 'UNBOUNDED':
            if not is_int(value[1]):
                raise ValueError(
                    'upper bound of range is not a valid integer or "UNBOUNDED": %s'
                    % safe_repr(value[0]))

            if value[0] >= value[1]:
                raise ValueError(
                    'upper bound of range is not greater than the lower bound: %s >= %s'
                    % (safe_repr(value[0]), safe_repr(value[1])))

        self.value = value
Example #24
0
def get_node_template(context, presentation, name):
    node_template_name = presentation._raw[0]
    node_template = context.presentation.get_from_dict('service_template',
                                                       'topology_template',
                                                       'node_templates',
                                                       node_template_name)
    if node_template is None:
        context.validation.report(
            'substitution mappings %s "%s" refers to an unknown node template: %s'
            % (name, presentation._name, safe_repr(node_template_name)),
            locator=presentation._locator,
            level=Issue.FIELD)
    return node_template
    def __init__(self, context, presentation, argument):
        self.locator = presentation._locator

        if (not isinstance(argument, list)) or (len(argument) < 2):
            raise InvalidValueError(
                'function "get_attribute" argument must be a list of at least 2 string expressions:'
                ' {0}'.format(safe_repr(argument)),
                locator=self.locator)

        self.modelable_entity_name = parse_modelable_entity_name(
            context, presentation, 'get_attribute', 0, argument[0])
        # The first of these will be tried as a req-or-cap name:
        self.nested_attribute_name_or_index = argument[1:]
Example #26
0
 def getter(field, presentation, context=None):
     raw = field.default_get(presentation, context)
     if (raw is None) or (allow_null and (raw is NULL)):
         return raw
     try:
         return cls(None, None, raw, None)
     except ValueError as e:
         raise InvalidValueError(
             u'{0} is not a valid "{1}" in "{2}": {3}'.format(
                 field.full_name, full_type_name(cls), presentation._name,
                 safe_repr(raw)),
             cause=e,
             locator=field.get_locator(raw))
Example #27
0
    def __init__(self, context, presentation, argument):
        self.locator = presentation._locator

        self.node_type_name = parse_string_expression(context, presentation, 'get_nodes_of_type',
                                                      None, 'the node type name', argument)

        if isinstance(self.node_type_name, basestring):
            node_types = context.presentation.get('service_template', 'node_types')
            if (node_types is None) or (self.node_type_name not in node_types):
                raise InvalidValueError(
                    'function "get_nodes_of_type" argument is not a valid node type name: {0}'
                    .format(safe_repr(argument)),
                    locator=self.locator)
Example #28
0
    def __evaluate__(self, container_holder):
        service = container_holder.service
        if service is None:
            raise CannotEvaluateFunctionException()

        value = service.inputs.get(self.input_property_name)
        if value is not None:
            value = value.value
            return Evaluation(value, False) # We never return final evaluations!

        raise InvalidValueError(
            'function "get_input" argument is not a valid input name: {0}'
            .format(safe_repr(self.input_property_name)),
            locator=self.locator)
Example #29
0
    def __init__(self, context, presentation, argument):
        self.locator = presentation._locator

        if not isinstance(argument, list):
            raise InvalidValueError(
                'function "concat" argument must be a list of string expressions: {0}'
                .format(safe_repr(argument)),
                locator=self.locator)

        string_expressions = []
        for index, an_argument in enumerate(argument):
            string_expressions.append(parse_string_expression(context, presentation, 'concat',
                                                              index, None, an_argument))
        self.string_expressions = FrozenList(string_expressions)
Example #30
0
def validate_substitution_mappings_capability(context, presentation):
    # Validate that the capability in substitution_mapping is defined in the substitution node type
    substitution_node_type = presentation._container._get_type(context)
    if substitution_node_type is None:
        return
    substitution_type_capabilities = substitution_node_type._get_capabilities(context)
    substitution_type_capability = substitution_type_capabilities.get(presentation._name)
    if substitution_type_capability is None:
        context.validation.report(
            u'substitution mapping capability "{0}" '
            u'is not declared in node type "{substitution_type}"'.format(
                presentation._name, substitution_type=substitution_node_type._name),
            locator=presentation._locator, level=Issue.BETWEEN_TYPES)
        return

    if not _validate_mapping_format(presentation):
        _report_invalid_mapping_format(context, presentation, field='capability')
        return

    # Validate that the capability in substitution_mapping is declared in the corresponding
    # node template
    node_template = _get_node_template(context, presentation)
    if node_template is None:
        _report_missing_node_template(context, presentation, field='capability')
        return
    mapped_capability_name = presentation._raw[1]
    node_template_capability = node_template._get_capabilities(context).get(mapped_capability_name)

    if node_template_capability is None:
        context.validation.report(
            u'substitution mapping capability "{0}" refers to an unknown '
            u'capability of node template "{1}": {mapped_capability_name}'.format(
                presentation._name, node_template._name,
                mapped_capability_name=safe_repr(mapped_capability_name)),
            locator=presentation._locator, level=Issue.BETWEEN_TYPES)
        return

    # Validate that the capability type in substitution_mapping is derived from the capability type
    # in the corresponding node template
    substitution_type_capability_type = substitution_type_capability._get_type(context)
    node_template_capability_type = node_template_capability._get_type(context)
    if not substitution_type_capability_type._is_descendant(context, node_template_capability_type):
        context.validation.report(
            u'node template capability type "{0}" is not a descendant of substitution mapping '
            u'capability "{1}" of type "{2}"'.format(
                node_template_capability_type._name,
                presentation._name,
                substitution_type_capability_type._name),
            locator=presentation._locator, level=Issue.BETWEEN_TYPES)