Ejemplo n.º 1
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
Ejemplo n.º 2
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
Ejemplo n.º 3
0
    def run(self, tmp=None, task_vars=None):

        self._supports_check_mode = True

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

        args = self._task.args
        raw = args.pop('_raw_params', {})
        if isinstance(raw, Mapping):
            # TODO: create 'conflict' detection in base class to deal with repeats and aliases and warn user
            args = combine_vars(raw, args)
        else:
            raise AssibleActionFail(
                'Invalid raw parameters passed, requires a dictonary/mapping got a  %s'
                % type(raw))

        # Parse out any hostname:port patterns
        new_name = args.get('name', args.get('hostname',
                                             args.get('host', None)))
        if new_name is None:
            raise AssibleActionFail(
                'name, host or hostname needs to be provided')

        display.vv("creating host via 'add_host': hostname=%s" % new_name)

        try:
            name, port = parse_address(new_name, allow_ranges=False)
        except Exception:
            # not a parsable hostname, but might still be usable
            name = new_name
            port = None

        if port:
            args['assible_ssh_port'] = port

        groups = args.get('groupname', args.get('groups',
                                                args.get('group', '')))
        # add it to the group if that was specified
        new_groups = []
        if groups:
            if isinstance(groups, list):
                group_list = groups
            elif isinstance(groups, string_types):
                group_list = groups.split(",")
            else:
                raise AssibleActionFail("Groups must be specified as a list.",
                                        obj=self._task)

            for group_name in group_list:
                if group_name not in new_groups:
                    new_groups.append(group_name.strip())

        # Add any variables to the new_host
        host_vars = dict()
        special_args = frozenset(('name', 'hostname', 'groupname', 'groups'))
        for k in args.keys():
            if k not in special_args:
                host_vars[k] = args[k]

        result['changed'] = False
        result['add_host'] = dict(host_name=name,
                                  groups=new_groups,
                                  host_vars=host_vars)
        return result
