示例#1
0
    def run(self, tmp=None, task_vars=None):
        if task_vars is None:
            task_vars = dict()

        result = super(ActionModule, self).run(tmp, task_vars)
        del tmp  # tmp no longer has any effect

        facts = dict()

        cacheable = boolean(self._task.args.pop('cacheable', False))

        if self._task.args:
            for (k, v) in iteritems(self._task.args):
                k = self._templar.template(k)

                if not isidentifier(k):
                    result['failed'] = True
                    result['msg'] = (
                        "The variable name '%s' is not valid. Variables must start with a letter or underscore character, and contain only "
                        "letters, numbers and underscores." % k)
                    return result

                if not C.DEFAULT_JINJA2_NATIVE and isinstance(
                        v, string_types) and v.lower() in ('true', 'false',
                                                           'yes', 'no'):
                    v = boolean(v, strict=False)
                facts[k] = v

        result['changed'] = False
        result['assible_facts'] = facts
        result['_assible_facts_cacheable'] = cacheable
        return result
示例#2
0
    def test_junk_values_strict(self):
        with pytest.raises(TypeError):
            assert boolean("flibbity", strict=True) is False

        with pytest.raises(TypeError):
            assert boolean(42, strict=True) is False

        with pytest.raises(TypeError):
            assert boolean(42.0, strict=True) is False

        with pytest.raises(TypeError):
            assert boolean(object(), strict=True) is False
示例#3
0
 def do_diff(self, arg):
     """Toggle whether plays run with diff"""
     if arg:
         self.diff = boolean(arg, strict=False)
         display.v("diff mode changed to %s" % self.diff)
     else:
         display.display("Please specify a diff value , e.g. `diff yes`")
示例#4
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)
示例#5
0
 def do_check(self, arg):
     """Toggle whether plays run with check mode"""
     if arg:
         self.check_mode = boolean(arg, strict=False)
         display.v("check mode changed to %s" % self.check_mode)
     else:
         display.display(
             "Please specify check mode value, e.g. `check yes`")
示例#6
0
 def do_become(self, arg):
     """Toggle whether plays run with become"""
     if arg:
         self.become = boolean(arg, strict=False)
         display.v("become changed to %s" % self.become)
         self.set_prompt()
     else:
         display.display("Please specify become value, e.g. `become yes`")
示例#7
0
 def test_strings(self):
     assert boolean("true") is True
     assert boolean("TRUE") is True
     assert boolean("t") is True
     assert boolean("yes") is True
     assert boolean("y") is True
     assert boolean("on") is True
示例#8
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
示例#9
0
    def run(self, tmp=None, task_vars=None):
        if task_vars is None:
            task_vars = dict()

        result = super(ActionModule, self).run(tmp, task_vars)
        del tmp  # tmp no longer has any effect

        stats = {'data': {}, 'per_host': False, 'aggregate': True}

        if self._task.args:
            data = self._task.args.get('data', {})

            if not isinstance(data, dict):
                data = self._templar.template(data,
                                              convert_bare=False,
                                              fail_on_undefined=True)

            if not isinstance(data, dict):
                result['failed'] = True
                result[
                    'msg'] = "The 'data' option needs to be a dictionary/hash"
                return result

            # set boolean options, defaults are set above in stats init
            for opt in ['per_host', 'aggregate']:
                val = self._task.args.get(opt, None)
                if val is not None:
                    if not isinstance(val, bool):
                        stats[opt] = boolean(self._templar.template(val),
                                             strict=False)
                    else:
                        stats[opt] = val

            for (k, v) in iteritems(data):

                k = self._templar.template(k)

                if not isidentifier(k):
                    result['failed'] = True
                    result['msg'] = (
                        "The variable name '%s' is not valid. Variables must start with a letter or underscore character, and contain only "
                        "letters, numbers and underscores." % k)
                    return result

                stats['data'][k] = self._templar.template(v)

        result['changed'] = False
        result['assible_stats'] = stats

        return result
示例#10
0
def load_provider(spec, args):
    provider = args.get("provider") or {}
    for key, value in iteritems(spec):
        if key not in provider:
            if "fallback" in value:
                provider[key] = _fallback(value["fallback"])
            elif "default" in value:
                provider[key] = value["default"]
            else:
                provider[key] = None
    if "authorize" in provider:
        # Coerce authorize to provider if a string has somehow snuck in.
        provider["authorize"] = boolean(provider["authorize"] or False)
    args["provider"] = provider
    return provider
示例#11
0
def load_provider(spec, args):
    provider = args.get('provider') or {}
    for key, value in iteritems(spec):
        if key not in provider:
            if 'fallback' in value:
                provider[key] = _fallback(value['fallback'])
            elif 'default' in value:
                provider[key] = value['default']
            else:
                provider[key] = None
    if 'authorize' in provider:
        # Coerce authorize to provider if a string has somehow snuck in.
        provider['authorize'] = boolean(provider['authorize'] or False)
    args['provider'] = provider
    return provider
示例#12
0
def truthy(value, convert_bool=False):
    """Evaluate as value for truthiness using python ``bool``

    Optionally, attempt to do a conversion to bool from boolean like values
    such as ``"false"``, ``"true"``, ``"yes"``, ``"no"``, ``"on"``, ``"off"``, etc.

    .. versionadded:: 2.10
    """
    if convert_bool:
        try:
            value = boolean(value)
        except TypeError:
            pass

    return bool(value)
示例#13
0
def check_type_bool(value):
    """Verify that the value is a bool or convert it to a bool and return it.

    Raises TypeError if unable to convert to a bool

    :arg value: String, int, or float to convert to bool. Valid booleans include:
         '1', 'on', 1, '0', 0, 'n', 'f', 'false', 'true', 'y', 't', 'yes', 'no', 'off'

    :returns: Boolean True or False
    """
    if isinstance(value, bool):
        return value

    if isinstance(value, string_types) or isinstance(value, (int, float)):
        return boolean(value)

    raise TypeError('%s cannot be converted to a bool' % type(value))
示例#14
0
    def run(self, terms, variables, **kwargs):
        def _raise_terms_error(msg=""):
            raise AssibleError(
                "subelements lookup expects a list of two or three items, " +
                msg)

        terms[0] = listify_lookup_plugin_terms(terms[0],
                                               templar=self._templar,
                                               loader=self._loader)

        # check lookup terms - check number of terms
        if not isinstance(terms, list) or not 2 <= len(terms) <= 3:
            _raise_terms_error()

        # first term should be a list (or dict), second a string holding the subkey
        if not isinstance(terms[0], (list, dict)) or not isinstance(
                terms[1], string_types):
            _raise_terms_error(
                "first a dict or a list, second a string pointing to the subkey"
            )
        subelements = terms[1].split(".")

        if isinstance(terms[0], dict):  # convert to list:
            if terms[0].get('skipped', False) is not False:
                # the registered result was completely skipped
                return []
            elementlist = []
            for key in terms[0]:
                elementlist.append(terms[0][key])
        else:
            elementlist = terms[0]

        # check for optional flags in third term
        flags = {}
        if len(terms) == 3:
            flags = terms[2]
        if not isinstance(flags, dict) and not all(
            [isinstance(key, string_types) and key in FLAGS for key in flags]):
            _raise_terms_error(
                "the optional third item must be a dict with flags %s" % FLAGS)

        # build_items
        ret = []
        for item0 in elementlist:
            if not isinstance(item0, dict):
                raise AssibleError(
                    "subelements lookup expects a dictionary, got '%s'" %
                    item0)
            if item0.get('skipped', False) is not False:
                # this particular item is to be skipped
                continue

            skip_missing = boolean(flags.get('skip_missing', False),
                                   strict=False)
            subvalue = item0
            lastsubkey = False
            sublist = []
            for subkey in subelements:
                if subkey == subelements[-1]:
                    lastsubkey = True
                if subkey not in subvalue:
                    if skip_missing:
                        continue
                    else:
                        raise AssibleError(
                            "could not find '%s' key in iterated item '%s'" %
                            (subkey, subvalue))
                if not lastsubkey:
                    if not isinstance(subvalue[subkey], dict):
                        if skip_missing:
                            continue
                        else:
                            raise AssibleError(
                                "the key %s should point to a dictionary, got '%s'"
                                % (subkey, subvalue[subkey]))
                    else:
                        subvalue = subvalue[subkey]
                else:  # lastsubkey
                    if not isinstance(subvalue[subkey], list):
                        raise AssibleError(
                            "the key %s should point to a list, got '%s'" %
                            (subkey, subvalue[subkey]))
                    else:
                        sublist = subvalue.pop(subkey, [])
            for item1 in sublist:
                ret.append((item0, item1))

        return ret
