Exemple #1
0
    def _load_file(self, file_name):
        if not file_name or not isinstance(file_name, string_types):
            raise AssibleParserError("Invalid filename: '%s'" % to_native(file_name))

        b_file_name = to_bytes(self.loader.path_dwim(file_name))
        if not self.loader.path_exists(b_file_name):
            raise AssibleFileNotFound("Unable to retrieve file contents", file_name=file_name)

        try:
            (b_data, private) = self.loader._get_file_contents(file_name)
            return toml.loads(to_text(b_data, errors='surrogate_or_strict'))
        except toml.TomlDecodeError as e:
            raise AssibleParserError(
                'TOML file (%s) is invalid: %s' % (file_name, to_native(e)),
                orig_exc=e
            )
        except (IOError, OSError) as e:
            raise AssibleParserError(
                "An error occurred while trying to read the file '%s': %s" % (file_name, to_native(e)),
                orig_exc=e
            )
        except Exception as e:
            raise AssibleParserError(
                "An unexpected error occurred while parsing the file '%s': %s" % (file_name, to_native(e)),
                orig_exc=e
            )
Exemple #2
0
    def parse(self, inventory, loader, path, cache=True):
        ''' parses the inventory file '''

        super(InventoryModule, self).parse(inventory, loader, path)
        self.set_options()

        try:
            data = self.loader.load_from_file(path, cache=False)
        except Exception as e:
            raise AssibleParserError(e)

        if not data:
            raise AssibleParserError('Parsed empty YAML file')
        elif not isinstance(data, MutableMapping):
            raise AssibleParserError(
                'YAML inventory has invalid structure, it should be a dictionary, got: %s'
                % type(data))
        elif data.get('plugin'):
            raise AssibleParserError(
                'Plugin configuration YAML file, not YAML inventory')

        # We expect top level keys to correspond to groups, iterate over them
        # to get host, vars and subgroups (which we iterate over recursivelly)
        if isinstance(data, MutableMapping):
            for group_name in data:
                self._parse_group(group_name, data[group_name])
        else:
            raise AssibleParserError(
                "Invalid data from file, expected dictionary and got:\n\n%s" %
                to_native(data))
    def preprocess_data(self, ds):
        '''
        Regorganizes the data for a PlaybookInclude datastructure to line
        up with what we expect the proper attributes to be
        '''

        if not isinstance(ds, dict):
            raise AssibleAssertionError(
                'ds (%s) should be a dict but was a %s' % (ds, type(ds)))

        # the new, cleaned datastructure, which will have legacy
        # items reduced to a standard structure
        new_ds = AssibleMapping()
        if isinstance(ds, AssibleBaseYAMLObject):
            new_ds.assible_pos = ds.assible_pos

        for (k, v) in iteritems(ds):
            if k in ('include', 'import_playbook'):
                self._preprocess_import(ds, new_ds, k, v)
            else:
                # some basic error checking, to make sure vars are properly
                # formatted and do not conflict with k=v parameters
                if k == 'vars':
                    if 'vars' in new_ds:
                        raise AssibleParserError(
                            "import_playbook parameters cannot be mixed with 'vars' entries for import statements",
                            obj=ds)
                    elif not isinstance(v, dict):
                        raise AssibleParserError(
                            "vars for import_playbook statements must be specified as a dictionary",
                            obj=ds)
                new_ds[k] = v

        return super(PlaybookInclude, self).preprocess_data(new_ds)
Exemple #4
0
    def check_options(self, task, data):
        '''
        Method for options validation to use in 'load_data' for TaskInclude and HandlerTaskInclude
        since they share the same validations. It is not named 'validate_options' on purpose
        to prevent confusion with '_validate_*" methods. Note that the task passed might be changed
        as a side-effect of this method.
        '''
        my_arg_names = frozenset(task.args.keys())

        # validate bad args, otherwise we silently ignore
        bad_opts = my_arg_names.difference(self.VALID_ARGS)
        if bad_opts and task.action in ('include_tasks', 'import_tasks'):
            raise AssibleParserError('Invalid options for %s: %s' %
                                     (task.action, ','.join(list(bad_opts))),
                                     obj=data)

        if not task.args.get('_raw_params'):
            task.args['_raw_params'] = task.args.pop('file', None)
            if not task.args['_raw_params']:
                raise AssibleParserError('No file specified for %s' %
                                         task.action)

        apply_attrs = task.args.get('apply', {})
        if apply_attrs and task.action != 'include_tasks':
            raise AssibleParserError('Invalid options for %s: apply' %
                                     task.action,
                                     obj=data)
        elif not isinstance(apply_attrs, dict):
            raise AssibleParserError(
                'Expected a dict for apply but got %s instead' %
                type(apply_attrs),
                obj=data)

        return task