Ejemplo n.º 4
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

        try:
            creates = self._task.args.get('creates')
            if creates:
                # do not run the command if the line contains creates=filename
                # and the filename already exists. This allows idempotence
                # of command executions.
                if self._remote_file_exists(creates):
                    raise AssibleActionSkip(
                        "%s exists, matching creates option" % creates)

            removes = self._task.args.get('removes')
            if removes:
                # do not run the command if the line contains removes=filename
                # and the filename does not exist. This allows idempotence
                # of command executions.
                if not self._remote_file_exists(removes):
                    raise AssibleActionSkip(
                        "%s does not exist, matching removes option" % removes)

            # The chdir must be absolute, because a relative path would rely on
            # remote node behaviour & user config.
            chdir = self._task.args.get('chdir')
            if chdir:
                # Powershell is the only Windows-path aware shell
                if getattr(self._connection._shell, "_IS_WINDOWS", False) and \
                        not self.windows_absolute_path_detection.match(chdir):
                    raise AssibleActionFail(
                        'chdir %s must be an absolute path for a Windows remote node'
                        % chdir)
                # Every other shell is unix-path-aware.
                if not getattr(self._connection._shell, "_IS_WINDOWS",
                               False) and not chdir.startswith('/'):
                    raise AssibleActionFail(
                        'chdir %s must be an absolute path for a Unix-aware remote node'
                        % chdir)

            # Split out the script as the first item in raw_params using
            # shlex.split() in order to support paths and files with spaces in the name.
            # Any arguments passed to the script will be added back later.
            raw_params = to_native(self._task.args.get('_raw_params', ''),
                                   errors='surrogate_or_strict')
            parts = [
                to_text(s, errors='surrogate_or_strict')
                for s in shlex.split(raw_params.strip())
            ]
            source = parts[0]

            # Support executable paths and files with spaces in the name.
            executable = to_native(self._task.args.get('executable', ''),
                                   errors='surrogate_or_strict')

            try:
                source = self._loader.get_real_file(
                    self._find_needle('files', source),
                    decrypt=self._task.args.get('decrypt', True))
            except AssibleError as e:
                raise AssibleActionFail(to_native(e))

            # now we execute script, always assume changed.
            result['changed'] = True

            if not self._play_context.check_mode:
                # transfer the file to a remote tmp location
                tmp_src = self._connection._shell.join_path(
                    self._connection._shell.tmpdir, os.path.basename(source))

                # Convert raw_params to text for the purpose of replacing the script since
                # parts and tmp_src are both unicode strings and raw_params will be different
                # depending on Python version.
                #
                # Once everything is encoded consistently, replace the script path on the remote
                # system with the remainder of the raw_params. This preserves quoting in parameters
                # that would have been removed by shlex.split().
                target_command = to_text(raw_params).strip().replace(
                    parts[0], tmp_src)

                self._transfer_file(source, tmp_src)

                # set file permissions, more permissive when the copy is done as a different user
                self._fixup_perms2((self._connection._shell.tmpdir, tmp_src),
                                   execute=True)

                # add preparation steps to one ssh roundtrip executing the script
                env_dict = dict()
                env_string = self._compute_environment_string(env_dict)

                if executable:
                    script_cmd = ' '.join(
                        [env_string, executable, target_command])
                else:
                    script_cmd = ' '.join([env_string, target_command])

            if self._play_context.check_mode:
                raise _AssibleActionDone()

            script_cmd = self._connection._shell.wrap_for_exec(script_cmd)

            exec_data = None
            # PowerShell runs the script in a special wrapper to enable things
            # like become and environment args
            if getattr(self._connection._shell, "_IS_WINDOWS", False):
                # FUTURE: use a more public method to get the exec payload
                pc = self._play_context
                exec_data = ps_manifest._create_powershell_wrapper(
                    to_bytes(script_cmd), source, {}, env_dict,
                    self._task.async_val, pc.become, pc.become_method,
                    pc.become_user, pc.become_pass, pc.become_flags, "script",
                    task_vars, None)
                # build the necessary exec wrapper command
                # FUTURE: this still doesn't let script work on Windows with non-pipelined connections or
                # full manual exec of KEEP_REMOTE_FILES
                script_cmd = self._connection._shell.build_module_command(
                    env_string='', shebang='#!powershell', cmd='')

            result.update(
                self._low_level_execute_command(cmd=script_cmd,
                                                in_data=exec_data,
                                                sudoable=True,
                                                chdir=chdir))

            if 'rc' in result and result['rc'] != 0:
                raise AssibleActionFail('non-zero return code')

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

        return result
Ejemplo n.º 5
0
    def run(self, tmp=None, task_vars=None):
        ''' handler for package operations '''

        self._supports_check_mode = True
        self._supports_async = True

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

        module = self._task.args.get('use', 'auto')

        if module == 'auto':
            try:
                if self._task.delegate_to:  # if we delegate, we should use delegated host's facts
                    module = self._templar.template(
                        "{{hostvars['%s']['assible_facts']['pkg_mgr']}}" %
                        self._task.delegate_to)
                else:
                    module = self._templar.template(
                        '{{assible_facts.pkg_mgr}}')
            except Exception:
                pass  # could not get it from template!

        try:
            if module == 'auto':
                facts = self._execute_module(
                    module_name='assible.legacy.setup',
                    module_args=dict(filter='assible_pkg_mgr',
                                     gather_subset='!all'),
                    task_vars=task_vars)
                display.debug("Facts %s" % facts)
                module = facts.get('assible_facts',
                                   {}).get('assible_pkg_mgr', 'auto')

            if module != 'auto':
                if not self._shared_loader_obj.module_loader.has_plugin(
                        module):
                    raise AssibleActionFail('Could not find a module for %s.' %
                                            module)
                else:
                    # run the 'package' module
                    new_module_args = self._task.args.copy()
                    if 'use' in new_module_args:
                        del new_module_args['use']

                    # get defaults for specific module
                    new_module_args = get_action_args_with_defaults(
                        module, new_module_args, self._task.module_defaults,
                        self._templar,
                        self._task._assible_internal_redirect_list)

                    if module in self.BUILTIN_PKG_MGR_MODULES:
                        # prefix with assible.legacy to eliminate external collisions while still allowing library/ override
                        module = 'assible.legacy.' + module

                    display.vvvv("Running %s" % module)
                    result.update(
                        self._execute_module(module_name=module,
                                             module_args=new_module_args,
                                             task_vars=task_vars,
                                             wrap_async=self._task.async_val))
            else:
                raise AssibleActionFail(
                    'Could not detect which package manager to use. Try gathering facts or setting the "use" option.'
                )

        except AssibleAction as e:
            result.update(e.result)
        finally:
            if not self._task.async_val:
                # remove a temporary path we created
                self._remove_tmp_path(self._connection._shell.tmpdir)

        return result