示例#15
0
 def test_junk_values_nonstrict(self):
     assert boolean("flibbity", strict=False) is False
     assert boolean(42, strict=False) is False
     assert boolean(42.0, strict=False) is False
     assert boolean(object(), strict=False) is False
示例#16
0
 def test_numbers(self):
     assert boolean(1) is True
     assert boolean(0) is False
     assert boolean(0.0) is False
示例#17
0
 def test_none(self):
     with pytest.raises(TypeError):
         assert boolean(None, strict=True) is False
     assert boolean(None, strict=False) is False
示例#18
0
 def test_bools(self):
     assert boolean(True) is True
     assert boolean(False) is False
示例#19
0
    def run(self, tmp=None, task_vars=None):
        ''' handler for file transfer operations '''
        if task_vars is None:
            task_vars = dict()

        result = super(ActionModule, self).run(tmp, task_vars)
        del tmp  # tmp no longer has any effect

        source = self._task.args.get('src', None)
        content = self._task.args.get('content', None)
        dest = self._task.args.get('dest', None)
        remote_src = boolean(self._task.args.get('remote_src', False),
                             strict=False)
        local_follow = boolean(self._task.args.get('local_follow', True),
                               strict=False)

        result['failed'] = True
        if not source and content is None:
            result['msg'] = 'src (or content) is required'
        elif not dest:
            result['msg'] = 'dest is required'
        elif source and content is not None:
            result['msg'] = 'src and content are mutually exclusive'
        elif content is not None and dest is not None and dest.endswith("/"):
            result['msg'] = "can not use content with a dir as dest"
        else:
            del result['failed']

        if result.get('failed'):
            return self._ensure_invocation(result)

        # Define content_tempfile in case we set it after finding content populated.
        content_tempfile = None

        # If content is defined make a tmp file and write the content into it.
        if content is not None:
            try:
                # If content comes to us as a dict it should be decoded json.
                # We need to encode it back into a string to write it out.
                if isinstance(content, dict) or isinstance(content, list):
                    content_tempfile = self._create_content_tempfile(
                        json.dumps(content))
                else:
                    content_tempfile = self._create_content_tempfile(content)
                source = content_tempfile
            except Exception as err:
                result['failed'] = True
                result[
                    'msg'] = "could not write content temp file: %s" % to_native(
                        err)
                return self._ensure_invocation(result)

        # if we have first_available_file in our vars
        # look up the files and use the first one we find as src
        elif remote_src:
            result.update(
                self._execute_module(module_name='assible.legacy.copy',
                                     task_vars=task_vars))
            return self._ensure_invocation(result)
        else:
            # find_needle returns a path that may not have a trailing slash on
            # a directory so we need to determine that now (we use it just
            # like rsync does to figure out whether to include the directory
            # or only the files inside the directory
            trailing_slash = source.endswith(os.path.sep)
            try:
                # find in expected paths
                source = self._find_needle('files', source)
            except AssibleError as e:
                result['failed'] = True
                result['msg'] = to_text(e)
                result['exception'] = traceback.format_exc()
                return self._ensure_invocation(result)

            if trailing_slash != source.endswith(os.path.sep):
                if source[-1] == os.path.sep:
                    source = source[:-1]
                else:
                    source = source + os.path.sep

        # A list of source file tuples (full_path, relative_path) which will try to copy to the destination
        source_files = {'files': [], 'directories': [], 'symlinks': []}

        # If source is a directory populate our list else source is a file and translate it to a tuple.
        if os.path.isdir(to_bytes(source, errors='surrogate_or_strict')):
            # Get a list of the files we want to replicate on the remote side
            source_files = _walk_dirs(source,
                                      local_follow=local_follow,
                                      trailing_slash_detector=self._connection.
                                      _shell.path_has_trailing_slash)

            # If it's recursive copy, destination is always a dir,
            # explicitly mark it so (note - copy module relies on this).
            if not self._connection._shell.path_has_trailing_slash(dest):
                dest = self._connection._shell.join_path(dest, '')
            # FIXME: Can we optimize cases where there's only one file, no
            # symlinks and any number of directories?  In the original code,
            # empty directories are not copied....
        else:
            source_files['files'] = [(source, os.path.basename(source))]

        changed = False
        module_return = dict(changed=False)

        # A register for if we executed a module.
        # Used to cut down on command calls when not recursive.
        module_executed = False

        # expand any user home dir specifier
        dest = self._remote_expand_user(dest)

        implicit_directories = set()
        for source_full, source_rel in source_files['files']:
            # copy files over.  This happens first as directories that have
            # a file do not need to be created later

            # We only follow symlinks for files in the non-recursive case
            if source_files['directories']:
                follow = False
            else:
                follow = boolean(self._task.args.get('follow', False),
                                 strict=False)

            module_return = self._copy_file(source_full, source_rel, content,
                                            content_tempfile, dest, task_vars,
                                            follow)
            if module_return is None:
                continue

            if module_return.get('failed'):
                result.update(module_return)
                return self._ensure_invocation(result)

            paths = os.path.split(source_rel)
            dir_path = ''
            for dir_component in paths:
                os.path.join(dir_path, dir_component)
                implicit_directories.add(dir_path)
            if 'diff' in result and not result['diff']:
                del result['diff']
            module_executed = True
            changed = changed or module_return.get('changed', False)

        for src, dest_path in source_files['directories']:
            # Find directories that are leaves as they might not have been
            # created yet.
            if dest_path in implicit_directories:
                continue

            # Use file module to create these
            new_module_args = _create_remote_file_args(self._task.args)
            new_module_args['path'] = os.path.join(dest, dest_path)
            new_module_args['state'] = 'directory'
            new_module_args['mode'] = self._task.args.get(
                'directory_mode', None)
            new_module_args['recurse'] = False
            del new_module_args['src']

            module_return = self._execute_module(
                module_name='assible.legacy.file',
                module_args=new_module_args,
                task_vars=task_vars)

            if module_return.get('failed'):
                result.update(module_return)
                return self._ensure_invocation(result)

            module_executed = True
            changed = changed or module_return.get('changed', False)

        for target_path, dest_path in source_files['symlinks']:
            # Copy symlinks over
            new_module_args = _create_remote_file_args(self._task.args)
            new_module_args['path'] = os.path.join(dest, dest_path)
            new_module_args['src'] = target_path
            new_module_args['state'] = 'link'
            new_module_args['force'] = True

            # Only follow remote symlinks in the non-recursive case
            if source_files['directories']:
                new_module_args['follow'] = False

            # file module cannot deal with 'preserve' mode and is meaningless
            # for symlinks anyway, so just don't pass it.
            if new_module_args.get('mode', None) == 'preserve':
                new_module_args.pop('mode')

            module_return = self._execute_module(
                module_name='assible.legacy.file',
                module_args=new_module_args,
                task_vars=task_vars)
            module_executed = True

            if module_return.get('failed'):
                result.update(module_return)
                return self._ensure_invocation(result)

            changed = changed or module_return.get('changed', False)

        if module_executed and len(source_files['files']) == 1:
            result.update(module_return)

            # the file module returns the file path as 'path', but
            # the copy module uses 'dest', so add it if it's not there
            if 'path' in result and 'dest' not in result:
                result['dest'] = result['path']
        else:
            result.update(dict(dest=dest, src=source, changed=changed))

        # Delete tmp path
        self._remove_tmp_path(self._connection._shell.tmpdir)

        return self._ensure_invocation(result)