Exemple #5
0
    def _get_file_contents(self, file_name):
        '''
        Reads the file contents from the given file name

        If the contents are vault-encrypted, it will decrypt them and return
        the decrypted data

        :arg file_name: The name of the file to read.  If this is a relative
            path, it will be expanded relative to the basedir
        :raises AssibleFileNotFound: if the file_name does not refer to a file
        :raises AssibleParserError: if we were unable to read the file
        :return: Returns a byte string of the file contents
        '''
        if not file_name or not isinstance(file_name,
                                           (binary_type, text_type)):
            raise AssibleParserError("Invalid filename: '%s'" %
                                     to_native(file_name))

        b_file_name = to_bytes(self.path_dwim(file_name))
        # This is what we really want but have to fix unittests to make it pass
        # if not os.path.exists(b_file_name) or not os.path.isfile(b_file_name):
        if not self.path_exists(b_file_name):
            raise AssibleFileNotFound("Unable to retrieve file contents",
                                      file_name=file_name)

        try:
            with open(b_file_name, 'rb') as f:
                data = f.read()
                return self._decrypt_if_vault_data(data, b_file_name)
        except (IOError, OSError) as e:
            raise AssibleParserError(
                "an error occurred while trying to read the file '%s': %s" %
                (file_name, to_native(e)),
                orig_exc=e)
Exemple #6
0
def parse_kv(args, check_raw=False):
    '''
    Convert a string of key/value items to a dict. If any free-form params
    are found and the check_raw option is set to True, they will be added
    to a new parameter called '_raw_params'. If check_raw is not enabled,
    they will simply be ignored.
    '''

    args = to_text(args, nonstring='passthru')

    options = {}
    if args is not None:
        try:
            vargs = split_args(args)
        except IndexError as e:
            raise AssibleParserError("Unable to parse argument string",
                                     orig_exc=e)
        except ValueError as ve:
            if 'no closing quotation' in str(ve).lower():
                raise AssibleParserError(
                    "error parsing argument string, try quoting the entire line.",
                    orig_exc=ve)
            else:
                raise

        raw_params = []
        for orig_x in vargs:
            x = _decode_escapes(orig_x)
            if "=" in x:
                pos = 0
                try:
                    while True:
                        pos = x.index('=', pos + 1)
                        if pos > 0 and x[pos - 1] != '\\':
                            break
                except ValueError:
                    # ran out of string, but we must have some escaped equals,
                    # so replace those and append this to the list of raw params
                    raw_params.append(x.replace('\\=', '='))
                    continue

                k = x[:pos]
                v = x[pos + 1:]

                # FIXME: make the retrieval of this list of shell/command
                #        options a function, so the list is centralized
                if check_raw and k not in ('creates', 'removes', 'chdir',
                                           'executable', 'warn'):
                    raw_params.append(orig_x)
                else:
                    options[k.strip()] = unquote(v.strip())
            else:
                raw_params.append(orig_x)

        # recombine the free-form params, if any were found, and assign
        # them to a special option for use later by the shell/command module
        if len(raw_params) > 0:
            options[u'_raw_params'] = join_args(raw_params)

    return options
