예제 #1
0
def validate_inputs_types(inputs, inputs_schema):
    for input_key, _input in inputs_schema.iteritems():
        input_type = _input.get('type')
        if input_type is None:
            # no type defined - no validation
            continue
        input_val = inputs[input_key]

        if functions.parse(input_val) != input_val:
            # intrinsic function - not validated at the moment
            continue

        if input_type == 'integer':
            if isinstance(input_val, (int, long)) and not \
                    isinstance(input_val, bool):
                continue
        elif input_type == 'float':
            if isinstance(input_val, (int, float, long)) and not \
                    isinstance(input_val, bool):
                continue
        elif input_type == 'boolean':
            if isinstance(input_val, bool):
                continue
        elif input_type == 'string':
            continue
        else:
            raise DSLParsingLogicException(
                80, "Unexpected type defined in inputs schema "
                "for input '{0}' - unknown type is {1}".format(
                    input_key, input_type))

        raise DSLParsingLogicException(
            50, "Input type validation failed: Input '{0}' type "
            "is '{1}', yet it was assigned with the value '{2}'".format(
                input_key, input_type, input_val))
예제 #2
0
def merge_schema_and_instance_properties(
        instance_properties,
        schema_properties,
        undefined_property_error_message,
        missing_property_error_message,
        node_name):
    flattened_schema_props = flatten_schema(schema_properties)

    # validate instance properties don't
    # contain properties that are not defined
    # in the schema.

    for key in instance_properties.iterkeys():
        if key not in flattened_schema_props:
            ex = DSLParsingLogicException(
                106,
                undefined_property_error_message.format(node_name, key))
            ex.property = key
            raise ex

    merged_properties = dict(flattened_schema_props.items() +
                             instance_properties.items())

    for key, value in merged_properties.iteritems():
        if value is None:
            ex = DSLParsingLogicException(
                107,
                missing_property_error_message.format(node_name, key))
            ex.property = key
            raise ex

    _validate_properties_types(merged_properties, schema_properties)

    return merged_properties
예제 #3
0
def extract_complete_type_recursive(dsl_type,
                                    dsl_type_name,
                                    dsl_container,
                                    merging_func,
                                    is_relationships,
                                    visited_type_names=None):

    """
    This method is applicable to both types and relationships.
    it's concerned with extracting the super types recursively,
    where the merging_func parameter is used
    to merge them with the current type

    :param dsl_type:
    :param dsl_type_name:
    :param dsl_container:
    :param merging_func:
    :param is_relationships:
    :param visited_type_names:
    :return:
    """

    if not visited_type_names:
        visited_type_names = []
    if dsl_type_name in visited_type_names:
        visited_type_names.append(dsl_type_name)
        ex = DSLParsingLogicException(
            100, 'Failed parsing {0} {1}, Circular dependency detected: {2}'
                 .format('relationship' if is_relationships else 'type',
                         dsl_type_name,
                         ' --> '.join(visited_type_names)))
        ex.circular_dependency = visited_type_names
        raise ex
    visited_type_names.append(dsl_type_name)
    current_level_type = copy.deepcopy(dsl_type)

    # halt condition
    if 'derived_from' not in current_level_type:
        return current_level_type

    super_type_name = current_level_type['derived_from']
    if super_type_name not in dsl_container:
        raise DSLParsingLogicException(
            14, 'Missing definition for {0} {1} which is declared as derived '
                'by {0} {2}'
                .format('relationship' if is_relationships else 'type',
                        super_type_name,
                        dsl_type_name))

    super_type = dsl_container[super_type_name]
    complete_super_type = extract_complete_type_recursive(
        dsl_type=super_type,
        dsl_type_name=super_type_name,
        dsl_container=dsl_container,
        merging_func=merging_func,
        visited_type_names=visited_type_names,
        is_relationships=is_relationships)
    return merging_func(complete_super_type, current_level_type)