Ejemplo n.º 6
0
    def run(self, tmp=None, task_vars=None):
        ''' handler for package operations '''

        self._supports_check_mode = True
        self._supports_async = True

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

        module = self._task.args.get('use', 'auto').lower()

        if module == 'auto':
            try:
                if self._task.delegate_to:  # if we delegate, we should use delegated host's facts
                    module = self._templar.template(
                        "{{hostvars['%s']['assible_facts']['service_mgr']}}" %
                        self._task.delegate_to)
                else:
                    module = self._templar.template(
                        '{{assible_facts.service_mgr}}')
            except Exception:
                pass  # could not get it from template!

        try:
            if module == 'auto':
                facts = self._execute_module(
                    module_name='assible.legacy.setup',
                    module_args=dict(gather_subset='!all',
                                     filter='assible_service_mgr'),
                    task_vars=task_vars)
                self._display.debug("Facts %s" % facts)
                module = facts.get('assible_facts',
                                   {}).get('assible_service_mgr', 'auto')

            if not module or module == 'auto' or not self._shared_loader_obj.module_loader.has_plugin(
                    module):
                module = 'assible.legacy.service'

            if module != 'auto':
                # run the 'service' module
                new_module_args = self._task.args.copy()
                if 'use' in new_module_args:
                    del new_module_args['use']

                if module in self.UNUSED_PARAMS:
                    for unused in self.UNUSED_PARAMS[module]:
                        if unused in new_module_args:
                            del new_module_args[unused]
                            self._display.warning(
                                'Ignoring "%s" as it is not used in "%s"' %
                                (unused, module))

                # get defaults for specific module
                new_module_args = get_action_args_with_defaults(
                    module, new_module_args, self._task.module_defaults,
                    self._templar, self._task._assible_internal_redirect_list)

                # collection prefix known internal modules to avoid collisions from collections search, while still allowing library/ overrides
                if module in self.BUILTIN_SVC_MGR_MODULES:
                    module = 'assible.legacy.' + module

                self._display.vvvv("Running %s" % module)
                result.update(
                    self._execute_module(module_name=module,
                                         module_args=new_module_args,
                                         task_vars=task_vars,
                                         wrap_async=self._task.async_val))
            else:
                raise AssibleActionFail(
                    'Could not detect which service manager to use. Try gathering facts or setting the "use" option.'
                )

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

        return result