Exemple #7
0
    def _load_vars(self, attr, ds):
        '''
        Vars in a play can be specified either as a dictionary directly, or
        as a list of dictionaries. If the later, this method will turn the
        list into a single dictionary.
        '''

        def _validate_variable_keys(ds):
            for key in ds:
                if not isidentifier(key):
                    raise TypeError("'%s' is not a valid variable name" % key)

        try:
            if isinstance(ds, dict):
                _validate_variable_keys(ds)
                return combine_vars(self.vars, ds)
            elif isinstance(ds, list):
                all_vars = self.vars
                for item in ds:
                    if not isinstance(item, dict):
                        raise ValueError
                    _validate_variable_keys(item)
                    all_vars = combine_vars(all_vars, item)
                return all_vars
            elif ds is None:
                return {}
            else:
                raise ValueError
        except ValueError as e:
            raise AssibleParserError("Vars in a %s must be specified as a dictionary, or a list of dictionaries" % self.__class__.__name__,
                                     obj=ds, orig_exc=e)
        except TypeError as e:
            raise AssibleParserError("Invalid variable name in vars specified for %s: %s" % (self.__class__.__name__, e), obj=ds, orig_exc=e)
Exemple #8
0
    def get_vars(self, loader, path, entities, cache=True):
        ''' parses the inventory file '''

        if not isinstance(entities, list):
            entities = [entities]

        super(VarsModule, self).get_vars(loader, path, entities)

        data = {}
        for entity in entities:
            if isinstance(entity, Host):
                subdir = 'host_vars'
            elif isinstance(entity, Group):
                subdir = 'group_vars'
            else:
                raise AssibleParserError(
                    "Supplied entity must be Host or Group, got %s instead" %
                    (type(entity)))

            # avoid 'chroot' type inventory hostnames /path/to/chroot
            if not entity.name.startswith(os.path.sep):
                try:
                    found_files = []
                    # load vars
                    b_opath = os.path.realpath(
                        to_bytes(os.path.join(self._basedir, subdir)))
                    opath = to_text(b_opath)
                    key = '%s.%s' % (entity.name, opath)
                    if cache and key in FOUND:
                        found_files = FOUND[key]
                    else:
                        # no need to do much if path does not exist for basedir
                        if os.path.exists(b_opath):
                            if os.path.isdir(b_opath):
                                self._display.debug("\tprocessing dir %s" %
                                                    opath)
                                found_files = loader.find_vars_files(
                                    opath, entity.name)
                                FOUND[key] = found_files
                            else:
                                self._display.warning(
                                    "Found %s that is not a directory, skipping: %s"
                                    % (subdir, opath))

                    for found in found_files:
                        new_data = loader.load_from_file(found,
                                                         cache=True,
                                                         unsafe=True)
                        if new_data:  # ignore empty files
                            data = combine_vars(data, new_data)

                except Exception as e:
                    raise AssibleParserError(to_native(e))
        return data
Exemple #9
0
    def _normalize_parameters(self, thing, action=None, additional_args=None):
        '''
        arguments can be fuzzy.  Deal with all the forms.
        '''

        additional_args = {} if additional_args is None else additional_args

        # final args are the ones we'll eventually return, so first update
        # them with any additional args specified, which have lower priority
        # than those which may be parsed/normalized next
        final_args = dict()
        if additional_args:
            if isinstance(additional_args, string_types):
                templar = Templar(loader=None)
                if templar.is_template(additional_args):
                    final_args['_variable_params'] = additional_args
                else:
                    raise AssibleParserError("Complex args containing variables cannot use bare variables (without Jinja2 delimiters), "
                                             "and must use the full variable style ('{{var_name}}')")
            elif isinstance(additional_args, dict):
                final_args.update(additional_args)
            else:
                raise AssibleParserError('Complex args must be a dictionary or variable string ("{{var}}").')

        # how we normalize depends if we figured out what the module name is
        # yet.  If we have already figured it out, it's a 'new style' invocation.
        # otherwise, it's not

        if action is not None:
            args = self._normalize_new_style_args(thing, action)
        else:
            (action, args) = self._normalize_old_style_args(thing)

            # this can occasionally happen, simplify
            if args and 'args' in args:
                tmp_args = args.pop('args')
                if isinstance(tmp_args, string_types):
                    tmp_args = parse_kv(tmp_args)
                args.update(tmp_args)

        # only internal variables can start with an underscore, so
        # we don't allow users to set them directly in arguments
        if args and action not in FREEFORM_ACTIONS:
            for arg in args:
                arg = to_text(arg)
                if arg.startswith('_assible_'):
                    raise AssibleError("invalid parameter specified for action '%s': '%s'" % (action, arg))

        # finally, update the args we're going to return with the ones
        # which were normalized above
        if args:
            final_args.update(args)

        return (action, final_args)