예제 #4
0
def _merge_flattened_schema_and_instance_properties(
        instance_properties,
        schema_properties,
        flattened_schema_properties,
        data_types,
        undefined_property_error_message,
        missing_property_error_message,
        node_name,
        path,
        raise_on_missing_property):
    path = path or []

    # validate instance properties don't
    # contain properties that are not defined
    # in the schema.
    for key in instance_properties:
        if key not in schema_properties:
            ex = DSLParsingLogicException(
                106,
                undefined_property_error_message.format(
                    node_name,
                    _property_description(path, key)))
            ex.property = key
            raise ex

    merged_properties = flattened_schema_properties.copy()
    merged_properties.update(instance_properties)
    result = {}
    for key, property_schema in schema_properties.items():
        if key not in merged_properties:
            required = property_schema.get('required', True)
            if required and raise_on_missing_property:
                ex = DSLParsingLogicException(
                    107,
                    missing_property_error_message.format(
                        node_name,
                        _property_description(path, key)))
                ex.property = key
                raise ex
            else:
                continue
        prop_path = copy.copy(path)
        prop_path.append(key)
        result[key] = parse_value(
            value=merged_properties.get(key),
            derived_value=flattened_schema_properties.get(key),
            type_name=property_schema.get('type'),
            data_types=data_types,
            undefined_property_error_message=undefined_property_error_message,
            missing_property_error_message=missing_property_error_message,
            node_name=node_name,
            path=prop_path,
            raise_on_missing_property=raise_on_missing_property)
    return result
예제 #5
0
def parse_value(value,
                type_name,
                data_types,
                undefined_property_error_message,
                missing_property_error_message,
                node_name,
                path,
                derived_value=None,
                raise_on_missing_property=True):
    if type_name is None:
        return value
    if functions.parse(value) != value:
        # intrinsic function - not validated at the moment
        return value
    if type_name == 'integer':
        if isinstance(value, (int, long)) and not isinstance(value, bool):
            return value
    elif type_name == 'float':
        if isinstance(value,
                      (int, float, long)) and not isinstance(value, bool):
            return value
    elif type_name == 'boolean':
        if isinstance(value, bool):
            return value
    elif type_name == 'string':
        return value
    elif type_name in data_types:
        if isinstance(value, dict):
            data_schema = data_types[type_name]['properties']
            flattened_data_schema = flatten_schema(data_schema)
            if isinstance(derived_value, dict):
                flattened_data_schema.update(derived_value)
            undef_msg = undefined_property_error_message
            return _merge_flattened_schema_and_instance_properties(
                instance_properties=value,
                schema_properties=data_schema,
                flattened_schema_properties=flattened_data_schema,
                data_types=data_types,
                undefined_property_error_message=undef_msg,
                missing_property_error_message=missing_property_error_message,
                node_name=node_name,
                path=path,
                raise_on_missing_property=raise_on_missing_property)
    else:
        raise RuntimeError(
            "Unexpected type defined in property schema for property '{0}'"
            " - unknown type is '{1}'".format(_property_description(path),
                                              type_name))

    raise DSLParsingLogicException(
        exceptions.ERROR_VALUE_DOES_NOT_MATCH_TYPE,
        "Property type validation failed in '{0}': property "
        "'{1}' type is '{2}', yet it was assigned with the "
        "value '{3}'".format(node_name, _property_description(path), type_name,
                             value))
    def resolve(self, import_url):
        failed_urls = {}
        # trying to find a matching rule that can resolve this url
        for rule in self.rules:
            key = list(rule.keys())[0]
            value = list(rule.values())[0]
            prefix = key
            prefix_len = len(key)
            if prefix == import_url[:prefix_len]:
                # found a matching rule
                url_to_resolve = value + import_url[prefix_len:]
                # trying to resolve the resolved_url
                if url_to_resolve not in failed_urls.keys():
                    # there is no point to try to resolve the same url twice
                    try:
                        return read_import(url_to_resolve)
                    except DSLParsingLogicException as ex:
                        # failed to resolve current rule,
                        # continue to the next one
                        failed_urls[url_to_resolve] = str(ex)

        # failed to resolve the url using the rules
        # trying to open the original url
        try:
            return read_import(import_url)
        except DSLParsingLogicException as ex:
            if not self.rules:
                raise
            if not failed_urls:
                # no matching rules
                msg = 'None of the resolver rules {0} was applicable, ' \
                      'failed to resolve the original import url: {1} '\
                    .format(self.rules, ex)
            else:
                # all urls failed to be resolved
                msg = 'Failed to resolve the following urls: {0}. ' \
                      'In addition, failed to resolve the original ' \
                      'import url - {1}'.format(failed_urls, ex)
            ex = DSLParsingLogicException(13, msg)
            ex.failed_import = import_url
            raise ex