Ejemplo n.º 7
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
Ejemplo n.º 8
0
    def run(self, tmp=None, task_vars=None):
        self._supports_async = True

        if task_vars is None:
            task_vars = dict()

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

        body_format = self._task.args.get('body_format', 'raw')
        body = self._task.args.get('body')
        src = self._task.args.get('src', None)
        remote_src = boolean(self._task.args.get('remote_src', 'no'),
                             strict=False)

        try:
            if remote_src:
                # everything is remote, so we just execute the module
                # without changing any of the module arguments
                # call with assible.legacy prefix to prevent collections collisions while allowing local override
                raise _AssibleActionDone(result=self._execute_module(
                    module_name='assible.legacy.uri',
                    task_vars=task_vars,
                    wrap_async=self._task.async_val))

            kwargs = {}

            if src:
                try:
                    src = self._find_needle('files', src)
                except AssibleError as e:
                    raise AssibleActionFail(to_native(e))

                tmp_src = self._connection._shell.join_path(
                    self._connection._shell.tmpdir, os.path.basename(src))
                kwargs['src'] = tmp_src
                self._transfer_file(src, tmp_src)
                self._fixup_perms2((self._connection._shell.tmpdir, tmp_src))
            elif body_format == 'form-multipart':
                if not isinstance(body, Mapping):
                    raise AssibleActionFail(
                        'body must be mapping, cannot be type %s' %
                        body.__class__.__name__)
                for field, value in body.items():
                    if isinstance(value, text_type):
                        continue
                    content = value.get('content')
                    filename = value.get('filename')
                    if not filename or content:
                        continue

                    try:
                        filename = self._find_needle('files', filename)
                    except AssibleError as e:
                        raise AssibleActionFail(to_native(e))

                    tmp_src = self._connection._shell.join_path(
                        self._connection._shell.tmpdir,
                        os.path.basename(filename))
                    value['filename'] = tmp_src
                    self._transfer_file(filename, tmp_src)
                    self._fixup_perms2(
                        (self._connection._shell.tmpdir, tmp_src))
                kwargs['body'] = body

            new_module_args = self._task.args.copy()
            new_module_args.update(kwargs)

            # call with assible.legacy prefix to prevent collections collisions while allowing local override
            result.update(
                self._execute_module('assible.legacy.uri',
                                     module_args=new_module_args,
                                     task_vars=task_vars,
                                     wrap_async=self._task.async_val))
        except AssibleAction as e:
            result.update(e.result)
        finally:
            if not self._task.async_val:
                self._remove_tmp_path(self._connection._shell.tmpdir)
        return result