Exemple #10
0
 def get_validated_value(self, name, attribute, value, templar):
     if attribute.isa == 'string':
         value = to_text(value)
     elif attribute.isa == 'int':
         value = int(value)
     elif attribute.isa == 'float':
         value = float(value)
     elif attribute.isa == 'bool':
         value = boolean(value, strict=True)
     elif attribute.isa == 'percent':
         # special value, which may be an integer or float
         # with an optional '%' at the end
         if isinstance(value, string_types) and '%' in value:
             value = value.replace('%', '')
         value = float(value)
     elif attribute.isa == 'list':
         if value is None:
             value = []
         elif not isinstance(value, list):
             value = [value]
         if attribute.listof is not None:
             for item in value:
                 if not isinstance(item, attribute.listof):
                     raise AssibleParserError("the field '%s' should be a list of %s, "
                                              "but the item '%s' is a %s" % (name, attribute.listof, item, type(item)), obj=self.get_ds())
                 elif attribute.required and attribute.listof == string_types:
                     if item is None or item.strip() == "":
                         raise AssibleParserError("the field '%s' is required, and cannot have empty values" % (name,), obj=self.get_ds())
     elif attribute.isa == 'set':
         if value is None:
             value = set()
         elif not isinstance(value, (list, set)):
             if isinstance(value, string_types):
                 value = value.split(',')
             else:
                 # Making a list like this handles strings of
                 # text and bytes properly
                 value = [value]
         if not isinstance(value, set):
             value = set(value)
     elif attribute.isa == 'dict':
         if value is None:
             value = dict()
         elif not isinstance(value, dict):
             raise TypeError("%s is not a dictionary" % value)
     elif attribute.isa == 'class':
         if not isinstance(value, attribute.class_type):
             raise TypeError("%s is not a valid %s (got a %s instead)" % (name, attribute.class_type, type(value)))
         value.post_validate(templar=templar)
     return value
Exemple #11
0
    def preprocess_data(self, ds):
        '''
        Adjusts play datastructure to cleanup old/legacy items
        '''

        if not isinstance(ds, dict):
            raise AssibleAssertionError(
                'while preprocessing data (%s), ds should be a dict but was a %s'
                % (ds, type(ds)))

        # The use of 'user' in the Play datastructure was deprecated to
        # line up with the same change for Tasks, due to the fact that
        # 'user' conflicted with the user module.
        if 'user' in ds:
            # this should never happen, but error out with a helpful message
            # to the user if it does...
            if 'remote_user' in ds:
                raise AssibleParserError(
                    "both 'user' and 'remote_user' are set for %s. "
                    "The use of 'user' is deprecated, and should be removed" %
                    self.get_name(),
                    obj=ds)

            ds['remote_user'] = ds['user']
            del ds['user']

        return super(Play, self).preprocess_data(ds)
Exemple #12
0
    def validate(self, all_vars=None):
        ''' validation that is done at parse time, not load time '''
        all_vars = {} if all_vars is None else all_vars

        if not self._validated:
            # walk all fields in the object
            for (name, attribute) in iteritems(self._valid_attrs):

                if name in self._alias_attrs:
                    name = self._alias_attrs[name]

                # run validator only if present
                method = getattr(self, '_validate_%s' % name, None)
                if method:
                    method(attribute, name, getattr(self, name))
                else:
                    # and make sure the attribute is of the type it should be
                    value = self._attributes[name]
                    if value is not None:
                        if attribute.isa == 'string' and isinstance(value, (list, dict)):
                            raise AssibleParserError(
                                "The field '%s' is supposed to be a string type,"
                                " however the incoming data structure is a %s" % (name, type(value)), obj=self.get_ds()
                            )

        self._validated = True