예제 #7
0
def parse_dsl_version(dsl_version):

    if not dsl_version:
        raise DSLParsingLogicException(
            71, '{0} is missing or empty'.format(VERSION))

    if not isinstance(dsl_version, basestring):
        raise DSLParsingLogicException(
            72,
            'Invalid {0}: {1} is not a string'.format(VERSION, dsl_version))

    # handle the 'dsl_version_' prefix
    if dsl_version.startswith(DSL_VERSION_PREFIX):
        short_dsl_version = dsl_version[len(DSL_VERSION_PREFIX):]
    else:
        raise DSLParsingLogicException(
            73, "Invalid {0}: '{1}', expected a "
            "value following this format: '{2}'".format(
                VERSION, dsl_version, DSL_VERSION_1_0))

    if not short_dsl_version.__contains__("_"):
        raise DSLParsingLogicException(
            73, "Invalid {0}: '{1}', expected a "
            "value following this format: '{2}'".format(
                VERSION, dsl_version, DSL_VERSION_1_0))

    version_parts = short_dsl_version.split('_')
    version_details = collections.namedtuple('version_details',
                                             ['major', 'minor', 'micro'])
    major = version_parts[0]
    minor = version_parts[1]
    micro = None
    if len(version_parts) > 2:
        micro = version_parts[2]

    if not major.isdigit():
        raise DSLParsingLogicException(
            74, "Invalid {0}: '{1}', major version "
            "is '{2}' while expected to be a number".format(
                VERSION, dsl_version, major))

    if not minor.isdigit():
        raise DSLParsingLogicException(
            75, "Invalid {0}: '{1}', minor version "
            "is '{2}' while expected to be a number".format(
                VERSION, dsl_version, minor))

    if micro and not micro.isdigit():
        raise DSLParsingLogicException(
            76, "Invalid {0}: '{1}', micro version "
            "is '{2}' while expected to be a number".format(
                VERSION, dsl_version, micro))

    return version_details(int(major), int(minor),
                           int(micro) if micro else None)
    def resolve(self, import_url):
        failed_urls = {}
        # trying to find a matching rule that can resolve this url
        for rule in self.rules:
            # the validate method checks that the dict has exactly 1 element
            prefix, value = list(rule.items())[0]
            prefix_len = len(prefix)
            if prefix == import_url[:prefix_len]:
                # found a matching rule
                url_to_resolve = value + import_url[prefix_len:]
                # trying to resolve the resolved_url
                if url_to_resolve not in failed_urls:
                    # there is no point to try to resolve the same url twice
                    try:
                        return read_import(url_to_resolve)
                    except DSLParsingLogicException as ex:
                        # failed to resolve current rule,
                        # continue to the next one
                        failed_urls[url_to_resolve] = str(ex)

        # failed to resolve the url using the rules
        # trying to open the original url
        try:
            return read_import(import_url)
        except DSLParsingLogicException as ex:
            if not self.rules:
                raise
            if not failed_urls:
                # no matching rules
                msg = 'None of the resolver rules {0} was applicable, ' \
                      'failed to resolve the original import url: {1} '\
                    .format(self.rules, ex)
            else:
                # all urls failed to be resolved
                msg = 'Failed to resolve the following urls: {0}. ' \
                      'In addition, failed to resolve the original ' \
                      'import url - {1}'.format(failed_urls, ex)
            ex = DSLParsingLogicException(13, msg)
            ex.failed_import = import_url
            raise ex
예제 #9
0
def validate_missing_inputs(inputs, schema_inputs):
    """Check that all inputs defined in schema_inputs exist in inputs"""

    missing_inputs = set(schema_inputs) - set(inputs)
    if missing_inputs:
        if len(missing_inputs) == 1:
            message = "Input '{0}' is missing a value".format(
                missing_inputs.pop())
        else:
            formatted_inputs = ', '.join("'{0}'".format(input_name)
                                         for input_name in missing_inputs)
            message = "Inputs {0} are missing a value".format(formatted_inputs)

        raise DSLParsingLogicException(ERROR_MISSING_PROPERTY, message)
예제 #10
0
def _merge_flattened_schema_and_instance_properties(
        instance_properties,
        schema_properties,
        flattened_schema_properties,
        data_types,
        undefined_property_error_message,
        missing_property_error_message,
        node_name,
        path,
        raise_on_missing_property):
    path = path or []

    # validate instance properties don't
    # contain properties that are not defined
    # in the schema.
    for key in instance_properties.iterkeys():
        if key not in schema_properties:
            ex = DSLParsingLogicException(
                106,
                undefined_property_error_message.format(
                    node_name,
                    _property_description(path, key)))
            ex.property = key
            raise ex

    merged_properties = dict(flattened_schema_properties.items() +
                             instance_properties.items())
    result = {}
    for key, property_schema in schema_properties.iteritems():
        if key not in merged_properties:
            required = property_schema.get('required', True)
            if required and raise_on_missing_property:
                ex = DSLParsingLogicException(
                    107,
                    missing_property_error_message.format(
                        node_name,
                        _property_description(path, key)))
                ex.property = key
                raise ex
            else:
                continue
        prop_path = copy.copy(path)
        prop_path.append(key)
        result[key] = parse_value(
            value=merged_properties.get(key),
            derived_value=flattened_schema_properties.get(key),
            type_name=property_schema.get('type'),
            data_types=data_types,
            undefined_property_error_message=undefined_property_error_message,
            missing_property_error_message=missing_property_error_message,
            node_name=node_name,
            path=prop_path,
            raise_on_missing_property=raise_on_missing_property)
    return result
