Exemplo n.º 1
0
def _validate_elements(wanted_type,
                       parameter,
                       values,
                       options_context=None,
                       errors=None):

    if errors is None:
        errors = AnsibleValidationErrorMultiple()

    type_checker, wanted_element_type = _get_type_validator(wanted_type)
    validated_parameters = []
    # Get param name for strings so we can later display this value in a useful error message if needed
    # Only pass 'kwargs' to our checkers and ignore custom callable checkers
    kwargs = {}
    if wanted_element_type == 'str' and isinstance(wanted_type, string_types):
        if isinstance(parameter, string_types):
            kwargs['param'] = parameter
        elif isinstance(parameter, dict):
            kwargs['param'] = list(parameter.keys())[0]

    for value in values:
        try:
            validated_parameters.append(type_checker(value, **kwargs))
        except (TypeError, ValueError) as e:
            msg = "Elements value for option '%s'" % parameter
            if options_context:
                msg += " found in '%s'" % " -> ".join(options_context)
            msg += " is of type %s and we were unable to convert to %s: %s" % (
                type(value), wanted_element_type, to_native(e))
            errors.append(ElementError(msg))
    return validated_parameters
Exemplo n.º 2
0
def _validate_argument_values(argument_spec, parameters, options_context=None, errors=None):
    """Ensure all arguments have the requested values, and there are no stray arguments"""

    if errors is None:
        errors = AnsibleValidationErrorMultiple()

    for param, spec in argument_spec.items():
        choices = spec.get('choices')
        if choices is None:
            continue

        if isinstance(choices, (frozenset, KeysView, Sequence)) and not isinstance(choices, (binary_type, text_type)):
            if param in parameters:
                # Allow one or more when type='list' param with choices
                if isinstance(parameters[param], list):
                    diff_list = ", ".join([item for item in parameters[param] if item not in choices])
                    if diff_list:
                        choices_str = ", ".join([to_native(c) for c in choices])
                        msg = "value of %s must be one or more of: %s. Got no match for: %s" % (param, choices_str, diff_list)
                        if options_context:
                            msg = "{0} found in {1}".format(msg, " -> ".join(options_context))
                        errors.append(ArgumentValueError(msg))
                elif parameters[param] not in choices:
                    # PyYaml converts certain strings to bools. If we can unambiguously convert back, do so before checking
                    # the value. If we can't figure this out, module author is responsible.
                    lowered_choices = None
                    if parameters[param] == 'False':
                        lowered_choices = lenient_lowercase(choices)
                        overlap = BOOLEANS_FALSE.intersection(choices)
                        if len(overlap) == 1:
                            # Extract from a set
                            (parameters[param],) = overlap

                    if parameters[param] == 'True':
                        if lowered_choices is None:
                            lowered_choices = lenient_lowercase(choices)
                        overlap = BOOLEANS_TRUE.intersection(choices)
                        if len(overlap) == 1:
                            (parameters[param],) = overlap

                    if parameters[param] not in choices:
                        choices_str = ", ".join([to_native(c) for c in choices])
                        msg = "value of %s must be one of: %s, got: %s" % (param, choices_str, parameters[param])
                        if options_context:
                            msg = "{0} found in {1}".format(msg, " -> ".join(options_context))
                        errors.append(ArgumentValueError(msg))
        else:
            msg = "internal error: choices for argument %s are not iterable: %s" % (param, choices)
            if options_context:
                msg = "{0} found in {1}".format(msg, " -> ".join(options_context))
            errors.append(ArgumentTypeError(msg))