Exemple #13
0
    def _add_host_to_composed_groups(self,
                                     groups,
                                     variables,
                                     host,
                                     strict=False):
        ''' helper to create complex groups for plugins based on jinja2 conditionals, hosts that meet the conditional are added to group'''
        # process each 'group entry'
        if groups and isinstance(groups, dict):
            variables = combine_vars(variables,
                                     self.inventory.get_host(host).get_vars())
            self.templar.available_variables = variables
            for group_name in groups:
                conditional = "{%% if %s %%} True {%% else %%} False {%% endif %%}" % groups[
                    group_name]
                group_name = original_safe(group_name, force=True)
                try:
                    result = boolean(self.templar.template(conditional))
                except Exception as e:
                    if strict:
                        raise AssibleParserError(
                            "Could not add host %s to group %s: %s" %
                            (host, group_name, to_native(e)))
                    continue

                if result:
                    # ensure group exists, use sanitized name
                    group_name = self.inventory.add_group(group_name)
                    # add host to group
                    self.inventory.add_child(group_name, host)
Exemple #14
0
    def _load_roles(self, attr, ds):
        '''
        Loads and returns a list of RoleInclude objects from the datastructure
        list of role definitions and creates the Role from those objects
        '''

        if ds is None:
            ds = []

        try:
            role_includes = load_list_of_roles(
                ds,
                play=self,
                variable_manager=self._variable_manager,
                loader=self._loader,
                collection_search_list=self.collections)
        except AssertionError as e:
            raise AssibleParserError(
                "A malformed role declaration was encountered.",
                obj=self._ds,
                orig_exc=e)

        roles = []
        for ri in role_includes:
            roles.append(Role.load(ri, play=self))

        self.roles[:0] = roles

        return self.roles
Exemple #15
0
    def run(self, terms, variables=None, **kwargs):

        ret = []

        for term in terms:
            display.debug("File lookup term: %s" % term)

            # Find the file in the expected search path
            lookupfile = self.find_file_in_search_path(variables, 'files', term)
            display.vvvv(u"File lookup using %s as file" % lookupfile)
            try:
                if lookupfile:
                    b_contents, show_data = self._loader._get_file_contents(lookupfile)
                    contents = to_text(b_contents, errors='surrogate_or_strict')
                    if kwargs.get('lstrip', False):
                        contents = contents.lstrip()
                    if kwargs.get('rstrip', True):
                        contents = contents.rstrip()
                    ret.append(contents)
                else:
                    raise AssibleParserError()
            except AssibleParserError:
                raise AssibleError("could not locate file in lookup: %s" % term)

        return ret
Exemple #16
0
 def _load_role_yaml(self, subdir, main=None, allow_dir=False):
     file_path = os.path.join(self._role_path, subdir)
     if self._loader.path_exists(file_path) and self._loader.is_directory(
             file_path):
         # Valid extensions and ordering for roles is hard-coded to maintain
         # role portability
         extensions = ['.yml', '.yaml', '.json']
         # If no <main> is specified by the user, look for files with
         # extensions before bare name. Otherwise, look for bare name first.
         if main is None:
             _main = 'main'
             extensions.append('')
         else:
             _main = main
             extensions.insert(0, '')
         found_files = self._loader.find_vars_files(file_path, _main,
                                                    extensions, allow_dir)
         if found_files:
             data = {}
             for found in found_files:
                 new_data = self._loader.load_from_file(found)
                 if new_data and allow_dir:
                     data = combine_vars(data, new_data)
                 else:
                     data = new_data
             return data
         elif main is not None:
             raise AssibleParserError(
                 "Could not find specified file in role: %s/%s" %
                 (subdir, main))
     return None
Exemple #17
0
def from_yaml(data,
              file_name='<string>',
              show_content=True,
              vault_secrets=None,
              json_only=False):
    '''
    Creates a python datastructure from the given data, which can be either
    a JSON or YAML string.
    '''
    new_data = None

    try:
        # in case we have to deal with vaults
        AssibleJSONDecoder.set_secrets(vault_secrets)

        # we first try to load this data as JSON.
        # Fixes issues with extra vars json strings not being parsed correctly by the yaml parser
        new_data = json.loads(data, cls=AssibleJSONDecoder)
    except Exception as json_exc:

        if json_only:
            raise AssibleParserError(to_native(json_exc), orig_exc=json_exc)

        # must not be JSON, let the rest try
        try:
            new_data = _safe_load(data,
                                  file_name=file_name,
                                  vault_secrets=vault_secrets)
        except YAMLError as yaml_exc:
            _handle_error(json_exc, yaml_exc, file_name, show_content)

    return new_data