示例#20
0
    def run(self, tmp=None, task_vars=None):
        ''' handler for file transfer operations '''
        if task_vars is None:
            task_vars = dict()

        result = super(ActionModule, self).run(tmp, task_vars)
        del tmp  # tmp no longer has any effect

        source = self._task.args.get('src', None)
        content = self._task.args.get('content', None)
        dest = self._task.args.get('dest', None)
        remote_src = boolean(self._task.args.get('remote_src', False),
                             strict=False)
        local_follow = boolean(self._task.args.get('local_follow', False),
                               strict=False)
        force = boolean(self._task.args.get('force', True), strict=False)
        decrypt = boolean(self._task.args.get('decrypt', True), strict=False)
        backup = boolean(self._task.args.get('backup', False), strict=False)

        result['src'] = source
        result['dest'] = dest

        result['failed'] = True
        if (source is None and content is None) or dest is None:
            result['msg'] = "src (or content) and dest are required"
        elif source is not None and content is not None:
            result['msg'] = "src and content are mutually exclusive"
        elif content is not None and dest is not None and (dest.endswith(
                os.path.sep) or dest.endswith(self.WIN_PATH_SEPARATOR)):
            result['msg'] = "dest must be a file if content is defined"
        else:
            del result['failed']

        if result.get('failed'):
            return result

        # If content is defined make a temp file and write the content into it
        content_tempfile = None
        if content is not None:
            try:
                # if content comes to us as a dict it should be decoded json.
                # We need to encode it back into a string and write it out
                if isinstance(content, dict) or isinstance(content, list):
                    content_tempfile = self._create_content_tempfile(
                        json.dumps(content))
                else:
                    content_tempfile = self._create_content_tempfile(content)
                source = content_tempfile
            except Exception as err:
                result['failed'] = True
                result[
                    'msg'] = "could not write content tmp file: %s" % to_native(
                        err)
                return result
        # all actions should occur on the remote server, run win_copy module
        elif remote_src:
            new_module_args = self._task.args.copy()
            new_module_args.update(
                dict(
                    _copy_mode="remote",
                    dest=dest,
                    src=source,
                    force=force,
                    backup=backup,
                ))
            new_module_args.pop('content', None)
            result.update(
                self._execute_module(module_args=new_module_args,
                                     task_vars=task_vars))
            return result
        # find_needle returns a path that may not have a trailing slash on a
        # directory so we need to find that out first and append at the end
        else:
            trailing_slash = source.endswith(os.path.sep)
            try:
                # find in expected paths
                source = self._find_needle('files', source)
            except AssibleError as e:
                result['failed'] = True
                result['msg'] = to_text(e)
                result['exception'] = traceback.format_exc()
                return result

            if trailing_slash != source.endswith(os.path.sep):
                if source[-1] == os.path.sep:
                    source = source[:-1]
                else:
                    source = source + os.path.sep

        # A list of source file tuples (full_path, relative_path) which will try to copy to the destination
        source_files = {'files': [], 'directories': [], 'symlinks': []}

        # If source is a directory populate our list else source is a file and translate it to a tuple.
        if os.path.isdir(to_bytes(source, errors='surrogate_or_strict')):
            result['operation'] = 'folder_copy'

            # Get a list of the files we want to replicate on the remote side
            source_files = _walk_dirs(source,
                                      self._loader,
                                      decrypt=decrypt,
                                      local_follow=local_follow,
                                      trailing_slash_detector=self._connection.
                                      _shell.path_has_trailing_slash,
                                      checksum_check=force)

            # If it's recursive copy, destination is always a dir,
            # explicitly mark it so (note - win_copy module relies on this).
            if not self._connection._shell.path_has_trailing_slash(dest):
                dest = "%s%s" % (dest, self.WIN_PATH_SEPARATOR)

            check_dest = dest
        # Source is a file, add details to source_files dict
        else:
            result['operation'] = 'file_copy'

            # If the local file does not exist, get_real_file() raises AssibleFileNotFound
            try:
                source_full = self._loader.get_real_file(source,
                                                         decrypt=decrypt)
            except AssibleFileNotFound as e:
                result['failed'] = True
                result['msg'] = "could not find src=%s, %s" % (source_full,
                                                               to_text(e))
                return result

            original_basename = os.path.basename(source)
            result['original_basename'] = original_basename

            # check if dest ends with / or \ and append source filename to dest
            if self._connection._shell.path_has_trailing_slash(dest):
                check_dest = dest
                filename = original_basename
                result['dest'] = self._connection._shell.join_path(
                    dest, filename)
            else:
                # replace \\ with / so we can use os.path to get the filename or dirname
                unix_path = dest.replace(self.WIN_PATH_SEPARATOR, os.path.sep)
                filename = os.path.basename(unix_path)
                check_dest = os.path.dirname(unix_path)

            file_checksum = _get_local_checksum(force, source_full)
            source_files['files'].append(
                dict(src=source_full, dest=filename, checksum=file_checksum))
            result['checksum'] = file_checksum
            result['size'] = os.path.getsize(
                to_bytes(source_full, errors='surrogate_or_strict'))

        # find out the files/directories/symlinks that we need to copy to the server
        query_args = self._task.args.copy()
        query_args.update(
            dict(
                _copy_mode="query",
                dest=check_dest,
                force=force,
                files=source_files['files'],
                directories=source_files['directories'],
                symlinks=source_files['symlinks'],
            ))
        # src is not required for query, will fail path validation is src has unix allowed chars
        query_args.pop('src', None)

        query_args.pop('content', None)
        query_return = self._execute_module(module_args=query_args,
                                            task_vars=task_vars)

        if query_return.get('failed') is True:
            result.update(query_return)
            return result

        if len(query_return['files']) > 0 or len(query_return[
                'directories']) > 0 and self._connection._shell.tmpdir is None:
            self._connection._shell.tmpdir = self._make_tmp_path()

        if len(query_return['files']) == 1 and len(
                query_return['directories']) == 0:
            # we only need to copy 1 file, don't mess around with zips
            file_src = query_return['files'][0]['src']
            file_dest = query_return['files'][0]['dest']
            result.update(
                self._copy_single_file(file_src, dest, file_dest, task_vars,
                                       self._connection._shell.tmpdir, backup))
            if result.get('failed') is True:
                result['msg'] = "failed to copy file %s: %s" % (file_src,
                                                                result['msg'])
            result['changed'] = True

        elif len(query_return['files']) > 0 or len(
                query_return['directories']) > 0:
            # either multiple files or directories need to be copied, compress
            # to a zip and 'explode' the zip on the server
            # TODO: handle symlinks
            result.update(
                self._copy_zip_file(dest, source_files['files'],
                                    source_files['directories'], task_vars,
                                    self._connection._shell.tmpdir, backup))
            result['changed'] = True
        else:
            # no operations need to occur
            result['failed'] = False
            result['changed'] = False

        # remove the content tmp file and remote tmp file if it was created
        self._remove_tempfile_if_content_defined(content, content_tempfile)
        self._remove_tmp_path(self._connection._shell.tmpdir)
        return result