예제 #11
0
def validate_dsl_version(dsl_version):
    if dsl_version not in SUPPORTED_VERSIONS:
        raise DSLParsingLogicException(
            29, 'Unexpected tosca_definitions_version {0}; Currently '
                'supported versions are: {1}'.format(dsl_version,
                                                     SUPPORTED_VERSIONS))
예제 #12
0
def validate_missing_inputs(inputs):
    for key, value in inputs.iteritems():
        if value is None:
            raise DSLParsingLogicException(
                107, "Input '{0}' is missing a value".format(key))
예제 #13
0
class DefaultImportResolver(AbstractImportResolver):
    """
    This class is a default implementation of an import resolver.
    This resolver uses the rules to replace URL's prefix with another prefix
    and tries to resolve the new URL (after the prefix has been replaced).
    If there aren't any rules, none of the rules matches or
    none of the prefix replacements works,
    the resolver will try to use the original URL.

    Each rule in the ``rules`` list is expected to be
    a dictionary with one (key, value) pair which represents
    a prefix and its replacement which can be used to resolve the import url.

    The resolver will go over the rules and for each matching rule
    (its key is a prefix of the url) it will replace the prefix
    with the value and will try to resolve the new url.

    For example:
        The rules list: [
            {'http://prefix1': 'http://prefix1_replacement'},
            {'http://prefix2': 'http://prefix2_replacement1'},
            {'http://prefix2': 'http://prefix2_replacement2'}
        ]
        contains three rules that can be used for resolve URLs that
        starts with 'http://prefix1' and 'http://prefix2'.
        If the url is 'http://prefix2.suffix2.org' than the resolve method
        will find a match in both the second and the third rules.

        It will first try to apply the second rule by replacing the url's
        prefix with the second rule value ('http://prefix2_replacement1')
        and will try to resolve the new url:
        'http://prefix2_replacement1.suffix2.org'.

        In case this url cannot be resolved, it will try to apply
        the third rule by replacing the url's prefix with
        the third rule value ('http://prefix2_replacement2')
        and will try to resolve the url:
        'http://prefix2_replacement2.suffix2.org'.

        If this url, also, cannot be resolved,
        it will try to resolve the original url,
        i.e. http://prefix2.suffix2.org'

        In case that all the resolve attempts will fail,
        a DSLParsingLogicException will be raise.
    """
    def __init__(self, rules=None):
        # set the rules
        self.rules = rules
        if self.rules is None:
            self.rules = DEFAULT_RULES
        self._validate_rules()

    def resolve(self, import_url):
        failed_urls = {}
        # trying to find a matching rule that can resolve this url
        for rule in self.rules:
            key = rule.keys()[0]
            value = rule.values()[0]
            prefix = key
            prefix_len = len(key)
            if prefix == import_url[:prefix_len]:
                # found a matching rule
                url_to_resolve = value + import_url[prefix_len:]
                # trying to resolve the resolved_url
                if url_to_resolve not in failed_urls.keys():
                    # there is no point to try to resolve the same url twice
                    try:
                        return read_import(url_to_resolve)
                    except DSLParsingLogicException, ex:
                        # failed to resolve current rule,
                        # continue to the next one
                        failed_urls[url_to_resolve] = str(ex)

        # failed to resolve the url using the rules
        # trying to open the original url
        try:
            return read_import(import_url)
        except DSLParsingLogicException, ex:
            if not self.rules:
                raise
            if not failed_urls:
                # no matching rules
                msg = 'None of the resolver rules {0} was applicable, ' \
                      'failed to resolve the original import url: {1} '\
                    .format(self.rules, ex)
            else:
                # all urls failed to be resolved
                msg = 'Failed to resolve the following urls: {0}. ' \
                      'In addition, failed to resolve the original ' \
                      'import url - {1}'.format(failed_urls, ex)
            ex = DSLParsingLogicException(13, msg)
            ex.failed_import = import_url
            raise ex