Exemple #18
0
    def _normalize_new_style_args(self, thing, action):
        '''
        deals with fuzziness in new style module invocations
        accepting key=value pairs and dictionaries, and returns
        a dictionary of arguments

        possible example inputs:
            'echo hi', 'shell'
            {'region': 'xyz'}, 'ec2'
        standardized outputs like:
            { _raw_params: 'echo hi', _uses_shell: True }
        '''

        if isinstance(thing, dict):
            # form is like: { xyz: { x: 2, y: 3 } }
            args = thing
        elif isinstance(thing, string_types):
            # form is like: copy: src=a dest=b
            check_raw = action in FREEFORM_ACTIONS
            args = parse_kv(thing, check_raw=check_raw)
        elif thing is None:
            # this can happen with modules which take no params, like ping:
            args = None
        else:
            raise AssibleParserError("unexpected parameter type in action: %s" % type(thing), obj=self._task_ds)
        return args
Exemple #19
0
    def get_real_file(self, file_path, decrypt=True):
        """
        If the file is vault encrypted return a path to a temporary decrypted file
        If the file is not encrypted then the path is returned
        Temporary files are cleanup in the destructor
        """

        if not file_path or not isinstance(file_path,
                                           (binary_type, text_type)):
            raise AssibleParserError("Invalid filename: '%s'" %
                                     to_native(file_path))

        b_file_path = to_bytes(file_path, errors='surrogate_or_strict')
        if not self.path_exists(b_file_path) or not self.is_file(b_file_path):
            raise AssibleFileNotFound(file_name=file_path)

        real_path = self.path_dwim(file_path)

        try:
            if decrypt:
                with open(to_bytes(real_path), 'rb') as f:
                    # Limit how much of the file is read since we do not know
                    # whether this is a vault file and therefore it could be very
                    # large.
                    if is_encrypted_file(f, count=len(b_HEADER)):
                        # if the file is encrypted and no password was specified,
                        # the decrypt call would throw an error, but we check first
                        # since the decrypt function doesn't know the file name
                        data = f.read()
                        if not self._vault.secrets:
                            raise AssibleParserError(
                                "A vault password or secret must be specified to decrypt %s"
                                % to_native(file_path))

                        data = self._vault.decrypt(data, filename=real_path)
                        # Make a temp file
                        real_path = self._create_content_tempfile(data)
                        self._tempfiles.add(real_path)

            return real_path

        except (IOError, OSError) as e:
            raise AssibleParserError(
                "an error occurred while trying to read the file '%s': %s" %
                (to_native(real_path), to_native(e)),
                orig_exc=e)
Exemple #20
0
    def _load_loop_control(self, attr, ds):
        if not isinstance(ds, dict):
            raise AssibleParserError(
                "the `loop_control` value must be specified as a dictionary and cannot "
                "be a variable itself (though it can contain variables)",
                obj=ds,
            )

        return LoopControl.load(data=ds, variable_manager=self._variable_manager, loader=self._loader)
Exemple #21
0
    def _validate_attributes(self, ds):
        '''
        Ensures that there are no keys in the datastructure which do
        not map to attributes for this object.
        '''

        valid_attrs = frozenset(self._valid_attrs.keys())
        for key in ds:
            if key not in valid_attrs:
                raise AssibleParserError("'%s' is not a valid attribute for a %s" % (key, self.__class__.__name__), obj=ds)
Exemple #22
0
    def _parse_group(self, group, group_data):
        if group_data is not None and not isinstance(group_data, MutableMapping):
            self.display.warning("Skipping '%s' as this is not a valid group definition" % group)
            return

        group = self.inventory.add_group(group)
        if group_data is None:
            return

        for key, data in group_data.items():
            if key == 'vars':
                if not isinstance(data, MutableMapping):
                    raise AssibleParserError(
                        'Invalid "vars" entry for "%s" group, requires a dict, found "%s" instead.' %
                        (group, type(data))
                    )
                for var, value in data.items():
                    self.inventory.set_variable(group, var, value)

            elif key == 'children':
                if not isinstance(data, MutableSequence):
                    raise AssibleParserError(
                        'Invalid "children" entry for "%s" group, requires a list, found "%s" instead.' %
                        (group, type(data))
                    )
                for subgroup in data:
                    self._parse_group(subgroup, {})
                    self.inventory.add_child(group, subgroup)

            elif key == 'hosts':
                if not isinstance(data, MutableMapping):
                    raise AssibleParserError(
                        'Invalid "hosts" entry for "%s" group, requires a dict, found "%s" instead.' %
                        (group, type(data))
                    )
                for host_pattern, value in data.items():
                    hosts, port = self._expand_hostpattern(host_pattern)
                    self._populate_host_vars(hosts, value, group, port)
            else:
                self.display.warning(
                    'Skipping unexpected key "%s" in group "%s", only "vars", "children" and "hosts" are valid' %
                    (key, group)
                )