示例#21
0
    def run(self, tmp=None, task_vars=None):
        ''' handler for fetch operations '''
        if task_vars is None:
            task_vars = dict()

        result = super(ActionModule, self).run(tmp, task_vars)
        del tmp  # tmp no longer has any effect

        try:
            if self._play_context.check_mode:
                raise AssibleActionSkip(
                    'check mode not (yet) supported for this module')

            source = self._task.args.get('src', None)
            original_dest = dest = self._task.args.get('dest', None)
            flat = boolean(self._task.args.get('flat'), strict=False)
            fail_on_missing = boolean(self._task.args.get(
                'fail_on_missing', True),
                                      strict=False)
            validate_checksum = boolean(self._task.args.get(
                'validate_checksum', True),
                                        strict=False)

            msg = ''
            # validate source and dest are strings FIXME: use basic.py and module specs
            if not isinstance(source, string_types):
                msg = "Invalid type supplied for source option, it must be a string"

            if not isinstance(dest, string_types):
                msg = "Invalid type supplied for dest option, it must be a string"

            if source is None or dest is None:
                msg = "src and dest are required"

            if msg:
                raise AssibleActionFail(msg)

            source = self._connection._shell.join_path(source)
            source = self._remote_expand_user(source)

            remote_checksum = None
            if not self._connection.become:
                # calculate checksum for the remote file, don't bother if using become as slurp will be used
                # Force remote_checksum to follow symlinks because fetch always follows symlinks
                remote_checksum = self._remote_checksum(source,
                                                        all_vars=task_vars,
                                                        follow=True)

            # use slurp if permissions are lacking or privilege escalation is needed
            remote_data = None
            if remote_checksum in ('1', '2', None):
                slurpres = self._execute_module(
                    module_name='assible.legacy.slurp',
                    module_args=dict(src=source),
                    task_vars=task_vars)
                if slurpres.get('failed'):
                    if not fail_on_missing and (
                            slurpres.get('msg').startswith('file not found')
                            or remote_checksum == '1'):
                        result[
                            'msg'] = "the remote file does not exist, not transferring, ignored"
                        result['file'] = source
                        result['changed'] = False
                    else:
                        result.update(slurpres)
                    return result
                else:
                    if slurpres['encoding'] == 'base64':
                        remote_data = base64.b64decode(slurpres['content'])
                    if remote_data is not None:
                        remote_checksum = checksum_s(remote_data)

            # calculate the destination name
            if os.path.sep not in self._connection._shell.join_path('a', ''):
                source = self._connection._shell._unquote(source)
                source_local = source.replace('\\', '/')
            else:
                source_local = source

            # ensure we only use file name, avoid relative paths
            if not is_subpath(dest, original_dest):
                # TODO: ? dest = os.path.expanduser(dest.replace(('../','')))
                raise AssibleActionFail(
                    "Detected directory traversal, expected to be contained in '%s' but got '%s'"
                    % (original_dest, dest))

            if flat:
                if os.path.isdir(to_bytes(dest, errors='surrogate_or_strict')
                                 ) and not dest.endswith(os.sep):
                    raise AssibleActionFail(
                        "dest is an existing directory, use a trailing slash if you want to fetch src into that directory"
                    )
                if dest.endswith(os.sep):
                    # if the path ends with "/", we'll use the source filename as the
                    # destination filename
                    base = os.path.basename(source_local)
                    dest = os.path.join(dest, base)
                if not dest.startswith("/"):
                    # if dest does not start with "/", we'll assume a relative path
                    dest = self._loader.path_dwim(dest)
            else:
                # files are saved in dest dir, with a subdir for each host, then the filename
                if 'inventory_hostname' in task_vars:
                    target_name = task_vars['inventory_hostname']
                else:
                    target_name = self._play_context.remote_addr
                dest = "%s/%s/%s" % (self._loader.path_dwim(dest), target_name,
                                     source_local)

            if remote_checksum in ('0', '1', '2', '3', '4', '5'):
                result['changed'] = False
                result['file'] = source
                if remote_checksum == '0':
                    result[
                        'msg'] = "unable to calculate the checksum of the remote file"
                elif remote_checksum == '1':
                    result['msg'] = "the remote file does not exist"
                elif remote_checksum == '2':
                    result['msg'] = "no read permission on remote file"
                elif remote_checksum == '3':
                    result[
                        'msg'] = "remote file is a directory, fetch cannot work on directories"
                elif remote_checksum == '4':
                    result[
                        'msg'] = "python isn't present on the system.  Unable to compute checksum"
                elif remote_checksum == '5':
                    result[
                        'msg'] = "stdlib json was not found on the remote machine. Only the raw module can work without those installed"
                # Historically, these don't fail because you may want to transfer
                # a log file that possibly MAY exist but keep going to fetch other
                # log files. Today, this is better achieved by adding
                # ignore_errors or failed_when to the task.  Control the behaviour
                # via fail_when_missing
                if fail_on_missing:
                    result['failed'] = True
                    del result['changed']
                else:
                    result['msg'] += ", not transferring, ignored"
                return result

            dest = os.path.normpath(dest)

            # calculate checksum for the local file
            local_checksum = checksum(dest)

            if remote_checksum != local_checksum:
                # create the containing directories, if needed
                makedirs_safe(os.path.dirname(dest))

                # fetch the file and check for changes
                if remote_data is None:
                    self._connection.fetch_file(source, dest)
                else:
                    try:
                        f = open(to_bytes(dest, errors='surrogate_or_strict'),
                                 'wb')
                        f.write(remote_data)
                        f.close()
                    except (IOError, OSError) as e:
                        raise AssibleActionFail(
                            "Failed to fetch the file: %s" % e)
                new_checksum = secure_hash(dest)
                # For backwards compatibility. We'll return None on FIPS enabled systems
                try:
                    new_md5 = md5(dest)
                except ValueError:
                    new_md5 = None

                if validate_checksum and new_checksum != remote_checksum:
                    result.update(
                        dict(failed=True,
                             md5sum=new_md5,
                             msg="checksum mismatch",
                             file=source,
                             dest=dest,
                             remote_md5sum=None,
                             checksum=new_checksum,
                             remote_checksum=remote_checksum))
                else:
                    result.update({
                        'changed': True,
                        'md5sum': new_md5,
                        'dest': dest,
                        'remote_md5sum': None,
                        'checksum': new_checksum,
                        'remote_checksum': remote_checksum
                    })
            else:
                # For backwards compatibility. We'll return None on FIPS enabled systems
                try:
                    local_md5 = md5(dest)
                except ValueError:
                    local_md5 = None
                result.update(
                    dict(changed=False,
                         md5sum=local_md5,
                         file=source,
                         dest=dest,
                         checksum=local_checksum))

        finally:
            self._remove_tmp_path(self._connection._shell.tmpdir)

        return result
示例#22
0
    def run(self, tmp=None, task_vars=None):
        ''' run the pause action module '''
        if task_vars is None:
            task_vars = dict()

        result = super(ActionModule, self).run(tmp, task_vars)
        del tmp  # tmp no longer has any effect

        duration_unit = 'minutes'
        prompt = None
        seconds = None
        echo = True
        echo_prompt = ''
        result.update(
            dict(changed=False,
                 rc=0,
                 stderr='',
                 stdout='',
                 start=None,
                 stop=None,
                 delta=None,
                 echo=echo))

        # Should keystrokes be echoed to stdout?
        if 'echo' in self._task.args:
            try:
                echo = boolean(self._task.args['echo'])
            except TypeError as e:
                result['failed'] = True
                result['msg'] = to_native(e)
                return result

            # Add a note saying the output is hidden if echo is disabled
            if not echo:
                echo_prompt = ' (output is hidden)'

        # Is 'prompt' a key in 'args'?
        if 'prompt' in self._task.args:
            prompt = "[%s]\n%s%s:" % (self._task.get_name().strip(),
                                      self._task.args['prompt'], echo_prompt)
        else:
            # If no custom prompt is specified, set a default prompt
            prompt = "[%s]\n%s%s:" % (self._task.get_name().strip(
            ), 'Press enter to continue, Ctrl+C to interrupt', echo_prompt)

        # Are 'minutes' or 'seconds' keys that exist in 'args'?
        if 'minutes' in self._task.args or 'seconds' in self._task.args:
            try:
                if 'minutes' in self._task.args:
                    # The time() command operates in seconds so we need to
                    # recalculate for minutes=X values.
                    seconds = int(self._task.args['minutes']) * 60
                else:
                    seconds = int(self._task.args['seconds'])
                    duration_unit = 'seconds'

            except ValueError as e:
                result['failed'] = True
                result[
                    'msg'] = u"non-integer value given for prompt duration:\n%s" % to_text(
                        e)
                return result

        ########################################################################
        # Begin the hard work!

        start = time.time()
        result['start'] = to_text(datetime.datetime.now())
        result['user_input'] = b''

        stdin_fd = None
        old_settings = None
        try:
            if seconds is not None:
                if seconds < 1:
                    seconds = 1

                # setup the alarm handler
                signal.signal(signal.SIGALRM, timeout_handler)
                signal.alarm(seconds)

                # show the timer and control prompts
                display.display("Pausing for %d seconds%s" %
                                (seconds, echo_prompt))
                display.display(
                    "(ctrl+C then 'C' = continue early, ctrl+C then 'A' = abort)\r"
                ),

                # show the prompt specified in the task
                if 'prompt' in self._task.args:
                    display.display(prompt)

            else:
                display.display(prompt)

            # save the attributes on the existing (duped) stdin so
            # that we can restore them later after we set raw mode
            stdin_fd = None
            stdout_fd = None
            try:
                if PY3:
                    stdin = self._connection._new_stdin.buffer
                    stdout = sys.stdout.buffer
                else:
                    stdin = self._connection._new_stdin
                    stdout = sys.stdout
                stdin_fd = stdin.fileno()
                stdout_fd = stdout.fileno()
            except (ValueError, AttributeError):
                # ValueError: someone is using a closed file descriptor as stdin
                # AttributeError: someone is using a null file descriptor as stdin on windoez
                stdin = None

            if stdin_fd is not None:
                if isatty(stdin_fd):
                    # grab actual Ctrl+C sequence
                    try:
                        intr = termios.tcgetattr(stdin_fd)[6][termios.VINTR]
                    except Exception:
                        # unsupported/not present, use default
                        intr = b'\x03'  # value for Ctrl+C

                    # get backspace sequences
                    try:
                        backspace = termios.tcgetattr(stdin_fd)[6][
                            termios.VERASE]
                    except Exception:
                        backspace = [b'\x7f', b'\x08']

                    old_settings = termios.tcgetattr(stdin_fd)
                    tty.setraw(stdin_fd)

                    # Only set stdout to raw mode if it is a TTY. This is needed when redirecting
                    # stdout to a file since a file cannot be set to raw mode.
                    if isatty(stdout_fd):
                        tty.setraw(stdout_fd)

                    # Only echo input if no timeout is specified
                    if not seconds and echo:
                        new_settings = termios.tcgetattr(stdin_fd)
                        new_settings[3] = new_settings[3] | termios.ECHO
                        termios.tcsetattr(stdin_fd, termios.TCSANOW,
                                          new_settings)

                    # flush the buffer to make sure no previous key presses
                    # are read in below
                    termios.tcflush(stdin, termios.TCIFLUSH)

            while True:

                try:
                    if stdin_fd is not None:

                        key_pressed = stdin.read(1)

                        if key_pressed == intr:  # value for Ctrl+C
                            clear_line(stdout)
                            raise KeyboardInterrupt

                    if not seconds:
                        if stdin_fd is None or not isatty(stdin_fd):
                            display.warning(
                                "Not waiting for response to prompt as stdin is not interactive"
                            )
                            break

                        # read key presses and act accordingly
                        if key_pressed in (b'\r', b'\n'):
                            clear_line(stdout)
                            break
                        elif key_pressed in backspace:
                            # delete a character if backspace is pressed
                            result['user_input'] = result['user_input'][:-1]
                            clear_line(stdout)
                            if echo:
                                stdout.write(result['user_input'])
                            stdout.flush()
                        else:
                            result['user_input'] += key_pressed

                except KeyboardInterrupt:
                    signal.alarm(0)
                    display.display(
                        "Press 'C' to continue the play or 'A' to abort \r"),
                    if self._c_or_a(stdin):
                        clear_line(stdout)
                        break

                    clear_line(stdout)

                    raise AssibleError('user requested abort!')

        except AssibleTimeoutExceeded:
            # this is the exception we expect when the alarm signal
            # fires, so we simply ignore it to move into the cleanup
            pass
        finally:
            # cleanup and save some information
            # restore the old settings for the duped stdin stdin_fd
            if not (None in (stdin_fd, old_settings)) and isatty(stdin_fd):
                termios.tcsetattr(stdin_fd, termios.TCSADRAIN, old_settings)

            duration = time.time() - start
            result['stop'] = to_text(datetime.datetime.now())
            result['delta'] = int(duration)

            if duration_unit == 'minutes':
                duration = round(duration / 60.0, 2)
            else:
                duration = round(duration, 2)
            result['stdout'] = "Paused for %s %s" % (duration, duration_unit)

        result['user_input'] = to_text(result['user_input'],
                                       errors='surrogate_or_strict')
        return result