Ejemplo n.º 9
0
    def run(self, tmp=None, task_vars=None):
        ''' handler for template 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

        # Options type validation
        # stings
        for s_type in ('src', 'dest', 'state', 'newline_sequence',
                       'variable_start_string', 'variable_end_string',
                       'block_start_string', 'block_end_string'):
            if s_type in self._task.args:
                value = ensure_type(self._task.args[s_type], 'string')
                if value is not None and not isinstance(value, string_types):
                    raise AssibleActionFail(
                        "%s is expected to be a string, but got %s instead" %
                        (s_type, type(value)))
                self._task.args[s_type] = value

        # booleans
        try:
            follow = boolean(self._task.args.get('follow', False),
                             strict=False)
            trim_blocks = boolean(self._task.args.get('trim_blocks', True),
                                  strict=False)
            lstrip_blocks = boolean(self._task.args.get(
                'lstrip_blocks', False),
                                    strict=False)
        except TypeError as e:
            raise AssibleActionFail(to_native(e))

        # assign to local vars for ease of use
        source = self._task.args.get('src', None)
        dest = self._task.args.get('dest', None)
        state = self._task.args.get('state', None)
        newline_sequence = self._task.args.get('newline_sequence',
                                               self.DEFAULT_NEWLINE_SEQUENCE)
        variable_start_string = self._task.args.get('variable_start_string',
                                                    None)
        variable_end_string = self._task.args.get('variable_end_string', None)
        block_start_string = self._task.args.get('block_start_string', None)
        block_end_string = self._task.args.get('block_end_string', None)
        output_encoding = self._task.args.get('output_encoding',
                                              'utf-8') or 'utf-8'

        # Option `lstrip_blocks' was added in Jinja2 version 2.7.
        if lstrip_blocks:
            try:
                import jinja2.defaults
            except ImportError:
                raise AssibleError(
                    'Unable to import Jinja2 defaults for determining Jinja2 features.'
                )

            try:
                jinja2.defaults.LSTRIP_BLOCKS
            except AttributeError:
                raise AssibleError(
                    "Option `lstrip_blocks' is only available in Jinja2 versions >=2.7"
                )

        wrong_sequences = ["\\n", "\\r", "\\r\\n"]
        allowed_sequences = ["\n", "\r", "\r\n"]

        # We need to convert unescaped sequences to proper escaped sequences for Jinja2
        if newline_sequence in wrong_sequences:
            newline_sequence = allowed_sequences[wrong_sequences.index(
                newline_sequence)]

        try:
            # logical validation
            if state is not None:
                raise AssibleActionFail(
                    "'state' cannot be specified on a template")
            elif source is None or dest is None:
                raise AssibleActionFail("src and dest are required")
            elif newline_sequence not in allowed_sequences:
                raise AssibleActionFail(
                    "newline_sequence needs to be one of: \n, \r or \r\n")
            else:
                try:
                    source = self._find_needle('templates', source)
                except AssibleError as e:
                    raise AssibleActionFail(to_text(e))

            mode = self._task.args.get('mode', None)
            if mode == 'preserve':
                mode = '0%03o' % stat.S_IMODE(os.stat(source).st_mode)

            # Get vault decrypted tmp file
            try:
                tmp_source = self._loader.get_real_file(source)
            except AssibleFileNotFound as e:
                raise AssibleActionFail("could not find src=%s, %s" %
                                        (source, to_text(e)))
            b_tmp_source = to_bytes(tmp_source, errors='surrogate_or_strict')

            # template the source data locally & get ready to transfer
            try:
                with open(b_tmp_source, 'rb') as f:
                    try:
                        template_data = to_text(f.read(),
                                                errors='surrogate_or_strict')
                    except UnicodeError:
                        raise AssibleActionFail(
                            "Template source files must be utf-8 encoded")

                # set jinja2 internal search path for includes
                searchpath = task_vars.get('assible_search_path', [])
                searchpath.extend(
                    [self._loader._basedir,
                     os.path.dirname(source)])

                # We want to search into the 'templates' subdir of each search path in
                # addition to our original search paths.
                newsearchpath = []
                for p in searchpath:
                    newsearchpath.append(os.path.join(p, 'templates'))
                    newsearchpath.append(p)
                searchpath = newsearchpath

                # add assible 'template' vars
                temp_vars = task_vars.copy()
                temp_vars.update(generate_assible_template_vars(source, dest))

                # force templar to use AssibleEnvironment to prevent issues with native types
                # https://github.com/assible/assible/issues/46169
                templar = self._templar.copy_with_new_env(
                    environment_class=AssibleEnvironment,
                    searchpath=searchpath,
                    newline_sequence=newline_sequence,
                    block_start_string=block_start_string,
                    block_end_string=block_end_string,
                    variable_start_string=variable_start_string,
                    variable_end_string=variable_end_string,
                    trim_blocks=trim_blocks,
                    lstrip_blocks=lstrip_blocks,
                    available_variables=temp_vars)
                resultant = templar.do_template(
                    template_data,
                    preserve_trailing_newlines=True,
                    escape_backslashes=False)
            except AssibleAction:
                raise
            except Exception as e:
                raise AssibleActionFail("%s: %s" %
                                        (type(e).__name__, to_text(e)))
            finally:
                self._loader.cleanup_tmp_file(b_tmp_source)

            new_task = self._task.copy()
            # mode is either the mode from task.args or the mode of the source file if the task.args
            # mode == 'preserve'
            new_task.args['mode'] = mode

            # remove 'template only' options:
            for remove in ('newline_sequence', 'block_start_string',
                           'block_end_string', 'variable_start_string',
                           'variable_end_string', 'trim_blocks',
                           'lstrip_blocks', 'output_encoding'):
                new_task.args.pop(remove, None)

            local_tempdir = tempfile.mkdtemp(dir=C.DEFAULT_LOCAL_TMP)

            try:
                result_file = os.path.join(local_tempdir,
                                           os.path.basename(source))
                with open(to_bytes(result_file, errors='surrogate_or_strict'),
                          'wb') as f:
                    f.write(
                        to_bytes(resultant,
                                 encoding=output_encoding,
                                 errors='surrogate_or_strict'))

                new_task.args.update(
                    dict(
                        src=result_file,
                        dest=dest,
                        follow=follow,
                    ), )
                # call with assible.legacy prefix to eliminate collisions with collections while still allowing local override
                copy_action = self._shared_loader_obj.action_loader.get(
                    'assible.legacy.copy',
                    task=new_task,
                    connection=self._connection,
                    play_context=self._play_context,
                    loader=self._loader,
                    templar=self._templar,
                    shared_loader_obj=self._shared_loader_obj)
                result.update(copy_action.run(task_vars=task_vars))
            finally:
                shutil.rmtree(
                    to_bytes(local_tempdir, errors='surrogate_or_strict'))

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

        return result