Exemple #23
0
    def _populate_host_vars(self, hosts, variables, group=None, port=None):
        if not isinstance(variables, Mapping):
            raise AssibleParserError(
                "Invalid data from file, expected dictionary and got:\n\n%s" %
                to_native(variables))

        for host in hosts:
            self.inventory.add_host(host, group=group, port=port)
            for k in variables:
                self.inventory.set_variable(host, k, variables[k])
Exemple #24
0
 def _load_vars_prompt(self, attr, ds):
     new_ds = preprocess_vars(ds)
     vars_prompts = []
     if new_ds is not None:
         for prompt_data in new_ds:
             if 'name' not in prompt_data:
                 raise AssibleParserError(
                     "Invalid vars_prompt data structure, missing 'name' key",
                     obj=ds)
             for key in prompt_data:
                 if key not in ('name', 'prompt', 'default', 'private',
                                'confirm', 'encrypt', 'salt_size', 'salt',
                                'unsafe'):
                     raise AssibleParserError(
                         "Invalid vars_prompt data structure, found unsupported key '%s'"
                         % key,
                         obj=ds)
             vars_prompts.append(prompt_data)
     return vars_prompts
Exemple #25
0
    def _read_config_data(self, path):
        ''' validate config and set options as appropriate
            :arg path: path to common yaml format config file for this plugin
        '''

        config = {}
        try:
            # avoid loader cache so meta: refresh_inventory can pick up config changes
            # if we read more than once, fs cache should be good enough
            config = self.loader.load_from_file(path, cache=False)
        except Exception as e:
            raise AssibleParserError(to_native(e))

        # a plugin can be loaded via many different names with redirection- if so, we want to accept any of those names
        valid_names = getattr(self, '_redirected_names') or [self.NAME]

        if not config:
            # no data
            raise AssibleParserError("%s is empty" % (to_native(path)))
        elif config.get('plugin') not in valid_names:
            # this is not my config file
            raise AssibleParserError("Incorrect plugin name in file: %s" %
                                     config.get('plugin', 'none found'))
        elif not isinstance(config, Mapping):
            # configs are dictionaries
            raise AssibleParserError(
                'inventory source has invalid structure, it should be a dictionary, got: %s'
                % type(config))

        self.set_options(direct=config, var_options=self._vars)
        if 'cache' in self._options and self.get_option('cache'):
            cache_option_keys = [('_uri', 'cache_connection'),
                                 ('_timeout', 'cache_timeout'),
                                 ('_prefix', 'cache_prefix')]
            cache_options = dict((opt[0], self.get_option(opt[1]))
                                 for opt in cache_option_keys
                                 if self.get_option(opt[1]) is not None)
            self._cache = get_cache_plugin(self.get_option('cache_plugin'),
                                           **cache_options)

        return config
Exemple #26
0
    def _expand_hostpattern(self, hostpattern):
        '''
        do some extra checks over normal processing
        '''
        # specification?

        hostnames, port = super(InventoryModule,
                                self)._expand_hostpattern(hostpattern)

        if hostpattern.strip().endswith(':') and port is None:
            raise AssibleParserError(
                "Invalid host pattern '%s' supplied, ending in ':' is not allowed, this character is reserved to provide a port."
                % hostpattern)
        for pattern in hostnames:
            # some YAML parsing prevention checks
            if pattern.strip() == '---':
                raise AssibleParserError(
                    "Invalid host pattern '%s' supplied, '---' is normally a sign this is a YAML file."
                    % hostpattern)

        return (hostnames, port)