示例#23
0
    def run(self, tmp=None, task_vars=None):

        self._supports_check_mode = False

        result = super(ActionModule, self).run(tmp, task_vars)
        del tmp  # tmp no longer has any effect

        if task_vars is None:
            task_vars = dict()

        src = self._task.args.get('src', None)
        dest = self._task.args.get('dest', None)
        delimiter = self._task.args.get('delimiter', None)
        remote_src = self._task.args.get('remote_src', 'yes')
        regexp = self._task.args.get('regexp', None)
        follow = self._task.args.get('follow', False)
        ignore_hidden = self._task.args.get('ignore_hidden', False)
        decrypt = self._task.args.pop('decrypt', True)

        try:
            if src is None or dest is None:
                raise AssibleActionFail("src and dest are required")

            if boolean(remote_src, strict=False):
                # call assemble via assible.legacy to allow library/ overrides of the module without collection search
                result.update(
                    self._execute_module(module_name='assible.legacy.assemble',
                                         task_vars=task_vars))
                raise _AssibleActionDone()
            else:
                try:
                    src = self._find_needle('files', src)
                except AssibleError as e:
                    raise AssibleActionFail(to_native(e))

            if not os.path.isdir(src):
                raise AssibleActionFail(u"Source (%s) is not a directory" %
                                        src)

            _re = None
            if regexp is not None:
                _re = re.compile(regexp)

            # Does all work assembling the file
            path = self._assemble_from_fragments(src, delimiter, _re,
                                                 ignore_hidden, decrypt)

            path_checksum = checksum_s(path)
            dest = self._remote_expand_user(dest)
            dest_stat = self._execute_remote_stat(dest,
                                                  all_vars=task_vars,
                                                  follow=follow)

            diff = {}

            # setup args for running modules
            new_module_args = self._task.args.copy()

            # clean assemble specific options
            for opt in [
                    'remote_src', 'regexp', 'delimiter', 'ignore_hidden',
                    'decrypt'
            ]:
                if opt in new_module_args:
                    del new_module_args[opt]
            new_module_args['dest'] = dest

            if path_checksum != dest_stat['checksum']:

                if self._play_context.diff:
                    diff = self._get_diff_data(dest, path, task_vars)

                remote_path = self._connection._shell.join_path(
                    self._connection._shell.tmpdir, 'src')
                xfered = self._transfer_file(path, remote_path)

                # fix file permissions when the copy is done as a different user
                self._fixup_perms2(
                    (self._connection._shell.tmpdir, remote_path))

                new_module_args.update(dict(src=xfered, ))

                res = self._execute_module(module_name='assible.legacy.copy',
                                           module_args=new_module_args,
                                           task_vars=task_vars)
                if diff:
                    res['diff'] = diff
                result.update(res)
            else:
                result.update(
                    self._execute_module(module_name='assible.legacy.file',
                                         module_args=new_module_args,
                                         task_vars=task_vars))

        except AssibleAction as e:
            result.update(e.result)
        finally:
            self._remove_tmp_path(self._connection._shell.tmpdir)

        return result
示例#24
0
    def _copy_file(self, source_full, source_rel, content, content_tempfile,
                   dest, task_vars, follow):
        decrypt = boolean(self._task.args.get('decrypt', True), strict=False)
        force = boolean(self._task.args.get('force', 'yes'), strict=False)
        raw = boolean(self._task.args.get('raw', 'no'), strict=False)

        result = {}
        result['diff'] = []

        # If the local file does not exist, get_real_file() raises AssibleFileNotFound
        try:
            source_full = self._loader.get_real_file(source_full,
                                                     decrypt=decrypt)
        except AssibleFileNotFound as e:
            result['failed'] = True
            result['msg'] = "could not find src=%s, %s" % (source_full,
                                                           to_text(e))
            return result

        # Get the local mode and set if user wanted it preserved
        # https://github.com/assible/assible-modules-core/issues/1124
        lmode = None
        if self._task.args.get('mode', None) == 'preserve':
            lmode = '0%03o' % stat.S_IMODE(os.stat(source_full).st_mode)

        # This is kind of optimization - if user told us destination is
        # dir, do path manipulation right away, otherwise we still check
        # for dest being a dir via remote call below.
        if self._connection._shell.path_has_trailing_slash(dest):
            dest_file = self._connection._shell.join_path(dest, source_rel)
        else:
            dest_file = dest

        # Attempt to get remote file info
        dest_status = self._execute_remote_stat(dest_file,
                                                all_vars=task_vars,
                                                follow=follow,
                                                checksum=force)

        if dest_status['exists'] and dest_status['isdir']:
            # The dest is a directory.
            if content is not None:
                # If source was defined as content remove the temporary file and fail out.
                self._remove_tempfile_if_content_defined(
                    content, content_tempfile)
                result['failed'] = True
                result['msg'] = "can not use content with a dir as dest"
                return result
            else:
                # Append the relative source location to the destination and get remote stats again
                dest_file = self._connection._shell.join_path(dest, source_rel)
                dest_status = self._execute_remote_stat(dest_file,
                                                        all_vars=task_vars,
                                                        follow=follow,
                                                        checksum=force)

        if dest_status['exists'] and not force:
            # remote_file exists so continue to next iteration.
            return None

        # Generate a hash of the local file.
        local_checksum = checksum(source_full)

        if local_checksum != dest_status['checksum']:
            # The checksums don't match and we will change or error out.

            if self._play_context.diff and not raw:
                result['diff'].append(
                    self._get_diff_data(dest_file, source_full, task_vars))

            if self._play_context.check_mode:
                self._remove_tempfile_if_content_defined(
                    content, content_tempfile)
                result['changed'] = True
                return result

            # Define a remote directory that we will copy the file to.
            tmp_src = self._connection._shell.join_path(
                self._connection._shell.tmpdir, 'source')

            remote_path = None

            if not raw:
                remote_path = self._transfer_file(source_full, tmp_src)
            else:
                self._transfer_file(source_full, dest_file)

            # We have copied the file remotely and no longer require our content_tempfile
            self._remove_tempfile_if_content_defined(content, content_tempfile)
            self._loader.cleanup_tmp_file(source_full)

            # FIXME: I don't think this is needed when PIPELINING=0 because the source is created
            # world readable.  Access to the directory itself is controlled via fixup_perms2() as
            # part of executing the module. Check that umask with scp/sftp/piped doesn't cause
            # a problem before acting on this idea. (This idea would save a round-trip)
            # fix file permissions when the copy is done as a different user
            if remote_path:
                self._fixup_perms2(
                    (self._connection._shell.tmpdir, remote_path))

            if raw:
                # Continue to next iteration if raw is defined.
                return None

            # Run the copy module

            # src and dest here come after original and override them
            # we pass dest only to make sure it includes trailing slash in case of recursive copy
            new_module_args = _create_remote_copy_args(self._task.args)
            new_module_args.update(
                dict(src=tmp_src,
                     dest=dest,
                     _original_basename=source_rel,
                     follow=follow))
            if not self._task.args.get('checksum'):
                new_module_args['checksum'] = local_checksum

            if lmode:
                new_module_args['mode'] = lmode

            module_return = self._execute_module(
                module_name='assible.legacy.copy',
                module_args=new_module_args,
                task_vars=task_vars)

        else:
            # no need to transfer the file, already correct hash, but still need to call
            # the file module in case we want to change attributes
            self._remove_tempfile_if_content_defined(content, content_tempfile)
            self._loader.cleanup_tmp_file(source_full)

            if raw:
                return None

            # Fix for https://github.com/assible/assible-modules-core/issues/1568.
            # If checksums match, and follow = True, find out if 'dest' is a link. If so,
            # change it to point to the source of the link.
            if follow:
                dest_status_nofollow = self._execute_remote_stat(
                    dest_file, all_vars=task_vars, follow=False)
                if dest_status_nofollow[
                        'islnk'] and 'lnk_source' in dest_status_nofollow.keys(
                        ):
                    dest = dest_status_nofollow['lnk_source']

            # Build temporary module_args.
            new_module_args = _create_remote_file_args(self._task.args)
            new_module_args.update(
                dict(
                    dest=dest,
                    _original_basename=source_rel,
                    recurse=False,
                    state='file',
                ))
            # src is sent to the file module in _original_basename, not in src
            try:
                del new_module_args['src']
            except KeyError:
                pass

            if lmode:
                new_module_args['mode'] = lmode

            # Execute the file module.
            module_return = self._execute_module(
                module_name='assible.legacy.file',
                module_args=new_module_args,
                task_vars=task_vars)

        if not module_return.get('checksum'):
            module_return['checksum'] = local_checksum

        result.update(module_return)
        return result