Exemplo n.º 3
0
def _validate_sub_spec(argument_spec,
                       parameters,
                       prefix='',
                       options_context=None,
                       errors=None,
                       no_log_values=None,
                       unsupported_parameters=None):
    """Validate sub argument spec.

    This function is recursive.
    """

    if options_context is None:
        options_context = []

    if errors is None:
        errors = AnsibleValidationErrorMultiple()

    if no_log_values is None:
        no_log_values = set()

    if unsupported_parameters is None:
        unsupported_parameters = set()

    for param, value in argument_spec.items():
        wanted = value.get('type')
        if wanted == 'dict' or (wanted == 'list'
                                and value.get('elements', '') == 'dict'):
            sub_spec = value.get('options')
            if value.get('apply_defaults', False):
                if sub_spec is not None:
                    if parameters.get(param) is None:
                        parameters[param] = {}
                else:
                    continue
            elif sub_spec is None or param not in parameters or parameters[
                    param] is None:
                continue

            # Keep track of context for warning messages
            options_context.append(param)

            # Make sure we can iterate over the elements
            if not isinstance(parameters[param], Sequence) or isinstance(
                    parameters[param], string_types):
                elements = [parameters[param]]
            else:
                elements = parameters[param]

            for idx, sub_parameters in enumerate(elements):
                no_log_values.update(set_fallbacks(sub_spec, sub_parameters))

                if not isinstance(sub_parameters, dict):
                    errors.append(
                        SubParameterTypeError(
                            "value of '%s' must be of type dict or list of dicts"
                            % param))
                    continue

                # Set prefix for warning messages
                new_prefix = prefix + param
                if wanted == 'list':
                    new_prefix += '[%d]' % idx
                new_prefix += '.'

                alias_warnings = []
                alias_deprecations = []
                try:
                    options_aliases = _handle_aliases(sub_spec, sub_parameters,
                                                      alias_warnings,
                                                      alias_deprecations)
                except (TypeError, ValueError) as e:
                    options_aliases = {}
                    errors.append(AliasError(to_native(e)))

                for option, alias in alias_warnings:
                    warn('Both option %s and its alias %s are set.' %
                         (option, alias))

                try:
                    no_log_values.update(
                        _list_no_log_values(sub_spec, sub_parameters))
                except TypeError as te:
                    errors.append(NoLogError(to_native(te)))

                legal_inputs = _get_legal_inputs(sub_spec, sub_parameters,
                                                 options_aliases)
                unsupported_parameters.update(
                    _get_unsupported_parameters(sub_spec, sub_parameters,
                                                legal_inputs, options_context))

                try:
                    check_mutually_exclusive(value.get('mutually_exclusive'),
                                             sub_parameters, options_context)
                except TypeError as e:
                    errors.append(MutuallyExclusiveError(to_native(e)))

                no_log_values.update(
                    _set_defaults(sub_spec, sub_parameters, False))

                try:
                    check_required_arguments(sub_spec, sub_parameters,
                                             options_context)
                except TypeError as e:
                    errors.append(RequiredError(to_native(e)))

                _validate_argument_types(sub_spec,
                                         sub_parameters,
                                         new_prefix,
                                         options_context,
                                         errors=errors)
                _validate_argument_values(sub_spec,
                                          sub_parameters,
                                          options_context,
                                          errors=errors)

                for check in _ADDITIONAL_CHECKS:
                    try:
                        check['func'](value.get(check['attr']), sub_parameters,
                                      options_context)
                    except TypeError as e:
                        errors.append(check['err'](to_native(e)))

                no_log_values.update(_set_defaults(sub_spec, sub_parameters))

                # Handle nested specs
                _validate_sub_spec(sub_spec, sub_parameters, new_prefix,
                                   options_context, errors, no_log_values,
                                   unsupported_parameters)

            options_context.pop()
Exemplo n.º 4
0
def _validate_argument_types(argument_spec,
                             parameters,
                             prefix='',
                             options_context=None,
                             errors=None):
    """Validate that parameter types match the type in the argument spec.

    Determine the appropriate type checker function and run each
    parameter value through that function. All error messages from type checker
    functions are returned. If any parameter fails to validate, it will not
    be in the returned parameters.

    :arg argument_spec: Argument spec
    :type argument_spec: dict

    :arg parameters: Parameters
    :type parameters: dict

    :kwarg prefix: Name of the parent key that contains the spec. Used in the error message
    :type prefix: str

    :kwarg options_context: List of contexts?
    :type options_context: list

    :returns: Two item tuple containing validated and coerced parameters
              and a list of any errors that were encountered.
    :rtype: tuple

    """

    if errors is None:
        errors = AnsibleValidationErrorMultiple()

    for param, spec in argument_spec.items():
        if param not in parameters:
            continue

        value = parameters[param]
        if value is None:
            continue

        wanted_type = spec.get('type')
        type_checker, wanted_name = _get_type_validator(wanted_type)
        # Get param name for strings so we can later display this value in a useful error message if needed
        # Only pass 'kwargs' to our checkers and ignore custom callable checkers
        kwargs = {}
        if wanted_name == 'str' and isinstance(wanted_type, string_types):
            kwargs['param'] = list(parameters.keys())[0]

            # Get the name of the parent key if this is a nested option
            if prefix:
                kwargs['prefix'] = prefix

        try:
            parameters[param] = type_checker(value, **kwargs)
            elements_wanted_type = spec.get('elements', None)
            if elements_wanted_type:
                elements = parameters[param]
                if wanted_type != 'list' or not isinstance(elements, list):
                    msg = "Invalid type %s for option '%s'" % (wanted_name,
                                                               elements)
                    if options_context:
                        msg += " found in '%s'." % " -> ".join(options_context)
                    msg += ", elements value check is supported only with 'list' type"
                    errors.append(ArgumentTypeError(msg))
                parameters[param] = _validate_elements(elements_wanted_type,
                                                       param, elements,
                                                       options_context, errors)

        except (TypeError, ValueError) as e:
            msg = "argument '%s' is of type %s" % (param, type(value))
            if options_context:
                msg += " found in '%s'." % " -> ".join(options_context)
            msg += " and we were unable to convert to %s: %s" % (wanted_name,
                                                                 to_native(e))
            errors.append(ArgumentTypeError(msg))