Exemple #27
0
    def parse(self, inventory, loader, path, cache=True):
        ''' parses the inventory file '''
        if not HAS_TOML:
            raise AssibleParserError(
                'The TOML inventory plugin requires the python "toml" library'
            )

        super(InventoryModule, self).parse(inventory, loader, path)
        self.set_options()

        try:
            data = self._load_file(path)
        except Exception as e:
            raise AssibleParserError(e)

        if not data:
            raise AssibleParserError('Parsed empty TOML file')
        elif data.get('plugin'):
            raise AssibleParserError('Plugin configuration TOML file, not TOML inventory')

        for group_name in data:
            self._parse_group(group_name, data[group_name])
    def check_requirements(self):
        """ Check all requirements for this inventory are satisified"""
        if not HAS_REQUESTS:
            raise AssibleParserError(
                'Please install "requests" Python module as this is required'
                ' for VMware Guest dynamic inventory plugin.')
        elif not HAS_PYVMOMI:
            raise AssibleParserError(
                'Please install "PyVmomi" Python module as this is required'
                ' for VMware Guest dynamic inventory plugin.')
        if HAS_REQUESTS:
            # Pyvmomi 5.5 and onwards requires requests 2.3
            # https://github.com/vmware/pyvmomi/blob/master/requirements.txt
            required_version = (2, 3)
            requests_version = requests.__version__.split(".")[:2]
            try:
                requests_major_minor = tuple(map(int, requests_version))
            except ValueError:
                raise AssibleParserError(
                    "Failed to parse 'requests' library version.")

            if requests_major_minor < required_version:
                raise AssibleParserError(
                    "'requests' library version should"
                    " be >= %s, found: %s." % (".".join(
                        [str(w)
                         for w in required_version]), requests.__version__))

        if not HAS_VSPHERE and self.with_tags:
            raise AssibleError(
                "Unable to find 'vSphere Automation SDK' Python library which is required."
                " Please refer this URL for installation steps"
                " - https://code.vmware.com/web/sdk/65/vsphere-automation-python"
            )

        if not all([self.hostname, self.username, self.password]):
            raise AssibleError(
                "Missing one of the following : hostname, username, password. Please read "
                "the documentation for more information.")
Exemple #29
0
    def load(data, owner, variable_manager=None, loader=None):
        '''
        Returns a new RoleMetadata object based on the datastructure passed in.
        '''

        if not isinstance(data, dict):
            raise AssibleParserError(
                "the 'meta/main.yml' for role %s is not a dictionary" %
                owner.get_name())

        m = RoleMetadata(owner=owner).load_data(
            data, variable_manager=variable_manager, loader=loader)
        return m
Exemple #30
0
    def parse(self, inventory, loader, path, cache=False):
        ''' parses the inventory file '''

        super(InventoryModule, self).parse(inventory,
                                           loader,
                                           path,
                                           cache=cache)

        self._read_config_data(path)

        strict = self.get_option('strict')
        fact_cache = FactCache()
        try:
            # Go over hosts (less var copies)
            for host in inventory.hosts:

                # get available variables to templar
                hostvars = combine_vars(
                    get_group_vars(inventory.hosts[host].get_groups()),
                    inventory.hosts[host].get_vars())
                if host in fact_cache:  # adds facts if cache is active
                    hostvars = combine_vars(hostvars, fact_cache[host])

                # create composite vars
                self._set_composite_vars(self.get_option('compose'),
                                         hostvars,
                                         host,
                                         strict=strict)

                # refetch host vars in case new ones have been created above
                hostvars = combine_vars(
                    get_group_vars(inventory.hosts[host].get_groups()),
                    inventory.hosts[host].get_vars())
                if host in self._cache:  # adds facts if cache is active
                    hostvars = combine_vars(hostvars, self._cache[host])

                # constructed groups based on conditionals
                self._add_host_to_composed_groups(self.get_option('groups'),
                                                  hostvars,
                                                  host,
                                                  strict=strict)

                # constructed groups based variable values
                self._add_host_to_keyed_groups(self.get_option('keyed_groups'),
                                               hostvars,
                                               host,
                                               strict=strict)

        except Exception as e:
            raise AssibleParserError("failed to parse %s: %s " %
                                     (to_native(path), to_native(e)))