示例#25
0
    def _build_kwargs(self):
        self._psrp_host = self.get_option('remote_addr')
        self._psrp_user = self.get_option('remote_user')
        self._psrp_pass = self.get_option('remote_password')

        protocol = self.get_option('protocol')
        port = self.get_option('port')
        if protocol is None and port is None:
            protocol = 'https'
            port = 5986
        elif protocol is None:
            protocol = 'https' if int(port) != 5985 else 'http'
        elif port is None:
            port = 5986 if protocol == 'https' else 5985

        self._psrp_protocol = protocol
        self._psrp_port = int(port)

        self._psrp_path = self.get_option('path')
        self._psrp_auth = self.get_option('auth')
        # cert validation can either be a bool or a path to the cert
        cert_validation = self.get_option('cert_validation')
        cert_trust_path = self.get_option('ca_cert')
        if cert_validation == 'ignore':
            self._psrp_cert_validation = False
        elif cert_trust_path is not None:
            self._psrp_cert_validation = cert_trust_path
        else:
            self._psrp_cert_validation = True

        self._psrp_connection_timeout = self.get_option(
            'connection_timeout')  # Can be None
        self._psrp_read_timeout = self.get_option(
            'read_timeout')  # Can be None
        self._psrp_message_encryption = self.get_option('message_encryption')
        self._psrp_proxy = self.get_option('proxy')
        self._psrp_ignore_proxy = boolean(self.get_option('ignore_proxy'))
        self._psrp_operation_timeout = int(
            self.get_option('operation_timeout'))
        self._psrp_max_envelope_size = int(
            self.get_option('max_envelope_size'))
        self._psrp_configuration_name = self.get_option('configuration_name')
        self._psrp_reconnection_retries = int(
            self.get_option('reconnection_retries'))
        self._psrp_reconnection_backoff = float(
            self.get_option('reconnection_backoff'))

        self._psrp_certificate_key_pem = self.get_option('certificate_key_pem')
        self._psrp_certificate_pem = self.get_option('certificate_pem')
        self._psrp_credssp_auth_mechanism = self.get_option(
            'credssp_auth_mechanism')
        self._psrp_credssp_disable_tlsv1_2 = self.get_option(
            'credssp_disable_tlsv1_2')
        self._psrp_credssp_minimum_version = self.get_option(
            'credssp_minimum_version')
        self._psrp_negotiate_send_cbt = self.get_option('negotiate_send_cbt')
        self._psrp_negotiate_delegate = self.get_option('negotiate_delegate')
        self._psrp_negotiate_hostname_override = self.get_option(
            'negotiate_hostname_override')
        self._psrp_negotiate_service = self.get_option('negotiate_service')

        supported_args = []
        for auth_kwarg in AUTH_KWARGS.values():
            supported_args.extend(auth_kwarg)
        extra_args = set([
            v.replace('assible_psrp_', '') for v in self.get_option('_extras')
        ])
        unsupported_args = extra_args.difference(supported_args)

        for arg in unsupported_args:
            display.warning("assible_psrp_%s is unsupported by the current "
                            "psrp version installed" % arg)

        self._psrp_conn_kwargs = dict(
            server=self._psrp_host,
            port=self._psrp_port,
            username=self._psrp_user,
            password=self._psrp_pass,
            ssl=self._psrp_protocol == 'https',
            path=self._psrp_path,
            auth=self._psrp_auth,
            cert_validation=self._psrp_cert_validation,
            connection_timeout=self._psrp_connection_timeout,
            encryption=self._psrp_message_encryption,
            proxy=self._psrp_proxy,
            no_proxy=self._psrp_ignore_proxy,
            max_envelope_size=self._psrp_max_envelope_size,
            operation_timeout=self._psrp_operation_timeout,
            certificate_key_pem=self._psrp_certificate_key_pem,
            certificate_pem=self._psrp_certificate_pem,
            credssp_auth_mechanism=self._psrp_credssp_auth_mechanism,
            credssp_disable_tlsv1_2=self._psrp_credssp_disable_tlsv1_2,
            credssp_minimum_version=self._psrp_credssp_minimum_version,
            negotiate_send_cbt=self._psrp_negotiate_send_cbt,
            negotiate_delegate=self._psrp_negotiate_delegate,
            negotiate_hostname_override=self._psrp_negotiate_hostname_override,
            negotiate_service=self._psrp_negotiate_service,
        )

        # Check if PSRP version supports newer read_timeout argument (needs pypsrp 0.3.0+)
        if hasattr(pypsrp,
                   'FEATURES') and 'wsman_read_timeout' in pypsrp.FEATURES:
            self._psrp_conn_kwargs['read_timeout'] = self._psrp_read_timeout
        elif self._psrp_read_timeout is not None:
            display.warning(
                "assible_psrp_read_timeout is unsupported by the current psrp version installed, "
                "using assible_psrp_connection_timeout value for read_timeout instead."
            )

        # Check if PSRP version supports newer reconnection_retries argument (needs pypsrp 0.3.0+)
        if hasattr(pypsrp,
                   'FEATURES') and 'wsman_reconnections' in pypsrp.FEATURES:
            self._psrp_conn_kwargs[
                'reconnection_retries'] = self._psrp_reconnection_retries
            self._psrp_conn_kwargs[
                'reconnection_backoff'] = self._psrp_reconnection_backoff
        else:
            if self._psrp_reconnection_retries is not None:
                display.warning(
                    "assible_psrp_reconnection_retries is unsupported by the current psrp version installed."
                )
            if self._psrp_reconnection_backoff is not None:
                display.warning(
                    "assible_psrp_reconnection_backoff is unsupported by the current psrp version installed."
                )

        # add in the extra args that were set
        for arg in extra_args.intersection(supported_args):
            option = self.get_option('_extras')['assible_psrp_%s' % arg]
            self._psrp_conn_kwargs[arg] = option
示例#26
0
    def run(self, terms, variables, **kwargs):

        anydict = False
        skip = False

        for term in terms:
            if isinstance(term, dict):
                anydict = True

        total_search = []
        if anydict:
            for term in terms:
                if isinstance(term, dict):

                    files = term.get('files', [])
                    paths = term.get('paths', [])
                    skip = boolean(term.get('skip', False), strict=False)

                    filelist = files
                    if isinstance(files, string_types):
                        files = files.replace(',', ' ')
                        files = files.replace(';', ' ')
                        filelist = files.split(' ')

                    pathlist = paths
                    if paths:
                        if isinstance(paths, string_types):
                            paths = paths.replace(',', ' ')
                            paths = paths.replace(':', ' ')
                            paths = paths.replace(';', ' ')
                            pathlist = paths.split(' ')

                    if not pathlist:
                        total_search = filelist
                    else:
                        for path in pathlist:
                            for fn in filelist:
                                f = os.path.join(path, fn)
                                total_search.append(f)
                else:
                    total_search.append(term)
        else:
            total_search = self._flatten(terms)

        for fn in total_search:
            try:
                fn = self._templar.template(fn)
            except (AssibleUndefinedVariable, UndefinedError):
                continue

            # get subdir if set by task executor, default to files otherwise
            subdir = getattr(self, '_subdir', 'files')
            path = None
            path = self.find_file_in_search_path(variables,
                                                 subdir,
                                                 fn,
                                                 ignore_missing=True)
            if path is not None:
                return [path]
        if skip:
            return []
        raise AssibleLookupError(
            "No file was found when using first_found. Use errors='ignore' to allow this task to be skipped if no "
            "files are found")
示例#27
0
    def run(self, tmp=None, task_vars=None):
        ''' handler for unarchive operations '''
        if task_vars is None:
            task_vars = dict()

        result = super(ActionModule, self).run(tmp, task_vars)
        del tmp  # tmp no longer has any effect

        source = self._task.args.get('src', None)
        dest = self._task.args.get('dest', None)
        remote_src = boolean(self._task.args.get('remote_src', False),
                             strict=False)
        creates = self._task.args.get('creates', None)
        decrypt = self._task.args.get('decrypt', True)

        try:
            # "copy" is deprecated in favor of "remote_src".
            if 'copy' in self._task.args:
                # They are mutually exclusive.
                if 'remote_src' in self._task.args:
                    raise AssibleActionFail(
                        "parameters are mutually exclusive: ('copy', 'remote_src')"
                    )
                # We will take the information from copy and store it in
                # the remote_src var to use later in this file.
                self._task.args['remote_src'] = remote_src = not boolean(
                    self._task.args.pop('copy'), strict=False)

            if source is None or dest is None:
                raise AssibleActionFail(
                    "src (or content) and dest are required")

            if creates:
                # do not run the command if the line contains creates=filename
                # and the filename already exists. This allows idempotence
                # of command executions.
                creates = self._remote_expand_user(creates)
                if self._remote_file_exists(creates):
                    raise AssibleActionSkip("skipped, since %s exists" %
                                            creates)

            dest = self._remote_expand_user(
                dest)  # CCTODO: Fix path for Windows hosts.
            source = os.path.expanduser(source)

            if not remote_src:
                try:
                    source = self._loader.get_real_file(self._find_needle(
                        'files', source),
                                                        decrypt=decrypt)
                except AssibleError as e:
                    raise AssibleActionFail(to_text(e))

            try:
                remote_stat = self._execute_remote_stat(dest,
                                                        all_vars=task_vars,
                                                        follow=True)
            except AssibleError as e:
                raise AssibleActionFail(to_text(e))

            if not remote_stat['exists'] or not remote_stat['isdir']:
                raise AssibleActionFail("dest '%s' must be an existing dir" %
                                        dest)

            if not remote_src:
                # transfer the file to a remote tmp location
                tmp_src = self._connection._shell.join_path(
                    self._connection._shell.tmpdir, 'source')
                self._transfer_file(source, tmp_src)

            # handle diff mode client side
            # handle check mode client side

            # remove action plugin only keys
            new_module_args = self._task.args.copy()
            for key in ('decrypt', ):
                if key in new_module_args:
                    del new_module_args[key]

            if not remote_src:
                # fix file permissions when the copy is done as a different user
                self._fixup_perms2((self._connection._shell.tmpdir, tmp_src))
                new_module_args['src'] = tmp_src

            # execute the unarchive module now, with the updated args (using assible.legacy prefix to eliminate collections
            # collisions with local override
            result.update(
                self._execute_module(module_name='assible.legacy.unarchive',
                                     module_args=new_module_args,
                                     task_vars=task_vars))
        except AssibleAction as e:
            result.update(e.result)
        finally:
            self._remove_tmp_path(self._connection._shell.tmpdir)
        return result
示例#28
0
def ensure_type(value, value_type, origin=None):
    ''' return a configuration variable with casting
    :arg value: The value to ensure correct typing of
    :kwarg value_type: The type of the value.  This can be any of the following strings:
        :boolean: sets the value to a True or False value
        :bool: Same as 'boolean'
        :integer: Sets the value to an integer or raises a ValueType error
        :int: Same as 'integer'
        :float: Sets the value to a float or raises a ValueType error
        :list: Treats the value as a comma separated list.  Split the value
            and return it as a python list.
        :none: Sets the value to None
        :path: Expands any environment variables and tilde's in the value.
        :tmppath: Create a unique temporary directory inside of the directory
            specified by value and return its path.
        :temppath: Same as 'tmppath'
        :tmp: Same as 'tmppath'
        :pathlist: Treat the value as a typical PATH string.  (On POSIX, this
            means colon separated strings.)  Split the value and then expand
            each part for environment variables and tildes.
        :pathspec: Treat the value as a PATH string. Expands any environment variables
            tildes's in the value.
        :str: Sets the value to string types.
        :string: Same as 'str'
    '''

    errmsg = ''
    basedir = None
    if origin and os.path.isabs(origin) and os.path.exists(to_bytes(origin)):
        basedir = origin

    if value_type:
        value_type = value_type.lower()

    if value is not None:
        if value_type in ('boolean', 'bool'):
            value = boolean(value, strict=False)

        elif value_type in ('integer', 'int'):
            value = int(value)

        elif value_type == 'float':
            value = float(value)

        elif value_type == 'list':
            if isinstance(value, string_types):
                value = [x.strip() for x in value.split(',')]
            elif not isinstance(value, Sequence):
                errmsg = 'list'

        elif value_type == 'none':
            if value == "None":
                value = None

            if value is not None:
                errmsg = 'None'

        elif value_type == 'path':
            if isinstance(value, string_types):
                value = resolve_path(value, basedir=basedir)
            else:
                errmsg = 'path'

        elif value_type in ('tmp', 'temppath', 'tmppath'):
            if isinstance(value, string_types):
                value = resolve_path(value, basedir=basedir)
                if not os.path.exists(value):
                    makedirs_safe(value, 0o700)
                prefix = 'assible-local-%s' % os.getpid()
                value = tempfile.mkdtemp(prefix=prefix, dir=value)
                atexit.register(cleanup_tmp_file, value, warn=True)
            else:
                errmsg = 'temppath'

        elif value_type == 'pathspec':
            if isinstance(value, string_types):
                value = value.split(os.pathsep)

            if isinstance(value, Sequence):
                value = [resolve_path(x, basedir=basedir) for x in value]
            else:
                errmsg = 'pathspec'

        elif value_type == 'pathlist':
            if isinstance(value, string_types):
                value = [x.strip() for x in value.split(',')]

            if isinstance(value, Sequence):
                value = [resolve_path(x, basedir=basedir) for x in value]
            else:
                errmsg = 'pathlist'

        elif value_type in ('dict', 'dictionary'):
            if not isinstance(value, Mapping):
                errmsg = 'dictionary'

        elif value_type in ('str', 'string'):
            if isinstance(value, (string_types, AssibleVaultEncryptedUnicode, bool, int, float, complex)):
                value = unquote(to_text(value, errors='surrogate_or_strict'))
            else:
                errmsg = 'string'

        # defaults to string type
        elif isinstance(value, (string_types, AssibleVaultEncryptedUnicode)):
            value = unquote(to_text(value, errors='surrogate_or_strict'))

        if errmsg:
            raise ValueError('Invalid type provided for "%s": %s' % (errmsg, to_native(value)))

    return to_text(value, errors='surrogate_or_strict', nonstring='passthru')
示例#29
0
    def run(self):
        '''
        Run the given playbook, based on the settings in the play which
        may limit the runs to serialized groups, etc.
        '''

        result = 0
        entrylist = []
        entry = {}
        try:
            # preload become/connection/shell to set config defs cached
            list(connection_loader.all(class_only=True))
            list(shell_loader.all(class_only=True))
            list(become_loader.all(class_only=True))

            for playbook_path in self._playbooks:
                pb = Playbook.load(playbook_path,
                                   variable_manager=self._variable_manager,
                                   loader=self._loader)
                # FIXME: move out of inventory self._inventory.set_playbook_basedir(os.path.realpath(os.path.dirname(playbook_path)))

                if self._tqm is None:  # we are doing a listing
                    entry = {'playbook': playbook_path}
                    entry['plays'] = []
                else:
                    # make sure the tqm has callbacks loaded
                    self._tqm.load_callbacks()
                    self._tqm.send_callback('v2_playbook_on_start', pb)

                i = 1
                plays = pb.get_plays()
                display.vv(u'%d plays in %s' %
                           (len(plays), to_text(playbook_path)))

                for play in plays:
                    if play._included_path is not None:
                        self._loader.set_basedir(play._included_path)
                    else:
                        self._loader.set_basedir(pb._basedir)

                    # clear any filters which may have been applied to the inventory
                    self._inventory.remove_restriction()

                    # Allow variables to be used in vars_prompt fields.
                    all_vars = self._variable_manager.get_vars(play=play)
                    templar = Templar(loader=self._loader, variables=all_vars)
                    setattr(play, 'vars_prompt',
                            templar.template(play.vars_prompt))

                    # FIXME: this should be a play 'sub object' like loop_control
                    if play.vars_prompt:
                        for var in play.vars_prompt:
                            vname = var['name']
                            prompt = var.get("prompt", vname)
                            default = var.get("default", None)
                            private = boolean(var.get("private", True))
                            confirm = boolean(var.get("confirm", False))
                            encrypt = var.get("encrypt", None)
                            salt_size = var.get("salt_size", None)
                            salt = var.get("salt", None)
                            unsafe = var.get("unsafe", None)

                            if vname not in self._variable_manager.extra_vars:
                                if self._tqm:
                                    self._tqm.send_callback(
                                        'v2_playbook_on_vars_prompt', vname,
                                        private, prompt, encrypt, confirm,
                                        salt_size, salt, default, unsafe)
                                    play.vars[vname] = display.do_var_prompt(
                                        vname, private, prompt, encrypt,
                                        confirm, salt_size, salt, default,
                                        unsafe)
                                else:  # we are either in --list-<option> or syntax check
                                    play.vars[vname] = default

                    # Post validate so any play level variables are templated
                    all_vars = self._variable_manager.get_vars(play=play)
                    templar = Templar(loader=self._loader, variables=all_vars)
                    play.post_validate(templar)

                    if context.CLIARGS['syntax']:
                        continue

                    if self._tqm is None:
                        # we are just doing a listing
                        entry['plays'].append(play)

                    else:
                        self._tqm._unreachable_hosts.update(
                            self._unreachable_hosts)

                        previously_failed = len(self._tqm._failed_hosts)
                        previously_unreachable = len(
                            self._tqm._unreachable_hosts)

                        break_play = False
                        # we are actually running plays
                        batches = self._get_serialized_batches(play)
                        if len(batches) == 0:
                            self._tqm.send_callback(
                                'v2_playbook_on_play_start', play)
                            self._tqm.send_callback(
                                'v2_playbook_on_no_hosts_matched')
                        for batch in batches:
                            # restrict the inventory to the hosts in the serialized batch
                            self._inventory.restrict_to_hosts(batch)
                            # and run it...
                            result = self._tqm.run(play=play)

                            # break the play if the result equals the special return code
                            if result & self._tqm.RUN_FAILED_BREAK_PLAY != 0:
                                result = self._tqm.RUN_FAILED_HOSTS
                                break_play = True

                            # check the number of failures here, to see if they're above the maximum
                            # failure percentage allowed, or if any errors are fatal. If either of those
                            # conditions are met, we break out, otherwise we only break out if the entire
                            # batch failed
                            failed_hosts_count = len(self._tqm._failed_hosts) + len(self._tqm._unreachable_hosts) - \
                                (previously_failed + previously_unreachable)

                            if len(batch) == failed_hosts_count:
                                break_play = True
                                break

                            # update the previous counts so they don't accumulate incorrectly
                            # over multiple serial batches
                            previously_failed += len(
                                self._tqm._failed_hosts) - previously_failed
                            previously_unreachable += len(
                                self._tqm._unreachable_hosts
                            ) - previously_unreachable

                            # save the unreachable hosts from this batch
                            self._unreachable_hosts.update(
                                self._tqm._unreachable_hosts)

                        if break_play:
                            break

                    i = i + 1  # per play

                if entry:
                    entrylist.append(entry)  # per playbook

                # send the stats callback for this playbook
                if self._tqm is not None:
                    if C.RETRY_FILES_ENABLED:
                        retries = set(self._tqm._failed_hosts.keys())
                        retries.update(self._tqm._unreachable_hosts.keys())
                        retries = sorted(retries)
                        if len(retries) > 0:
                            if C.RETRY_FILES_SAVE_PATH:
                                basedir = C.RETRY_FILES_SAVE_PATH
                            elif playbook_path:
                                basedir = os.path.dirname(
                                    os.path.abspath(playbook_path))
                            else:
                                basedir = '~/'

                            (retry_name, _) = os.path.splitext(
                                os.path.basename(playbook_path))
                            filename = os.path.join(basedir,
                                                    "%s.retry" % retry_name)
                            if self._generate_retry_inventory(
                                    filename, retries):
                                display.display(
                                    "\tto retry, use: --limit @%s\n" %
                                    filename)

                    self._tqm.send_callback('v2_playbook_on_stats',
                                            self._tqm._stats)

                # if the last result wasn't zero, break out of the playbook file name loop
                if result != 0:
                    break

            if entrylist:
                return entrylist

        finally:
            if self._tqm is not None:
                self._tqm.cleanup()
            if self._loader:
                self._loader.cleanup_all_tmp_files()

        if context.CLIARGS['syntax']:
            display.display("No issues encountered")
            return result

        if context.CLIARGS['start_at_task'] and not self._tqm._start_at_done:
            display.error(
                "No matching task \"%s\" found."
                " Note: --start-at-task can only follow static includes." %
                context.CLIARGS['start_at_task'])

        return result
示例#30
0
    def _kerb_auth(self, principal, password):
        if password is None:
            password = ""

        self._kerb_ccache = tempfile.NamedTemporaryFile()
        display.vvvvv("creating Kerberos CC at %s" % self._kerb_ccache.name)
        krb5ccname = "FILE:%s" % self._kerb_ccache.name
        os.environ["KRB5CCNAME"] = krb5ccname
        krb5env = dict(KRB5CCNAME=krb5ccname)

        # Stores various flags to call with kinit, these could be explicit args set by 'assible_winrm_kinit_args' OR
        # '-f' if kerberos delegation is requested (assible_winrm_kerberos_delegation).
        kinit_cmdline = [self._kinit_cmd]
        kinit_args = self.get_option('kinit_args')
        if kinit_args:
            kinit_args = [
                to_text(a) for a in shlex.split(kinit_args) if a.strip()
            ]
            kinit_cmdline.extend(kinit_args)

        elif boolean(
                self.get_option('_extras').get(
                    'assible_winrm_kerberos_delegation', False)):
            kinit_cmdline.append('-f')

        kinit_cmdline.append(principal)

        # pexpect runs the process in its own pty so it can correctly send
        # the password as input even on MacOS which blocks subprocess from
        # doing so. Unfortunately it is not available on the built in Python
        # so we can only use it if someone has installed it
        if HAS_PEXPECT:
            proc_mechanism = "pexpect"
            command = kinit_cmdline.pop(0)
            password = to_text(password,
                               encoding='utf-8',
                               errors='surrogate_or_strict')

            display.vvvv("calling kinit with pexpect for principal %s" %
                         principal)
            try:
                child = pexpect.spawn(command,
                                      kinit_cmdline,
                                      timeout=60,
                                      env=krb5env,
                                      echo=False)
            except pexpect.ExceptionPexpect as err:
                err_msg = "Kerberos auth failure when calling kinit cmd " \
                          "'%s': %s" % (command, to_native(err))
                raise AssibleConnectionFailure(err_msg)

            try:
                child.expect(".*:")
                child.sendline(password)
            except OSError as err:
                # child exited before the pass was sent, Assible will raise
                # error based on the rc below, just display the error here
                display.vvvv("kinit with pexpect raised OSError: %s" %
                             to_native(err))

            # technically this is the stdout + stderr but to match the
            # subprocess error checking behaviour, we will call it stderr
            stderr = child.read()
            child.wait()
            rc = child.exitstatus
        else:
            proc_mechanism = "subprocess"
            password = to_bytes(password,
                                encoding='utf-8',
                                errors='surrogate_or_strict')

            display.vvvv("calling kinit with subprocess for principal %s" %
                         principal)
            try:
                p = subprocess.Popen(kinit_cmdline,
                                     stdin=subprocess.PIPE,
                                     stdout=subprocess.PIPE,
                                     stderr=subprocess.PIPE,
                                     env=krb5env)

            except OSError as err:
                err_msg = "Kerberos auth failure when calling kinit cmd " \
                          "'%s': %s" % (self._kinit_cmd, to_native(err))
                raise AssibleConnectionFailure(err_msg)

            stdout, stderr = p.communicate(password + b'\n')
            rc = p.returncode != 0

        if rc != 0:
            # one last attempt at making sure the password does not exist
            # in the output
            exp_msg = to_native(stderr.strip())
            exp_msg = exp_msg.replace(to_native(password), "<redacted>")

            err_msg = "Kerberos auth failure for principal %s with %s: %s" \
                      % (principal, proc_mechanism, exp_msg)
            raise AssibleConnectionFailure(err_msg)

        display.vvvvv("kinit succeeded for principal %s" % principal)