def update_timestamp_for_file(path, mtime, atime, diff=None):
    b_path = to_bytes(path, errors='surrogate_or_strict')

    try:
        # When mtime and atime are set to 'now', rely on utime(path, None) which does not require ownership of the file
        # https://github.com/ansible/ansible/issues/50943
        if mtime is Sentinel and atime is Sentinel:
            # It's not exact but we can't rely on os.stat(path).st_mtime after setting os.utime(path, None) as it may
            # not be updated. Just use the current time for the diff values
            mtime = atime = time.time()

            previous_mtime = os.stat(b_path).st_mtime
            previous_atime = os.stat(b_path).st_atime

            set_time = None
        else:
            # If both parameters are None 'preserve', nothing to do
            if mtime is None and atime is None:
                return False

            previous_mtime = os.stat(b_path).st_mtime
            previous_atime = os.stat(b_path).st_atime

            if mtime is None:
                mtime = previous_mtime
            elif mtime is Sentinel:
                mtime = time.time()

            if atime is None:
                atime = previous_atime
            elif atime is Sentinel:
                atime = time.time()

            # If both timestamps are already ok, nothing to do
            if mtime == previous_mtime and atime == previous_atime:
                return False

            set_time = (atime, mtime)

        os.utime(b_path, set_time)

        if diff is not None:
            if 'before' not in diff:
                diff['before'] = {}
            if 'after' not in diff:
                diff['after'] = {}
            if mtime != previous_mtime:
                diff['before']['mtime'] = previous_mtime
                diff['after']['mtime'] = mtime
            if atime != previous_atime:
                diff['before']['atime'] = previous_atime
                diff['after']['atime'] = atime
    except OSError as e:
        raise AnsibleModuleError(
            results={
                'msg':
                'Error while updating modification or access time: %s' %
                to_native(e, nonstring='simplerepr'),
                'path':
                path
            })
    return True
Exemple #2
0
    def _parse_pipeline_result(self, pipeline):
        """
        PSRP doesn't have the same concept as other protocols with its output.
        We need some extra logic to convert the pipeline streams and host
        output into the format that Ansible understands.

        :param pipeline: The finished PowerShell pipeline that invoked our
            commands
        :return: rc, stdout, stderr based on the pipeline output
        """
        # we try and get the rc from our host implementation, this is set if
        # exit or $host.SetShouldExit() is called in our pipeline, if not we
        # set to 0 if the pipeline had not errors and 1 if it did
        rc = self.host.rc or (1 if pipeline.had_errors else 0)

        # TODO: figure out a better way of merging this with the host output
        stdout_list = []
        for output in pipeline.output:
            # Not all pipeline outputs are a string or contain a __str__ value,
            # we will create our own output based on the properties of the
            # complex object if that is the case.
            if isinstance(output, GenericComplexObject) and output.to_string is None:
                obj_lines = output.property_sets
                for key, value in output.adapted_properties.items():
                    obj_lines.append(u"%s: %s" % (key, value))
                for key, value in output.extended_properties.items():
                    obj_lines.append(u"%s: %s" % (key, value))
                output_msg = u"\n".join(obj_lines)
            else:
                output_msg = to_text(output, nonstring='simplerepr')

            stdout_list.append(output_msg)

        if len(self.host.ui.stdout) > 0:
            stdout_list += self.host.ui.stdout
        stdout = u"\r\n".join(stdout_list)

        stderr_list = []
        for error in pipeline.streams.error:
            # the error record is not as fully fleshed out like we usually get
            # in PS, we will manually create it here
            command_name = "%s : " % error.command_name if error.command_name else ''
            position = "%s\r\n" % error.invocation_position_message if error.invocation_position_message else ''
            error_msg = "%s%s\r\n%s" \
                        "    + CategoryInfo          : %s\r\n" \
                        "    + FullyQualifiedErrorId : %s" \
                        % (command_name, str(error), position,
                           error.message, error.fq_error)
            stacktrace = error.script_stacktrace
            if self._play_context.verbosity >= 3 and stacktrace is not None:
                error_msg += "\r\nStackTrace:\r\n%s" % stacktrace
            stderr_list.append(error_msg)

        if len(self.host.ui.stderr) > 0:
            stderr_list += self.host.ui.stderr
        stderr = u"\r\n".join([to_text(o) for o in stderr_list])

        display.vvvvv("PSRP RC: %d" % rc, host=self._psrp_host)
        display.vvvvv("PSRP STDOUT: %s" % stdout, host=self._psrp_host)
        display.vvvvv("PSRP STDERR: %s" % stderr, host=self._psrp_host)

        # reset the host back output back to defaults, needed if running
        # multiple pipelines on the same RunspacePool
        self.host.rc = 0
        self.host.ui.stdout = []
        self.host.ui.stderr = []

        return rc, to_bytes(stdout, encoding='utf-8'), to_bytes(stderr, encoding='utf-8')
def set_module_args(args):
    """prepare arguments so that they will be picked up during module creation"""
    args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
    basic._ANSIBLE_ARGS = to_bytes(args)  # pylint: disable=protected-access
Exemple #4
0
 def _get_file_contents(self, path):
     path = to_text(path)
     if path in self._file_mapping:
         return (to_bytes(self._file_mapping[path]), False)
     else:
         raise AnsibleParserError("file not found: %s" % path)
Exemple #5
0
    def put_file(self, in_path, out_path):
        super(Connection, self).put_file(in_path, out_path)
        display.vvv("PUT %s TO %s" % (in_path, out_path), host=self._psrp_host)

        out_path = self._shell._unquote(out_path)
        script = u'''begin {
    $ErrorActionPreference = "Stop"

    $path = '%s'
    $fd = [System.IO.File]::Create($path)
    $algo = [System.Security.Cryptography.SHA1CryptoServiceProvider]::Create()
    $bytes = @()
} process {
    $bytes = [System.Convert]::FromBase64String($input)
    $algo.TransformBlock($bytes, 0, $bytes.Length, $bytes, 0) > $null
    $fd.Write($bytes, 0, $bytes.Length)
} end {
    $fd.Close()
    $algo.TransformFinalBlock($bytes, 0, 0) > $null
    $hash = [System.BitConverter]::ToString($algo.Hash)
    $hash = $hash.Replace("-", "").ToLowerInvariant()

    Write-Output -InputObject "{`"sha1`":`"$hash`"}"
}''' % self._shell._escape(out_path)

        cmd_parts = self._shell._encode_script(script, as_list=True,
                                               strict_mode=False,
                                               preserve_rc=False)
        b_in_path = to_bytes(in_path, errors='surrogate_or_strict')
        if not os.path.exists(b_in_path):
            raise AnsibleFileNotFound('file or module does not exist: "%s"'
                                      % to_native(in_path))

        in_size = os.path.getsize(b_in_path)
        buffer_size = int(self.runspace.connection.max_payload_size / 4 * 3)

        # copying files is faster when using the raw WinRM shell and not PSRP
        # we will create a WinRS shell just for this process
        # TODO: speed this up as there is overhead creating a shell for this
        with WinRS(self.runspace.connection, codepage=65001) as shell:
            process = Process(shell, cmd_parts[0], cmd_parts[1:])
            process.begin_invoke()

            offset = 0
            with open(b_in_path, 'rb') as src_file:
                for data in iter((lambda: src_file.read(buffer_size)), b""):
                    offset += len(data)
                    display.vvvvv("PSRP PUT %s to %s (offset=%d, size=%d" %
                                  (in_path, out_path, offset, len(data)),
                                  host=self._psrp_host)
                    b64_data = base64.b64encode(data) + b"\r\n"
                    process.send(b64_data, end=(src_file.tell() == in_size))

                # the file was empty, return empty buffer
                if offset == 0:
                    process.send(b"", end=True)

            process.end_invoke()
            process.signal(SignalCode.CTRL_C)

        if process.rc != 0:
            raise AnsibleError(to_native(process.stderr))

        put_output = json.loads(process.stdout)
        remote_sha1 = put_output.get("sha1")

        if not remote_sha1:
            raise AnsibleError("Remote sha1 was not returned, stdout: '%s', "
                               "stderr: '%s'" % (to_native(process.stdout),
                                                 to_native(process.stderr)))

        local_sha1 = secure_hash(in_path)
        if not remote_sha1 == local_sha1:
            raise AnsibleError("Remote sha1 hash %s does not match local hash "
                               "%s" % (to_native(remote_sha1),
                                       to_native(local_sha1)))
Exemple #6
0
 def get_user_data(self):
     user_data = self.module.params.get('user_data')
     if user_data is not None:
         user_data = to_text(base64.b64encode(to_bytes(user_data)))
     return user_data
 def edit_config(self, command):
     for cmd in chain(['configure terminal'], to_list(command), ['end']):
         self.send_command(to_bytes(cmd))
Exemple #8
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 AnsibleActionFail(
                        "%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 AnsibleActionFail(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 AnsibleError(
                    'Unable to import Jinja2 defaults for determining Jinja2 features.'
                )

            try:
                jinja2.defaults.LSTRIP_BLOCKS
            except AttributeError:
                raise AnsibleError(
                    "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 AnsibleActionFail(
                    "'state' cannot be specified on a template")
            elif source is None or dest is None:
                raise AnsibleActionFail("src and dest are required")
            elif newline_sequence not in allowed_sequences:
                raise AnsibleActionFail(
                    "newline_sequence needs to be one of: \n, \r or \r\n")
            else:
                try:
                    source = self._find_needle('templates', source)
                except AnsibleError as e:
                    raise AnsibleActionFail(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 AnsibleFileNotFound as e:
                raise AnsibleActionFail("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 AnsibleActionFail(
                            "Template source files must be utf-8 encoded")

                # set jinja2 internal search path for includes
                searchpath = task_vars.get('ansible_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 ansible 'template' vars
                temp_vars = task_vars.copy()
                temp_vars.update(generate_ansible_template_vars(source, dest))

                with self._templar.set_temporary_context(
                        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 = self._templar.do_template(
                        template_data,
                        preserve_trailing_newlines=True,
                        escape_backslashes=False)
            except AnsibleAction:
                raise
            except Exception as e:
                raise AnsibleActionFail("%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,
                    ), )
                copy_action = self._shared_loader_obj.action_loader.get(
                    '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 AnsibleAction as e:
            result.update(e.result)
        finally:
            self._remove_tmp_path(self._connection._shell.tmpdir)

        return result
    def run(self, tmp=None, task_vars=None):
        socket_path = None
        network_os = self._get_network_os(task_vars).split('.')[-1]
        persistent_connection = self._play_context.connection.split('.')[-1]

        result = super(ActionModule, self).run(task_vars=task_vars)

        if persistent_connection != 'network_cli':
            # It is supported only with network_cli
            result['failed'] = True
            result['msg'] = (
                'connection type %s is not valid for net_put module,'
                ' please use fully qualified name of network_cli connection type'
                % self._play_context.connection)
            return result

        try:
            src = self._task.args['src']
        except KeyError as exc:
            return {
                'failed': True,
                'msg': 'missing required argument: %s' % exc
            }

        src_file_path_name = src

        # Get destination file if specified
        dest = self._task.args.get('dest')

        # Get proto
        proto = self._task.args.get('protocol')
        if proto is None:
            proto = 'scp'

        # Get mode if set
        mode = self._task.args.get('mode')
        if mode is None:
            mode = 'binary'

        if mode == 'text':
            try:
                self._handle_template(convert_data=False)
            except ValueError as exc:
                return dict(failed=True, msg=to_text(exc))

            # Now src has resolved file write to disk in current diectory for scp
            src = self._task.args.get('src')
            filename = str(uuid.uuid4())
            cwd = self._loader.get_basedir()
            output_file = os.path.join(cwd, filename)
            try:
                with open(output_file, 'wb') as f:
                    f.write(to_bytes(src, encoding='utf-8'))
            except Exception:
                os.remove(output_file)
                raise
        else:
            try:
                output_file = self._get_binary_src_file(src)
            except ValueError as exc:
                return dict(failed=True, msg=to_text(exc))

        if socket_path is None:
            socket_path = self._connection.socket_path

        conn = Connection(socket_path)
        sock_timeout = conn.get_option('persistent_command_timeout')

        if dest is None:
            dest = src_file_path_name

        try:
            changed = self._handle_existing_file(conn, output_file, dest,
                                                 proto, sock_timeout)
            if changed is False:
                result['changed'] = changed
                result['destination'] = dest
                return result
        except Exception as exc:
            result['msg'] = (
                'Warning: %s idempotency check failed. Check dest' % exc)

        try:
            conn.copy_file(source=output_file,
                           destination=dest,
                           proto=proto,
                           timeout=sock_timeout)
        except Exception as exc:
            if to_text(exc) == "No response from server":
                if network_os == 'iosxr':
                    # IOSXR sometimes closes socket prematurely after completion
                    # of file transfer
                    result[
                        'msg'] = 'Warning: iosxr scp server pre close issue. Please check dest'
            else:
                result['failed'] = True
                result['msg'] = 'Exception received: %s' % exc

        if mode == 'text':
            # Cleanup tmp file expanded wih ansible vars
            os.remove(output_file)

        result['changed'] = changed
        result['destination'] = dest
        return result
Exemple #10
0
def main():
    module = AnsibleModule(
        argument_spec=dict(
            path=dict(type='path', required=True, aliases=['dest', 'name']),
            follow=dict(type='bool', default=False),
            get_md5=dict(type='bool', default=False),
            get_checksum=dict(type='bool', default=True),
            get_mime=dict(type='bool', default=True, aliases=['mime', 'mime_type', 'mime-type']),
            get_attributes=dict(type='bool', default=True, aliases=['attr', 'attributes']),
            checksum_algorithm=dict(type='str', default='sha1',
                                    choices=['md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512'],
                                    aliases=['checksum', 'checksum_algo']),
        ),
        supports_check_mode=True,
    )

    path = module.params.get('path')
    b_path = to_bytes(path, errors='surrogate_or_strict')
    follow = module.params.get('follow')
    get_mime = module.params.get('get_mime')
    get_attr = module.params.get('get_attributes')
    get_checksum = module.params.get('get_checksum')
    checksum_algorithm = module.params.get('checksum_algorithm')

    # NOTE: undocumented option since 2.9 to be removed at a later date if possible (3.0+)
    # no real reason for keeping other than fear we may break older content.
    get_md5 = module.params.get('get_md5')

    # main stat data
    try:
        if follow:
            st = os.stat(b_path)
        else:
            st = os.lstat(b_path)
    except OSError as e:
        if e.errno == errno.ENOENT:
            output = {'exists': False}
            module.exit_json(changed=False, stat=output)

        module.fail_json(msg=e.strerror)

    # process base results
    output = format_output(module, path, st)

    # resolved permissions
    for perm in [('readable', os.R_OK), ('writeable', os.W_OK), ('executable', os.X_OK)]:
        output[perm[0]] = os.access(b_path, perm[1])

    # symlink info
    if output.get('islnk'):
        output['lnk_source'] = os.path.realpath(b_path)
        output['lnk_target'] = os.readlink(b_path)

    try:  # user data
        pw = pwd.getpwuid(st.st_uid)
        output['pw_name'] = pw.pw_name
    except (TypeError, KeyError):
        pass

    try:  # group data
        grp_info = grp.getgrgid(st.st_gid)
        output['gr_name'] = grp_info.gr_name
    except (KeyError, ValueError, OverflowError):
        pass

    # checksums
    if output.get('isreg') and output.get('readable'):

        # NOTE: see above about get_md5
        if get_md5:
            # Will fail on FIPS-140 compliant systems
            try:
                output['md5'] = module.md5(b_path)
            except ValueError:
                output['md5'] = None

        if get_checksum:
            output['checksum'] = module.digest_from_file(b_path, checksum_algorithm)

    # try to get mime data if requested
    if get_mime:
        output['mimetype'] = output['charset'] = 'unknown'
        mimecmd = module.get_bin_path('file')
        if mimecmd:
            mimecmd = [mimecmd, '-i', b_path]
            try:
                rc, out, err = module.run_command(mimecmd)
                if rc == 0:
                    mimetype, charset = out.split(':')[1].split(';')
                    output['mimetype'] = mimetype.strip()
                    output['charset'] = charset.split('=')[1].strip()
            except Exception:
                pass

    # try to get attr data
    if get_attr:
        output['version'] = None
        output['attributes'] = []
        output['attr_flags'] = ''
        out = module.get_file_attributes(b_path)
        for x in ('version', 'attributes', 'attr_flags'):
            if x in out:
                output[x] = out[x]

    module.exit_json(changed=False, stat=output)
    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='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 AnsibleError 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='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

            module_return = self._execute_module(module_name='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)
def ensure_hardlink(path, src, follow, force, timestamps):
    b_path = to_bytes(path, errors='surrogate_or_strict')
    b_src = to_bytes(src, errors='surrogate_or_strict')
    prev_state = get_state(b_path)
    file_args = module.load_file_common_arguments(module.params)
    mtime = get_timestamp_for_time(timestamps['modification_time'],
                                   timestamps['modification_time_format'])
    atime = get_timestamp_for_time(timestamps['access_time'],
                                   timestamps['access_time_format'])

    # src is the source of a hardlink.  We require it if we are creating a new hardlink.
    # We require path in the argument_spec so we know it is present at this point.
    if src is None:
        raise AnsibleModuleError(
            results={'msg': 'src is required for creating new hardlinks'})

    if not os.path.exists(b_src):
        raise AnsibleModuleError(results={
            'msg': 'src does not exist',
            'dest': path,
            'src': src
        })

    diff = initial_diff(path, 'hard', prev_state)
    changed = False

    if prev_state == 'absent':
        changed = True
    elif prev_state == 'link':
        b_old_src = os.readlink(b_path)
        if b_old_src != b_src:
            diff['before']['src'] = to_native(b_old_src, errors='strict')
            diff['after']['src'] = src
            changed = True
    elif prev_state == 'hard':
        if not os.stat(b_path).st_ino == os.stat(b_src).st_ino:
            changed = True
            if not force:
                raise AnsibleModuleError(
                    results={
                        'msg':
                        'Cannot link, different hard link exists at destination',
                        'dest': path,
                        'src': src
                    })
    elif prev_state == 'file':
        changed = True
        if not force:
            raise AnsibleModuleError(
                results={
                    'msg': 'Cannot link, %s exists at destination' %
                    prev_state,
                    'dest': path,
                    'src': src
                })
    elif prev_state == 'directory':
        changed = True
        if os.path.exists(b_path):
            if os.stat(b_path).st_ino == os.stat(b_src).st_ino:
                return {'path': path, 'changed': False}
            elif not force:
                raise AnsibleModuleError(
                    results={
                        'msg':
                        'Cannot link: different hard link exists at destination',
                        'dest': path,
                        'src': src
                    })
    else:
        raise AnsibleModuleError(results={
            'msg': 'unexpected position reached',
            'dest': path,
            'src': src
        })

    if changed and not module.check_mode:
        if prev_state != 'absent':
            # try to replace atomically
            b_tmppath = to_bytes(os.path.sep).join([
                os.path.dirname(b_path),
                to_bytes(".%s.%s.tmp" % (os.getpid(), time.time()))
            ])
            try:
                if prev_state == 'directory':
                    if os.path.exists(b_path):
                        try:
                            os.unlink(b_path)
                        except OSError as e:
                            if e.errno != errno.ENOENT:  # It may already have been removed
                                raise
                os.link(b_src, b_tmppath)
                os.rename(b_tmppath, b_path)
            except OSError as e:
                if os.path.exists(b_tmppath):
                    os.unlink(b_tmppath)
                raise AnsibleModuleError(
                    results={
                        'msg':
                        'Error while replacing: %s' %
                        to_native(e, nonstring='simplerepr'),
                        'path':
                        path
                    })
        else:
            try:
                os.link(b_src, b_path)
            except OSError as e:
                raise AnsibleModuleError(
                    results={
                        'msg':
                        'Error while linking: %s' %
                        to_native(e, nonstring='simplerepr'),
                        'path':
                        path
                    })

    if module.check_mode and not os.path.exists(b_path):
        return {'dest': path, 'src': src, 'changed': changed, 'diff': diff}

    changed = module.set_fs_attributes_if_different(file_args,
                                                    changed,
                                                    diff,
                                                    expand=False)
    changed |= update_timestamp_for_file(file_args['path'], mtime, atime, diff)

    return {'dest': path, 'src': src, 'changed': changed, 'diff': diff}
def ensure_symlink(path, src, follow, force, timestamps):
    b_path = to_bytes(path, errors='surrogate_or_strict')
    b_src = to_bytes(src, errors='surrogate_or_strict')
    prev_state = get_state(b_path)
    mtime = get_timestamp_for_time(timestamps['modification_time'],
                                   timestamps['modification_time_format'])
    atime = get_timestamp_for_time(timestamps['access_time'],
                                   timestamps['access_time_format'])
    # source is both the source of a symlink or an informational passing of the src for a template module
    # or copy module, even if this module never uses it, it is needed to key off some things
    if src is None:
        if follow:
            # use the current target of the link as the source
            src = to_native(os.path.realpath(b_path), errors='strict')
            b_src = to_bytes(src, errors='surrogate_or_strict')

    if not os.path.islink(b_path) and os.path.isdir(b_path):
        relpath = path
    else:
        b_relpath = os.path.dirname(b_path)
        relpath = to_native(b_relpath, errors='strict')

    absrc = os.path.join(relpath, src)
    b_absrc = to_bytes(absrc, errors='surrogate_or_strict')
    if not force and not os.path.exists(b_absrc):
        raise AnsibleModuleError(
            results={
                'msg':
                'src file does not exist, use "force=yes" if you'
                ' really want to create the link: %s' % absrc,
                'path':
                path,
                'src':
                src
            })

    if prev_state == 'directory':
        if not force:
            raise AnsibleModuleError(
                results={
                    'msg':
                    'refusing to convert from %s to symlink for %s' %
                    (prev_state, path),
                    'path':
                    path
                })
        elif os.listdir(b_path):
            # refuse to replace a directory that has files in it
            raise AnsibleModuleError(
                results={
                    'msg':
                    'the directory %s is not empty, refusing to'
                    ' convert it' % path,
                    'path':
                    path
                })
    elif prev_state in ('file', 'hard') and not force:
        raise AnsibleModuleError(
            results={
                'msg':
                'refusing to convert from %s to symlink for %s' %
                (prev_state, path),
                'path':
                path
            })

    diff = initial_diff(path, 'link', prev_state)
    changed = False

    if prev_state == 'absent':
        changed = True
    elif prev_state == 'link':
        b_old_src = os.readlink(b_path)
        if b_old_src != b_src:
            diff['before']['src'] = to_native(b_old_src, errors='strict')
            diff['after']['src'] = src
            changed = True
    elif prev_state == 'hard':
        changed = True
        if not force:
            raise AnsibleModuleError(
                results={
                    'msg':
                    'Cannot link because a hard link exists at destination',
                    'dest': path,
                    'src': src
                })
    elif prev_state == 'file':
        changed = True
        if not force:
            raise AnsibleModuleError(
                results={
                    'msg': 'Cannot link because a file exists at destination',
                    'dest': path,
                    'src': src
                })
    elif prev_state == 'directory':
        changed = True
        if os.path.exists(b_path):
            if not force:
                raise AnsibleModuleError(
                    results={
                        'msg':
                        'Cannot link because a file exists at destination',
                        'dest': path,
                        'src': src
                    })
    else:
        raise AnsibleModuleError(results={
            'msg': 'unexpected position reached',
            'dest': path,
            'src': src
        })

    if changed and not module.check_mode:
        if prev_state != 'absent':
            # try to replace atomically
            b_tmppath = to_bytes(os.path.sep).join([
                os.path.dirname(b_path),
                to_bytes(".%s.%s.tmp" % (os.getpid(), time.time()))
            ])
            try:
                if prev_state == 'directory':
                    os.rmdir(b_path)
                os.symlink(b_src, b_tmppath)
                os.rename(b_tmppath, b_path)
            except OSError as e:
                if os.path.exists(b_tmppath):
                    os.unlink(b_tmppath)
                raise AnsibleModuleError(
                    results={
                        'msg':
                        'Error while replacing: %s' %
                        to_native(e, nonstring='simplerepr'),
                        'path':
                        path
                    })
        else:
            try:
                os.symlink(b_src, b_path)
            except OSError as e:
                raise AnsibleModuleError(
                    results={
                        'msg':
                        'Error while linking: %s' %
                        to_native(e, nonstring='simplerepr'),
                        'path':
                        path
                    })

    if module.check_mode and not os.path.exists(b_path):
        return {'dest': path, 'src': src, 'changed': changed, 'diff': diff}

    # Now that we might have created the symlink, get the arguments.
    # We need to do it now so we can properly follow the symlink if needed
    # because load_file_common_arguments sets 'path' according
    # the value of follow and the symlink existence.
    file_args = module.load_file_common_arguments(module.params)

    # Whenever we create a link to a nonexistent target we know that the nonexistent target
    # cannot have any permissions set on it.  Skip setting those and emit a warning (the user
    # can set follow=False to remove the warning)
    if follow and os.path.islink(b_path) and not os.path.exists(
            file_args['path']):
        module.warn(
            'Cannot set fs attributes on a non-existent symlink target. follow should be'
            ' set to False to avoid this.')
    else:
        changed = module.set_fs_attributes_if_different(file_args,
                                                        changed,
                                                        diff,
                                                        expand=False)
        changed |= update_timestamp_for_file(file_args['path'], mtime, atime,
                                             diff)

    return {'dest': path, 'src': src, 'changed': changed, 'diff': diff}
def ensure_directory(path, follow, recurse, timestamps):
    b_path = to_bytes(path, errors='surrogate_or_strict')
    prev_state = get_state(b_path)
    file_args = module.load_file_common_arguments(module.params)
    mtime = get_timestamp_for_time(timestamps['modification_time'],
                                   timestamps['modification_time_format'])
    atime = get_timestamp_for_time(timestamps['access_time'],
                                   timestamps['access_time_format'])

    # For followed symlinks, we need to operate on the target of the link
    if follow and prev_state == 'link':
        b_path = os.path.realpath(b_path)
        path = to_native(b_path, errors='strict')
        file_args['path'] = path
        prev_state = get_state(b_path)

    changed = False
    diff = initial_diff(path, 'directory', prev_state)

    if prev_state == 'absent':
        # Create directory and assign permissions to it
        if module.check_mode:
            return {'changed': True, 'diff': diff}
        curpath = ''

        try:
            # Split the path so we can apply filesystem attributes recursively
            # from the root (/) directory for absolute paths or the base path
            # of a relative path.  We can then walk the appropriate directory
            # path to apply attributes.
            # Something like mkdir -p with mode applied to all of the newly created directories
            for dirname in path.strip('/').split('/'):
                curpath = '/'.join([curpath, dirname])
                # Remove leading slash if we're creating a relative path
                if not os.path.isabs(path):
                    curpath = curpath.lstrip('/')
                b_curpath = to_bytes(curpath, errors='surrogate_or_strict')
                if not os.path.exists(b_curpath):
                    try:
                        os.mkdir(b_curpath)
                        changed = True
                    except OSError as ex:
                        # Possibly something else created the dir since the os.path.exists
                        # check above. As long as it's a dir, we don't need to error out.
                        if not (ex.errno == errno.EEXIST
                                and os.path.isdir(b_curpath)):
                            raise
                    tmp_file_args = file_args.copy()
                    tmp_file_args['path'] = curpath
                    changed = module.set_fs_attributes_if_different(
                        tmp_file_args, changed, diff, expand=False)
                    changed |= update_timestamp_for_file(
                        file_args['path'], mtime, atime, diff)
        except Exception as e:
            raise AnsibleModuleError(
                results={
                    'msg':
                    'There was an issue creating %s as requested:'
                    ' %s' % (curpath, to_native(e)),
                    'path':
                    path
                })
        return {'path': path, 'changed': changed, 'diff': diff}

    elif prev_state != 'directory':
        # We already know prev_state is not 'absent', therefore it exists in some form.
        raise AnsibleModuleError(
            results={
                'msg': '%s already exists as a %s' % (path, prev_state),
                'path': path
            })

    #
    # previous state == directory
    #

    changed = module.set_fs_attributes_if_different(file_args,
                                                    changed,
                                                    diff,
                                                    expand=False)
    changed |= update_timestamp_for_file(file_args['path'], mtime, atime, diff)
    if recurse:
        changed |= recursive_set_attributes(b_path, follow, file_args, mtime,
                                            atime)

    return {'path': path, 'changed': changed, 'diff': diff}
def main():
    module = AnsibleModule(
        argument_spec=dict(
            group_id=dict(required=True),
            artifact_id=dict(required=True),
            version=dict(default=None),
            version_by_spec=dict(default=None),
            classifier=dict(default=''),
            extension=dict(default='jar'),
            repository_url=dict(default='https://repo1.maven.org/maven2'),
            username=dict(default=None, aliases=['aws_secret_key']),
            password=dict(default=None, no_log=True, aliases=['aws_secret_access_key']),
            headers=dict(type='dict'),
            force_basic_auth=dict(default=False, type='bool'),
            state=dict(default="present", choices=["present", "absent"]),  # TODO - Implement a "latest" state
            timeout=dict(default=10, type='int'),
            dest=dict(type="path", required=True),
            validate_certs=dict(required=False, default=True, type='bool'),
            keep_name=dict(required=False, default=False, type='bool'),
            verify_checksum=dict(required=False, default='download', choices=['never', 'download', 'change', 'always'])
        ),
        add_file_common_args=True,
        mutually_exclusive=([('version', 'version_by_spec')])
    )

    if not HAS_LXML_ETREE:
        module.fail_json(msg=missing_required_lib('lxml'), exception=LXML_ETREE_IMP_ERR)

    if module.params['version_by_spec'] and not HAS_SEMANTIC_VERSION:
        module.fail_json(msg=missing_required_lib('semantic_version'), exception=SEMANTIC_VERSION_IMP_ERR)

    repository_url = module.params["repository_url"]
    if not repository_url:
        repository_url = "https://repo1.maven.org/maven2"
    try:
        parsed_url = urlparse(repository_url)
    except AttributeError as e:
        module.fail_json(msg='url parsing went wrong %s' % e)

    local = parsed_url.scheme == "file"

    if parsed_url.scheme == 's3' and not HAS_BOTO:
        module.fail_json(msg=missing_required_lib('boto3', reason='when using s3:// repository URLs'),
                         exception=BOTO_IMP_ERR)

    group_id = module.params["group_id"]
    artifact_id = module.params["artifact_id"]
    version = module.params["version"]
    version_by_spec = module.params["version_by_spec"]
    classifier = module.params["classifier"]
    extension = module.params["extension"]
    headers = module.params['headers']
    state = module.params["state"]
    dest = module.params["dest"]
    b_dest = to_bytes(dest, errors='surrogate_or_strict')
    keep_name = module.params["keep_name"]
    verify_checksum = module.params["verify_checksum"]
    verify_download = verify_checksum in ['download', 'always']
    verify_change = verify_checksum in ['change', 'always']

    downloader = MavenDownloader(module, repository_url, local, headers)

    if not version_by_spec and not version:
        version = "latest"

    try:
        artifact = Artifact(group_id, artifact_id, version, version_by_spec, classifier, extension)
    except ValueError as e:
        module.fail_json(msg=e.args[0])

    changed = False
    prev_state = "absent"

    if dest.endswith(os.sep):
        b_dest = to_bytes(dest, errors='surrogate_or_strict')
        if not os.path.exists(b_dest):
            (pre_existing_dir, new_directory_list) = split_pre_existing_dir(dest)
            os.makedirs(b_dest)
            directory_args = module.load_file_common_arguments(module.params)
            directory_mode = module.params["directory_mode"]
            if directory_mode is not None:
                directory_args['mode'] = directory_mode
            else:
                directory_args['mode'] = None
            changed = adjust_recursive_directory_permissions(pre_existing_dir, new_directory_list, module, directory_args, changed)

    if os.path.isdir(b_dest):
        version_part = version
        if version == 'latest':
            version_part = downloader.find_latest_version_available(artifact)
        elif version_by_spec:
            version_part = downloader.find_version_by_spec(artifact)

        filename = "{artifact_id}{version_part}{classifier}.{extension}".format(
            artifact_id=artifact_id,
            version_part="-{0}".format(version_part) if keep_name else "",
            classifier="-{0}".format(classifier) if classifier else "",
            extension=extension
        )
        dest = posixpath.join(dest, filename)

        b_dest = to_bytes(dest, errors='surrogate_or_strict')

    if os.path.lexists(b_dest) and ((not verify_change) or not downloader.is_invalid_md5(dest, downloader.find_uri_for_artifact(artifact))):
        prev_state = "present"

    if prev_state == "absent":
        try:
            download_error = downloader.download(module.tmpdir, artifact, verify_download, b_dest)
            if download_error is None:
                changed = True
            else:
                module.fail_json(msg="Cannot retrieve the artifact to destination: " + download_error)
        except ValueError as e:
            module.fail_json(msg=e.args[0])

    module.params['dest'] = dest
    file_args = module.load_file_common_arguments(module.params)
    changed = module.set_fs_attributes_if_different(file_args, changed)
    if changed:
        module.exit_json(state=state, dest=dest, group_id=group_id, artifact_id=artifact_id, version=version, classifier=classifier,
                         extension=extension, repository_url=repository_url, changed=changed)
    else:
        module.exit_json(state=state, dest=dest, changed=changed)
Exemple #16
0
    def is_unarchived(self):
        # BSD unzip doesn't support zipinfo listings with timestamp.
        cmd = [self.zipinfocmd_path, '-T', '-s', self.src]
        if self.excludes:
            cmd.extend(['-x', ] + self.excludes)
        rc, out, err = self.module.run_command(cmd)

        old_out = out
        diff = ''
        out = ''
        if rc == 0:
            unarchived = True
        else:
            unarchived = False

        # Get some information related to user/group ownership
        umask = os.umask(0)
        os.umask(umask)
        systemtype = platform.system()

        # Get current user and group information
        groups = os.getgroups()
        run_uid = os.getuid()
        run_gid = os.getgid()
        try:
            run_owner = pwd.getpwuid(run_uid).pw_name
        except (TypeError, KeyError):
            run_owner = run_uid
        try:
            run_group = grp.getgrgid(run_gid).gr_name
        except (KeyError, ValueError, OverflowError):
            run_group = run_gid

        # Get future user ownership
        fut_owner = fut_uid = None
        if self.file_args['owner']:
            try:
                tpw = pwd.getpwnam(self.file_args['owner'])
            except KeyError:
                try:
                    tpw = pwd.getpwuid(self.file_args['owner'])
                except (TypeError, KeyError):
                    tpw = pwd.getpwuid(run_uid)
            fut_owner = tpw.pw_name
            fut_uid = tpw.pw_uid
        else:
            try:
                fut_owner = run_owner
            except Exception:
                pass
            fut_uid = run_uid

        # Get future group ownership
        fut_group = fut_gid = None
        if self.file_args['group']:
            try:
                tgr = grp.getgrnam(self.file_args['group'])
            except (ValueError, KeyError):
                try:
                    tgr = grp.getgrgid(self.file_args['group'])
                except (KeyError, ValueError, OverflowError):
                    tgr = grp.getgrgid(run_gid)
            fut_group = tgr.gr_name
            fut_gid = tgr.gr_gid
        else:
            try:
                fut_group = run_group
            except Exception:
                pass
            fut_gid = run_gid

        for line in old_out.splitlines():
            change = False

            pcs = line.split(None, 7)
            if len(pcs) != 8:
                # Too few fields... probably a piece of the header or footer
                continue

            # Check first and seventh field in order to skip header/footer
            if len(pcs[0]) != 7 and len(pcs[0]) != 10:
                continue
            if len(pcs[6]) != 15:
                continue

            # Possible entries:
            #   -rw-rws---  1.9 unx    2802 t- defX 11-Aug-91 13:48 perms.2660
            #   -rw-a--     1.0 hpf    5358 Tl i4:3  4-Dec-91 11:33 longfilename.hpfs
            #   -r--ahs     1.1 fat    4096 b- i4:2 14-Jul-91 12:58 EA DATA. SF
            #   --w-------  1.0 mac   17357 bx i8:2  4-May-92 04:02 unzip.macr
            if pcs[0][0] not in 'dl-?' or not frozenset(pcs[0][1:]).issubset('rwxstah-'):
                continue

            ztype = pcs[0][0]
            permstr = pcs[0][1:]
            version = pcs[1]
            ostype = pcs[2]
            size = int(pcs[3])
            path = to_text(pcs[7], errors='surrogate_or_strict')

            # Skip excluded files
            if path in self.excludes:
                out += 'Path %s is excluded on request\n' % path
                continue

            # Itemized change requires L for symlink
            if path[-1] == '/':
                if ztype != 'd':
                    err += 'Path %s incorrectly tagged as "%s", but is a directory.\n' % (path, ztype)
                ftype = 'd'
            elif ztype == 'l':
                ftype = 'L'
            elif ztype == '-':
                ftype = 'f'
            elif ztype == '?':
                ftype = 'f'

            # Some files may be storing FAT permissions, not Unix permissions
            # For FAT permissions, we will use a base permissions set of 777 if the item is a directory or has the execute bit set.  Otherwise, 666.
            #     This permission will then be modified by the system UMask.
            # BSD always applies the Umask, even to Unix permissions.
            # For Unix style permissions on Linux or Mac, we want to use them directly.
            #     So we set the UMask for this file to zero.  That permission set will then be unchanged when calling _permstr_to_octal

            if len(permstr) == 6:
                if path[-1] == '/':
                    permstr = 'rwxrwxrwx'
                elif permstr == 'rwx---':
                    permstr = 'rwxrwxrwx'
                else:
                    permstr = 'rw-rw-rw-'
                file_umask = umask
            elif 'bsd' in systemtype.lower():
                file_umask = umask
            else:
                file_umask = 0

            # Test string conformity
            if len(permstr) != 9 or not ZIP_FILE_MODE_RE.match(permstr):
                raise UnarchiveError('ZIP info perm format incorrect, %s' % permstr)

            # DEBUG
#            err += "%s%s %10d %s\n" % (ztype, permstr, size, path)

            b_dest = os.path.join(self.b_dest, to_bytes(path, errors='surrogate_or_strict'))
            try:
                st = os.lstat(b_dest)
            except Exception:
                change = True
                self.includes.append(path)
                err += 'Path %s is missing\n' % path
                diff += '>%s++++++.?? %s\n' % (ftype, path)
                continue

            # Compare file types
            if ftype == 'd' and not stat.S_ISDIR(st.st_mode):
                change = True
                self.includes.append(path)
                err += 'File %s already exists, but not as a directory\n' % path
                diff += 'c%s++++++.?? %s\n' % (ftype, path)
                continue

            if ftype == 'f' and not stat.S_ISREG(st.st_mode):
                change = True
                unarchived = False
                self.includes.append(path)
                err += 'Directory %s already exists, but not as a regular file\n' % path
                diff += 'c%s++++++.?? %s\n' % (ftype, path)
                continue

            if ftype == 'L' and not stat.S_ISLNK(st.st_mode):
                change = True
                self.includes.append(path)
                err += 'Directory %s already exists, but not as a symlink\n' % path
                diff += 'c%s++++++.?? %s\n' % (ftype, path)
                continue

            itemized = list('.%s.......??' % ftype)

            # Note: this timestamp calculation has a rounding error
            # somewhere... unzip and this timestamp can be one second off
            # When that happens, we report a change and re-unzip the file
            dt_object = datetime.datetime(*(time.strptime(pcs[6], '%Y%m%d.%H%M%S')[0:6]))
            timestamp = time.mktime(dt_object.timetuple())

            # Compare file timestamps
            if stat.S_ISREG(st.st_mode):
                if self.module.params['keep_newer']:
                    if timestamp > st.st_mtime:
                        change = True
                        self.includes.append(path)
                        err += 'File %s is older, replacing file\n' % path
                        itemized[4] = 't'
                    elif stat.S_ISREG(st.st_mode) and timestamp < st.st_mtime:
                        # Add to excluded files, ignore other changes
                        out += 'File %s is newer, excluding file\n' % path
                        self.excludes.append(path)
                        continue
                else:
                    if timestamp != st.st_mtime:
                        change = True
                        self.includes.append(path)
                        err += 'File %s differs in mtime (%f vs %f)\n' % (path, timestamp, st.st_mtime)
                        itemized[4] = 't'

            # Compare file sizes
            if stat.S_ISREG(st.st_mode) and size != st.st_size:
                change = True
                err += 'File %s differs in size (%d vs %d)\n' % (path, size, st.st_size)
                itemized[3] = 's'

            # Compare file checksums
            if stat.S_ISREG(st.st_mode):
                crc = crc32(b_dest)
                if crc != self._crc32(path):
                    change = True
                    err += 'File %s differs in CRC32 checksum (0x%08x vs 0x%08x)\n' % (path, self._crc32(path), crc)
                    itemized[2] = 'c'

            # Compare file permissions

            # Do not handle permissions of symlinks
            if ftype != 'L':

                # Use the new mode provided with the action, if there is one
                if self.file_args['mode']:
                    if isinstance(self.file_args['mode'], int):
                        mode = self.file_args['mode']
                    else:
                        try:
                            mode = int(self.file_args['mode'], 8)
                        except Exception as e:
                            try:
                                mode = AnsibleModule._symbolic_mode_to_octal(st, self.file_args['mode'])
                            except ValueError as e:
                                self.module.fail_json(path=path, msg="%s" % to_native(e), exception=traceback.format_exc())
                # Only special files require no umask-handling
                elif ztype == '?':
                    mode = self._permstr_to_octal(permstr, 0)
                else:
                    mode = self._permstr_to_octal(permstr, file_umask)

                if mode != stat.S_IMODE(st.st_mode):
                    change = True
                    itemized[5] = 'p'
                    err += 'Path %s differs in permissions (%o vs %o)\n' % (path, mode, stat.S_IMODE(st.st_mode))

            # Compare file user ownership
            owner = uid = None
            try:
                owner = pwd.getpwuid(st.st_uid).pw_name
            except (TypeError, KeyError):
                uid = st.st_uid

            # If we are not root and requested owner is not our user, fail
            if run_uid != 0 and (fut_owner != run_owner or fut_uid != run_uid):
                raise UnarchiveError('Cannot change ownership of %s to %s, as user %s' % (path, fut_owner, run_owner))

            if owner and owner != fut_owner:
                change = True
                err += 'Path %s is owned by user %s, not by user %s as expected\n' % (path, owner, fut_owner)
                itemized[6] = 'o'
            elif uid and uid != fut_uid:
                change = True
                err += 'Path %s is owned by uid %s, not by uid %s as expected\n' % (path, uid, fut_uid)
                itemized[6] = 'o'

            # Compare file group ownership
            group = gid = None
            try:
                group = grp.getgrgid(st.st_gid).gr_name
            except (KeyError, ValueError, OverflowError):
                gid = st.st_gid

            if run_uid != 0 and fut_gid not in groups:
                raise UnarchiveError('Cannot change group ownership of %s to %s, as user %s' % (path, fut_group, run_owner))

            if group and group != fut_group:
                change = True
                err += 'Path %s is owned by group %s, not by group %s as expected\n' % (path, group, fut_group)
                itemized[6] = 'g'
            elif gid and gid != fut_gid:
                change = True
                err += 'Path %s is owned by gid %s, not by gid %s as expected\n' % (path, gid, fut_gid)
                itemized[6] = 'g'

            # Register changed files and finalize diff output
            if change:
                if path not in self.includes:
                    self.includes.append(path)
                diff += '%s %s\n' % (''.join(itemized), path)

        if self.includes:
            unarchived = False

        # DEBUG
#        out = old_out + out

        return dict(unarchived=unarchived, rc=rc, out=out, err=err, cmd=cmd, diff=diff)
def main():

    # the command module is the one ansible module that does not take key=value args
    # hence don't copy this one if you are looking to build others!
    module = AnsibleModule(
        argument_spec=dict(
            _raw_params=dict(),
            _uses_shell=dict(type='bool', default=False),
            argv=dict(type='list'),
            chdir=dict(type='path'),
            executable=dict(),
            creates=dict(type='path'),
            removes=dict(type='path'),
            # The default for this really comes from the action plugin
            warn=dict(type='bool', default=True),
            stdin=dict(required=False),
            stdin_add_newline=dict(type='bool', default=True),
            strip_empty_ends=dict(type='bool', default=True),
        ),
        supports_check_mode=True,
    )
    shell = module.params['_uses_shell']
    chdir = module.params['chdir']
    executable = module.params['executable']
    args = module.params['_raw_params']
    argv = module.params['argv']
    creates = module.params['creates']
    removes = module.params['removes']
    warn = module.params['warn']
    stdin = module.params['stdin']
    stdin_add_newline = module.params['stdin_add_newline']
    strip = module.params['strip_empty_ends']

    if not shell and executable:
        module.warn(
            "As of Ansible 2.4, the parameter 'executable' is no longer supported with the 'command' module. Not using '%s'."
            % executable)
        executable = None

    if (not args or args.strip() == '') and not argv:
        module.fail_json(rc=256, msg="no command given")

    if args and argv:
        module.fail_json(rc=256,
                         msg="only command or argv can be given, not both")

    if not shell and args:
        args = shlex.split(args)

    args = args or argv

    # All args must be strings
    if is_iterable(args, include_strings=False):
        args = [
            to_native(arg,
                      errors='surrogate_or_strict',
                      nonstring='simplerepr') for arg in args
        ]

    if chdir:
        try:
            chdir = to_bytes(os.path.abspath(chdir),
                             errors='surrogate_or_strict')
        except ValueError as e:
            module.fail_json(msg='Unable to use supplied chdir: %s' %
                             to_text(e))

        try:
            os.chdir(chdir)
        except (IOError, OSError) as e:
            module.fail_json(
                msg='Unable to change directory before execution: %s' %
                to_text(e))

    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 glob.glob(creates):
            module.exit_json(cmd=args,
                             stdout="skipped, since %s exists" % creates,
                             changed=False,
                             rc=0)

    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 glob.glob(removes):
            module.exit_json(cmd=args,
                             stdout="skipped, since %s does not exist" %
                             removes,
                             changed=False,
                             rc=0)

    if warn:
        check_command(module, args)

    startd = datetime.datetime.now()

    if not module.check_mode:
        rc, out, err = module.run_command(args,
                                          executable=executable,
                                          use_unsafe_shell=shell,
                                          encoding=None,
                                          data=stdin,
                                          binary_data=(not stdin_add_newline))
    elif creates or removes:
        rc = 0
        out = err = b'Command would have run if not in check mode'
    else:
        module.exit_json(msg="skipped, running in check mode", skipped=True)

    endd = datetime.datetime.now()
    delta = endd - startd

    if strip:
        out = out.rstrip(b"\r\n")
        err = err.rstrip(b"\r\n")

    result = dict(
        cmd=args,
        stdout=out,
        stderr=err,
        rc=rc,
        start=str(startd),
        end=str(endd),
        delta=str(delta),
        changed=True,
    )

    if rc != 0:
        module.fail_json(msg='non-zero return code', **result)

    module.exit_json(**result)
Exemple #18
0
def main():
    module = AnsibleModule(
        # not checking because of daisy chain to file module
        argument_spec=dict(
            src=dict(type='path', required=True),
            dest=dict(type='path', required=True),
            remote_src=dict(type='bool', default=False),
            creates=dict(type='path'),
            list_files=dict(type='bool', default=False),
            keep_newer=dict(type='bool', default=False),
            exclude=dict(type='list', default=[]),
            extra_opts=dict(type='list', default=[]),
            validate_certs=dict(type='bool', default=True),
        ),
        add_file_common_args=True,
        # check-mode only works for zip files, we cover that later
        supports_check_mode=True,
    )

    src = module.params['src']
    dest = module.params['dest']
    b_dest = to_bytes(dest, errors='surrogate_or_strict')
    remote_src = module.params['remote_src']
    file_args = module.load_file_common_arguments(module.params)

    # did tar file arrive?
    if not os.path.exists(src):
        if not remote_src:
            module.fail_json(msg="Source '%s' failed to transfer" % src)
        # If remote_src=true, and src= contains ://, try and download the file to a temp directory.
        elif '://' in src:
            src = fetch_file(module, src)
        else:
            module.fail_json(msg="Source '%s' does not exist" % src)
    if not os.access(src, os.R_OK):
        module.fail_json(msg="Source '%s' not readable" % src)

    # skip working with 0 size archives
    try:
        if os.path.getsize(src) == 0:
            module.fail_json(msg="Invalid archive '%s', the file is 0 bytes" % src)
    except Exception as e:
        module.fail_json(msg="Source '%s' not readable, %s" % (src, to_native(e)))

    # is dest OK to receive tar file?
    if not os.path.isdir(b_dest):
        module.fail_json(msg="Destination '%s' is not a directory" % dest)

    handler = pick_handler(src, b_dest, file_args, module)

    res_args = dict(handler=handler.__class__.__name__, dest=dest, src=src)

    # do we need to do unpack?
    check_results = handler.is_unarchived()

    # DEBUG
    # res_args['check_results'] = check_results

    if module.check_mode:
        res_args['changed'] = not check_results['unarchived']
    elif check_results['unarchived']:
        res_args['changed'] = False
    else:
        # do the unpack
        try:
            res_args['extract_results'] = handler.unarchive()
            if res_args['extract_results']['rc'] != 0:
                module.fail_json(msg="failed to unpack %s to %s" % (src, dest), **res_args)
        except IOError:
            module.fail_json(msg="failed to unpack %s to %s" % (src, dest), **res_args)
        else:
            res_args['changed'] = True

    # Get diff if required
    if check_results.get('diff', False):
        res_args['diff'] = {'prepared': check_results['diff']}

    # Run only if we found differences (idempotence) or diff was missing
    if res_args.get('diff', True) and not module.check_mode:
        # do we need to change perms?
        for filename in handler.files_in_archive:
            file_args['path'] = os.path.join(b_dest, to_bytes(filename, errors='surrogate_or_strict'))

            try:
                res_args['changed'] = module.set_fs_attributes_if_different(file_args, res_args['changed'], expand=False)
            except (IOError, OSError) as e:
                module.fail_json(msg="Unexpected error when accessing exploded file: %s" % to_native(e), **res_args)

    if module.params['list_files']:
        res_args['files'] = handler.files_in_archive

    module.exit_json(**res_args)
Exemple #19
0
def main():
    module = AnsibleModule(
        argument_spec=dict(name=dict(type='str', required=True),
                           state=dict(type='str',
                                      required=True,
                                      choices=[
                                          'absent', 'opts_absent',
                                          'opts_present', 'present'
                                      ]),
                           backing_device=dict(type='str'),
                           password=dict(type='path'),
                           opts=dict(type='str'),
                           path=dict(type='path', default='/etc/crypttab')),
        supports_check_mode=True,
    )

    backing_device = module.params['backing_device']
    password = module.params['password']
    opts = module.params['opts']
    state = module.params['state']
    path = module.params['path']
    name = module.params['name']
    if name.startswith('/dev/mapper/'):
        name = name[len('/dev/mapper/'):]

    if state != 'absent' and backing_device is None and password is None and opts is None:
        module.fail_json(
            msg=
            "expected one or more of 'backing_device', 'password' or 'opts'",
            **module.params)

    if 'opts' in state and (backing_device is not None
                            or password is not None):
        module.fail_json(
            msg="cannot update 'backing_device' or 'password' when state=%s" %
            state,
            **module.params)

    for arg_name, arg in (('name', name), ('backing_device', backing_device),
                          ('password', password), ('opts', opts)):
        if (arg is not None and (' ' in arg or '\t' in arg or arg == '')):
            module.fail_json(
                msg="invalid '%s': contains white space or is empty" %
                arg_name,
                **module.params)

    try:
        crypttab = Crypttab(path)
        existing_line = crypttab.match(name)
    except Exception as e:
        module.fail_json(msg="failed to open and parse crypttab file: %s" %
                         to_native(e),
                         exception=traceback.format_exc(),
                         **module.params)

    if 'present' in state and existing_line is None and backing_device is None:
        module.fail_json(msg="'backing_device' required to add a new entry",
                         **module.params)

    changed, reason = False, '?'

    if state == 'absent':
        if existing_line is not None:
            changed, reason = existing_line.remove()

    elif state == 'present':
        if existing_line is not None:
            changed, reason = existing_line.set(backing_device, password, opts)
        else:
            changed, reason = crypttab.add(
                Line(None, name, backing_device, password, opts))

    elif state == 'opts_present':
        if existing_line is not None:
            changed, reason = existing_line.opts.add(opts)
        else:
            changed, reason = crypttab.add(
                Line(None, name, backing_device, password, opts))

    elif state == 'opts_absent':
        if existing_line is not None:
            changed, reason = existing_line.opts.remove(opts)

    if changed and not module.check_mode:
        try:
            f = open(path, 'wb')
            f.write(to_bytes(crypttab, errors='surrogate_or_strict'))
        finally:
            f.close()

    module.exit_json(changed=changed, msg=reason, **module.params)
Exemple #20
0
def test_to_bytes(in_string, encoding, expected):
    """test happy path of encoding to bytes"""
    assert to_bytes(in_string, encoding) == expected
def b64decode(string, encoding='utf-8'):
    return to_text(base64.b64decode(to_bytes(string, errors='surrogate_or_strict')), encoding=encoding)
Exemple #22
0
def test_to_bytes_unsafe():
    assert isinstance(to_bytes(AnsibleUnsafeText(u'foo')), AnsibleUnsafeBytes)
    assert to_bytes(AnsibleUnsafeText(u'foo')) == AnsibleUnsafeBytes(b'foo')
Exemple #23
0
#
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type

from os import path
import json

from mock import MagicMock

from ansible_collections.notstdlib.moveitallout.tests.unit.compat import unittest
from ansible_collections.notstdlib.moveitallout.plugins.cliconf import ios
from ansible_collections.notstdlib.moveitallout.plugins.module_utils._text import to_bytes


b_FIXTURE_DIR = b'%s/fixtures/ios' % (
    to_bytes(path.dirname(path.abspath(__file__)), errors='surrogate_or_strict')
)


def _connection_side_effect(*args, **kwargs):
    try:
        if args:
            value = args[0]
        else:
            value = kwargs.get('command')

        fixture_path = path.abspath(
            b'%s/%s' % (b_FIXTURE_DIR, b'_'.join(value.split(b' ')))
        )
        with open(fixture_path, 'rb') as file_desc:
            return file_desc.read()
    def _connect(self):
        if not HAS_NCCLIENT:
            raise AnsibleError("%s: %s" % (missing_required_lib("ncclient"), to_native(NCCLIENT_IMP_ERR)))

        self.queue_message('log', 'ssh connection done, starting ncclient')

        allow_agent = True
        if self._play_context.password is not None:
            allow_agent = False
        setattr(self._play_context, 'allow_agent', allow_agent)

        self.key_filename = self._play_context.private_key_file or self.get_option('private_key_file')
        if self.key_filename:
            self.key_filename = str(os.path.expanduser(self.key_filename))

        self._ssh_config = self.get_option('netconf_ssh_config')
        if self._ssh_config in BOOLEANS_TRUE:
            self._ssh_config = True
        elif self._ssh_config in BOOLEANS_FALSE:
            self._ssh_config = None

        # Try to guess the network_os if the network_os is set to auto
        if self._network_os == 'auto':
            for cls in netconf_loader.all(class_only=True):
                network_os = cls.guess_network_os(self)
                if network_os:
                    self.queue_message('vvv', 'discovered network_os %s' % network_os)
                    self._network_os = network_os

        # If we have tried to detect the network_os but were unable to i.e. network_os is still 'auto'
        # then use default as the network_os

        if self._network_os == 'auto':
            # Network os not discovered. Set it to default
            self.queue_message('vvv', 'Unable to discover network_os. Falling back to default.')
            self._network_os = 'default'
        try:
            ncclient_device_handler = self.netconf.get_option('ncclient_device_handler')
        except KeyError:
            ncclient_device_handler = 'default'
        self.queue_message('vvv', 'identified ncclient device handler: %s.' % ncclient_device_handler)
        device_params = {'name': ncclient_device_handler}

        try:
            port = self._play_context.port or 830
            self.queue_message('vvv', "ESTABLISH NETCONF SSH CONNECTION FOR USER: %s on PORT %s TO %s WITH SSH_CONFIG = %s" %
                               (self._play_context.remote_user, port, self._play_context.remote_addr, self._ssh_config))
            self._manager = manager.connect(
                host=self._play_context.remote_addr,
                port=port,
                username=self._play_context.remote_user,
                password=self._play_context.password,
                key_filename=self.key_filename,
                hostkey_verify=self.get_option('host_key_checking'),
                look_for_keys=self.get_option('look_for_keys'),
                device_params=device_params,
                allow_agent=self._play_context.allow_agent,
                timeout=self.get_option('persistent_connect_timeout'),
                ssh_config=self._ssh_config
            )

            self._manager._timeout = self.get_option('persistent_command_timeout')
        except SSHUnknownHostError as exc:
            raise AnsibleConnectionFailure(to_native(exc))
        except ImportError:
            raise AnsibleError("connection=netconf is not supported on {0}".format(self._network_os))

        if not self._manager.connected:
            return 1, b'', b'not connected'

        self.queue_message('log', 'ncclient manager object created successfully')

        self._connected = True

        super(Connection, self)._connect()

        return 0, to_bytes(self._manager.session_id, errors='surrogate_or_strict'), b''
Exemple #25
0
    def fetch_file(self, in_path, out_path):
        super(Connection, self).fetch_file(in_path, out_path)
        display.vvv("FETCH %s TO %s" % (in_path, out_path),
                    host=self._psrp_host)

        in_path = self._shell._unquote(in_path)
        out_path = out_path.replace('\\', '/')

        # because we are dealing with base64 data we need to get the max size
        # of the bytes that the base64 size would equal
        max_b64_size = int(self.runspace.connection.max_payload_size -
                           (self.runspace.connection.max_payload_size / 4 * 3))
        buffer_size = max_b64_size - (max_b64_size % 1024)

        # setup the file stream with read only mode
        setup_script = '''$ErrorActionPreference = "Stop"
$path = '%s'

if (Test-Path -Path $path -PathType Leaf) {
    $fs = New-Object -TypeName System.IO.FileStream -ArgumentList @(
        $path,
        [System.IO.FileMode]::Open,
        [System.IO.FileAccess]::Read,
        [System.IO.FileShare]::Read
    )
    $buffer_size = %d
} elseif (Test-Path -Path $path -PathType Container) {
    Write-Output -InputObject "[DIR]"
} else {
    Write-Error -Message "$path does not exist"
    $host.SetShouldExit(1)
}''' % (self._shell._escape(in_path), buffer_size)

        # read the file stream at the offset and return the b64 string
        read_script = '''$ErrorActionPreference = "Stop"
$fs.Seek(%d, [System.IO.SeekOrigin]::Begin) > $null
$buffer = New-Object -TypeName byte[] -ArgumentList $buffer_size
$bytes_read = $fs.Read($buffer, 0, $buffer_size)

if ($bytes_read -gt 0) {
    $bytes = $buffer[0..($bytes_read - 1)]
    Write-Output -InputObject ([System.Convert]::ToBase64String($bytes))
}'''

        # need to run the setup script outside of the local scope so the
        # file stream stays active between fetch operations
        rc, stdout, stderr = self._exec_psrp_script(setup_script,
                                                    use_local_scope=False,
                                                    force_stop=True)
        if rc != 0:
            raise AnsibleError("failed to setup file stream for fetch '%s': %s"
                               % (out_path, to_native(stderr)))
        elif stdout.strip() == '[DIR]':
            # to be consistent with other connection plugins, we assume the caller has created the target dir
            return

        b_out_path = to_bytes(out_path, errors='surrogate_or_strict')
        # to be consistent with other connection plugins, we assume the caller has created the target dir
        offset = 0
        with open(b_out_path, 'wb') as out_file:
            while True:
                display.vvvvv("PSRP FETCH %s to %s (offset=%d" %
                              (in_path, out_path, offset), host=self._psrp_host)
                rc, stdout, stderr = self._exec_psrp_script(read_script % offset, force_stop=True)
                if rc != 0:
                    raise AnsibleError("failed to transfer file to '%s': %s"
                                       % (out_path, to_native(stderr)))

                data = base64.b64decode(stdout.strip())
                out_file.write(data)
                if len(data) < buffer_size:
                    break
                offset += len(data)

            rc, stdout, stderr = self._exec_psrp_script("$fs.Close()", force_stop=True)
            if rc != 0:
                display.warning("failed to close remote file stream of file "
                                "'%s': %s" % (in_path, to_native(stderr)))
def copy_left_only(src, dest, module):
    changed = False
    owner = module.params['owner']
    group = module.params['group']
    local_follow = module.params['local_follow']
    left_only = filecmp.dircmp(src, dest).left_only
    if len(left_only):
        changed = True
    if not module.check_mode:
        for item in left_only:
            src_item_path = os.path.join(src, item)
            dest_item_path = os.path.join(dest, item)
            b_src_item_path = to_bytes(src_item_path,
                                       errors='surrogate_or_strict')
            b_dest_item_path = to_bytes(dest_item_path,
                                        errors='surrogate_or_strict')

            if os.path.islink(b_src_item_path) and os.path.isdir(
                    b_src_item_path) and local_follow is True:
                shutil.copytree(b_src_item_path,
                                b_dest_item_path,
                                symlinks=not (local_follow))
                chown_recursive(b_dest_item_path, module)

            if os.path.islink(b_src_item_path) and os.path.isdir(
                    b_src_item_path) and local_follow is False:
                linkto = os.readlink(b_src_item_path)
                os.symlink(linkto, b_dest_item_path)

            if os.path.islink(b_src_item_path) and os.path.isfile(
                    b_src_item_path) and local_follow is True:
                shutil.copyfile(b_src_item_path, b_dest_item_path)
                if owner is not None:
                    module.set_owner_if_different(b_dest_item_path, owner,
                                                  False)
                if group is not None:
                    module.set_group_if_different(b_dest_item_path, group,
                                                  False)

            if os.path.islink(b_src_item_path) and os.path.isfile(
                    b_src_item_path) and local_follow is False:
                linkto = os.readlink(b_src_item_path)
                os.symlink(linkto, b_dest_item_path)

            if not os.path.islink(b_src_item_path) and os.path.isfile(
                    b_src_item_path):
                shutil.copyfile(b_src_item_path, b_dest_item_path)
                if owner is not None:
                    module.set_owner_if_different(b_dest_item_path, owner,
                                                  False)
                if group is not None:
                    module.set_group_if_different(b_dest_item_path, group,
                                                  False)

            if not os.path.islink(b_src_item_path) and os.path.isdir(
                    b_src_item_path):
                shutil.copytree(b_src_item_path,
                                b_dest_item_path,
                                symlinks=not (local_follow))
                chown_recursive(b_dest_item_path, module)

            changed = True
    return changed
def daemonize(module, cmd):
    '''
    Execute a command while detaching as a daemon, returns rc, stdout, and stderr.

    :arg module: is an  AnsibleModule object, used for it's utility methods
    :arg cmd: is a list or string representing the command and options to run

    This is complex because daemonization is hard for people.
    What we do is daemonize a part of this module, the daemon runs the command,
    picks up the return code and output, and returns it to the main process.
    '''

    # init some vars
    chunk = 4096  # FIXME: pass in as arg?
    errors = 'surrogate_or_strict'

    # start it!
    try:
        pipe = os.pipe()
        pid = fork_process()
    except OSError:
        module.fail_json(msg="Error while attempting to fork: %s",
                         exception=traceback.format_exc())
    except Exception as exc:
        module.fail_json(msg=to_text(exc), exception=traceback.format_exc())

    # we don't do any locking as this should be a unique module/process
    if pid == 0:
        os.close(pipe[0])

        # if command is string deal with  py2 vs py3 conversions for shlex
        if not isinstance(cmd, list):
            if PY2:
                cmd = shlex.split(to_bytes(cmd, errors=errors))
            else:
                cmd = shlex.split(to_text(cmd, errors=errors))

        # make sure we always use byte strings
        run_cmd = []
        for c in cmd:
            run_cmd.append(to_bytes(c, errors=errors))

        # execute the command in forked process
        p = subprocess.Popen(run_cmd,
                             shell=False,
                             stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE,
                             preexec_fn=lambda: os.close(pipe[1]))
        fds = [p.stdout, p.stderr]

        # loop reading output till its done
        output = {p.stdout: b(""), p.stderr: b("")}
        while fds:
            rfd, wfd, efd = select.select(fds, [], fds, 1)
            if (rfd + wfd + efd) or p.poll():
                for out in fds:
                    if out in rfd:
                        data = os.read(out.fileno(), chunk)
                        if not data:
                            fds.remove(out)
                    output[out] += b(data)

        # even after fds close, we might want to wait for pid to die
        p.wait()

        # Return a pickled data of parent
        return_data = pickle.dumps([
            p.returncode,
            to_text(output[p.stdout]),
            to_text(output[p.stderr])
        ],
                                   protocol=pickle.HIGHEST_PROTOCOL)
        os.write(pipe[1], to_bytes(return_data, errors=errors))

        # clean up
        os.close(pipe[1])
        os._exit(0)

    elif pid == -1:
        module.fail_json(
            msg=
            "Unable to fork, no exception thrown, probably due to lack of resources, check logs."
        )

    else:
        # in parent
        os.close(pipe[1])
        os.waitpid(pid, 0)

        # Grab response data after child finishes
        return_data = b("")
        while True:
            rfd, wfd, efd = select.select([pipe[0]], [], [pipe[0]])
            if pipe[0] in rfd:
                data = os.read(pipe[0], chunk)
                if not data:
                    break
                return_data += b(data)

        # Note: no need to specify encoding on py3 as this module sends the
        # pickle to itself (thus same python interpreter so we aren't mixing
        # py2 and py3)
        return pickle.loads(to_bytes(return_data, errors=errors))
def main():

    global module

    module = AnsibleModule(
        # not checking because of daisy chain to file module
        argument_spec=dict(
            src=dict(type='path'),
            _original_basename=dict(
                type='str'
            ),  # used to handle 'dest is a directory' via template, a slight hack
            content=dict(type='str', no_log=True),
            dest=dict(type='path', required=True),
            backup=dict(type='bool', default=False),
            force=dict(type='bool', default=True, aliases=['thirsty']),
            validate=dict(type='str'),
            directory_mode=dict(type='raw'),
            remote_src=dict(type='bool'),
            local_follow=dict(type='bool'),
            checksum=dict(type='str'),
        ),
        add_file_common_args=True,
        supports_check_mode=True,
    )

    if module.params.get('thirsty'):
        module.deprecate(
            'The alias "thirsty" has been deprecated and will be removed, use "force" instead',
            version='2.13')

    src = module.params['src']
    b_src = to_bytes(src, errors='surrogate_or_strict')
    dest = module.params['dest']
    # Make sure we always have a directory component for later processing
    if os.path.sep not in dest:
        dest = '.{0}{1}'.format(os.path.sep, dest)
    b_dest = to_bytes(dest, errors='surrogate_or_strict')
    backup = module.params['backup']
    force = module.params['force']
    _original_basename = module.params.get('_original_basename', None)
    validate = module.params.get('validate', None)
    follow = module.params['follow']
    local_follow = module.params['local_follow']
    mode = module.params['mode']
    owner = module.params['owner']
    group = module.params['group']
    remote_src = module.params['remote_src']
    checksum = module.params['checksum']

    if not os.path.exists(b_src):
        module.fail_json(msg="Source %s not found" % (src))
    if not os.access(b_src, os.R_OK):
        module.fail_json(msg="Source %s not readable" % (src))

    # Preserve is usually handled in the action plugin but mode + remote_src has to be done on the
    # remote host
    if module.params['mode'] == 'preserve':
        module.params['mode'] = '0%03o' % stat.S_IMODE(os.stat(b_src).st_mode)
    mode = module.params['mode']

    checksum_dest = None

    if os.path.isfile(src):
        checksum_src = module.sha1(src)
    else:
        checksum_src = None

    # Backwards compat only.  This will be None in FIPS mode
    try:
        if os.path.isfile(src):
            md5sum_src = module.md5(src)
        else:
            md5sum_src = None
    except ValueError:
        md5sum_src = None

    changed = False

    if checksum and checksum_src != checksum:
        module.fail_json(
            msg=
            'Copied file does not match the expected checksum. Transfer failed.',
            checksum=checksum_src,
            expected_checksum=checksum)

    # Special handling for recursive copy - create intermediate dirs
    if _original_basename and dest.endswith(os.sep):
        dest = os.path.join(dest, _original_basename)
        b_dest = to_bytes(dest, errors='surrogate_or_strict')
        dirname = os.path.dirname(dest)
        b_dirname = to_bytes(dirname, errors='surrogate_or_strict')
        if not os.path.exists(b_dirname):
            try:
                (pre_existing_dir,
                 new_directory_list) = split_pre_existing_dir(dirname)
            except AnsibleModuleError as e:
                e.result['msg'] += ' Could not copy to {0}'.format(dest)
                module.fail_json(**e.results)

            os.makedirs(b_dirname)
            directory_args = module.load_file_common_arguments(module.params)
            directory_mode = module.params["directory_mode"]
            if directory_mode is not None:
                directory_args['mode'] = directory_mode
            else:
                directory_args['mode'] = None
            adjust_recursive_directory_permissions(pre_existing_dir,
                                                   new_directory_list, module,
                                                   directory_args, changed)

    if os.path.isdir(b_dest):
        basename = os.path.basename(src)
        if _original_basename:
            basename = _original_basename
        dest = os.path.join(dest, basename)
        b_dest = to_bytes(dest, errors='surrogate_or_strict')

    if os.path.exists(b_dest):
        if os.path.islink(b_dest) and follow:
            b_dest = os.path.realpath(b_dest)
            dest = to_native(b_dest, errors='surrogate_or_strict')
        if not force:
            module.exit_json(msg="file already exists",
                             src=src,
                             dest=dest,
                             changed=False)
        if os.access(b_dest, os.R_OK) and os.path.isfile(b_dest):
            checksum_dest = module.sha1(dest)
    else:
        if not os.path.exists(os.path.dirname(b_dest)):
            try:
                # os.path.exists() can return false in some
                # circumstances where the directory does not have
                # the execute bit for the current user set, in
                # which case the stat() call will raise an OSError
                os.stat(os.path.dirname(b_dest))
            except OSError as e:
                if "permission denied" in to_native(e).lower():
                    module.fail_json(
                        msg="Destination directory %s is not accessible" %
                        (os.path.dirname(dest)))
            module.fail_json(msg="Destination directory %s does not exist" %
                             (os.path.dirname(dest)))

    if not os.access(os.path.dirname(b_dest),
                     os.W_OK) and not module.params['unsafe_writes']:
        module.fail_json(msg="Destination %s not writable" %
                         (os.path.dirname(dest)))

    backup_file = None
    if checksum_src != checksum_dest or os.path.islink(b_dest):
        if not module.check_mode:
            try:
                if backup:
                    if os.path.exists(b_dest):
                        backup_file = module.backup_local(dest)
                # allow for conversion from symlink.
                if os.path.islink(b_dest):
                    os.unlink(b_dest)
                    open(b_dest, 'w').close()
                if validate:
                    # if we have a mode, make sure we set it on the temporary
                    # file source as some validations may require it
                    if mode is not None:
                        module.set_mode_if_different(src, mode, False)
                    if owner is not None:
                        module.set_owner_if_different(src, owner, False)
                    if group is not None:
                        module.set_group_if_different(src, group, False)
                    if "%s" not in validate:
                        module.fail_json(msg="validate must contain %%s: %s" %
                                         (validate))
                    (rc, out, err) = module.run_command(validate % src)
                    if rc != 0:
                        module.fail_json(msg="failed to validate",
                                         exit_status=rc,
                                         stdout=out,
                                         stderr=err)
                b_mysrc = b_src
                if remote_src and os.path.isfile(b_src):
                    _, b_mysrc = tempfile.mkstemp(dir=os.path.dirname(b_dest))

                    shutil.copyfile(b_src, b_mysrc)
                    try:
                        shutil.copystat(b_src, b_mysrc)
                    except OSError as err:
                        if err.errno == errno.ENOSYS and mode == "preserve":
                            module.warn("Unable to copy stats {0}".format(
                                to_native(b_src)))
                        else:
                            raise

                # might be needed below
                if PY3 and hasattr(os, 'listxattr'):
                    try:
                        src_has_acls = 'system.posix_acl_access' in os.listxattr(
                            src)
                    except Exception as e:
                        # assume unwanted ACLs by default
                        src_has_acls = True

                module.atomic_move(
                    b_mysrc,
                    dest,
                    unsafe_writes=module.params['unsafe_writes'])

                if PY3 and hasattr(os, 'listxattr') and platform.system(
                ) == 'Linux' and not remote_src:
                    # atomic_move used above to copy src into dest might, in some cases,
                    # use shutil.copy2 which in turn uses shutil.copystat.
                    # Since Python 3.3, shutil.copystat copies file extended attributes:
                    # https://docs.python.org/3/library/shutil.html#shutil.copystat
                    # os.listxattr (along with others) was added to handle the operation.

                    # This means that on Python 3 we are copying the extended attributes which includes
                    # the ACLs on some systems - further limited to Linux as the documentation above claims
                    # that the extended attributes are copied only on Linux. Also, os.listxattr is only
                    # available on Linux.

                    # If not remote_src, then the file was copied from the controller. In that
                    # case, any filesystem ACLs are artifacts of the copy rather than preservation
                    # of existing attributes. Get rid of them:

                    if src_has_acls:
                        # FIXME If dest has any default ACLs, there are not applied to src now because
                        # they were overridden by copystat. Should/can we do anything about this?
                        # 'system.posix_acl_default' in os.listxattr(os.path.dirname(b_dest))

                        try:
                            clear_facls(dest)
                        except ValueError as e:
                            if 'setfacl' in to_native(e):
                                # No setfacl so we're okay.  The controller couldn't have set a facl
                                # without the setfacl command
                                pass
                            else:
                                raise
                        except RuntimeError as e:
                            # setfacl failed.
                            if 'Operation not supported' in to_native(e):
                                # The file system does not support ACLs.
                                pass
                            else:
                                raise

            except (IOError, OSError):
                module.fail_json(msg="failed to copy: %s to %s" % (src, dest),
                                 traceback=traceback.format_exc())
        changed = True
    else:
        changed = False

    if checksum_src is None and checksum_dest is None:
        if remote_src and os.path.isdir(module.params['src']):
            b_src = to_bytes(module.params['src'],
                             errors='surrogate_or_strict')
            b_dest = to_bytes(module.params['dest'],
                              errors='surrogate_or_strict')

            if src.endswith(os.path.sep) and os.path.isdir(
                    module.params['dest']):
                diff_files_changed = copy_diff_files(b_src, b_dest, module)
                left_only_changed = copy_left_only(b_src, b_dest, module)
                common_dirs_changed = copy_common_dirs(b_src, b_dest, module)
                owner_group_changed = chown_recursive(b_dest, module)
                if diff_files_changed or left_only_changed or common_dirs_changed or owner_group_changed:
                    changed = True

            if src.endswith(
                    os.path.sep) and not os.path.exists(module.params['dest']):
                b_basename = to_bytes(os.path.basename(src),
                                      errors='surrogate_or_strict')
                b_dest = to_bytes(os.path.join(b_dest, b_basename),
                                  errors='surrogate_or_strict')
                b_src = to_bytes(os.path.join(module.params['src'], ""),
                                 errors='surrogate_or_strict')
                if not module.check_mode:
                    shutil.copytree(b_src, b_dest, symlinks=not (local_follow))
                chown_recursive(dest, module)
                changed = True

            if not src.endswith(os.path.sep) and os.path.isdir(
                    module.params['dest']):
                b_basename = to_bytes(os.path.basename(src),
                                      errors='surrogate_or_strict')
                b_dest = to_bytes(os.path.join(b_dest, b_basename),
                                  errors='surrogate_or_strict')
                b_src = to_bytes(os.path.join(module.params['src'], ""),
                                 errors='surrogate_or_strict')
                if not module.check_mode and not os.path.exists(b_dest):
                    shutil.copytree(b_src, b_dest, symlinks=not (local_follow))
                    changed = True
                    chown_recursive(dest, module)
                if module.check_mode and not os.path.exists(b_dest):
                    changed = True
                if os.path.exists(b_dest):
                    diff_files_changed = copy_diff_files(b_src, b_dest, module)
                    left_only_changed = copy_left_only(b_src, b_dest, module)
                    common_dirs_changed = copy_common_dirs(
                        b_src, b_dest, module)
                    owner_group_changed = chown_recursive(b_dest, module)
                    if diff_files_changed or left_only_changed or common_dirs_changed or owner_group_changed:
                        changed = True

            if not src.endswith(os.path.sep) and not os.path.exists(
                    module.params['dest']):
                b_basename = to_bytes(os.path.basename(module.params['src']),
                                      errors='surrogate_or_strict')
                b_dest = to_bytes(os.path.join(b_dest, b_basename),
                                  errors='surrogate_or_strict')
                if not module.check_mode and not os.path.exists(b_dest):
                    os.makedirs(b_dest)
                    b_src = to_bytes(os.path.join(module.params['src'], ""),
                                     errors='surrogate_or_strict')
                    diff_files_changed = copy_diff_files(b_src, b_dest, module)
                    left_only_changed = copy_left_only(b_src, b_dest, module)
                    common_dirs_changed = copy_common_dirs(
                        b_src, b_dest, module)
                    owner_group_changed = chown_recursive(b_dest, module)
                    if diff_files_changed or left_only_changed or common_dirs_changed or owner_group_changed:
                        changed = True
                if module.check_mode and not os.path.exists(b_dest):
                    changed = True

    res_args = dict(dest=dest,
                    src=src,
                    md5sum=md5sum_src,
                    checksum=checksum_src,
                    changed=changed)
    if backup_file:
        res_args['backup_file'] = backup_file

    module.params['dest'] = dest
    if not module.check_mode:
        file_args = module.load_file_common_arguments(module.params)
        res_args['changed'] = module.set_fs_attributes_if_different(
            file_args, res_args['changed'])

    module.exit_json(**res_args)
def send_msg(msg,
             server='localhost',
             port='6667',
             channel=None,
             nick_to=None,
             key=None,
             topic=None,
             nick="ansible",
             color='none',
             passwd=False,
             timeout=30,
             use_ssl=False,
             part=True,
             style=None):
    '''send message to IRC'''
    nick_to = [] if nick_to is None else nick_to

    colornumbers = {
        'white': "00",
        'black': "01",
        'blue': "02",
        'green': "03",
        'red': "04",
        'brown': "05",
        'purple': "06",
        'orange': "07",
        'yellow': "08",
        'light_green': "09",
        'teal': "10",
        'light_cyan': "11",
        'light_blue': "12",
        'pink': "13",
        'gray': "14",
        'light_gray': "15",
    }

    stylechoices = {
        'bold': "\x02",
        'underline': "\x1F",
        'reverse': "\x16",
        'italic': "\x1D",
    }

    try:
        styletext = stylechoices[style]
    except Exception:
        styletext = ""

    try:
        colornumber = colornumbers[color]
        colortext = "\x03" + colornumber
    except Exception:
        colortext = ""

    message = styletext + colortext + msg

    irc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    if use_ssl:
        irc = ssl.wrap_socket(irc)
    irc.connect((server, int(port)))

    if passwd:
        irc.send(to_bytes('PASS %s\r\n' % passwd))
    irc.send(to_bytes('NICK %s\r\n' % nick))
    irc.send(to_bytes('USER %s %s %s :ansible IRC\r\n' % (nick, nick, nick)))
    motd = ''
    start = time.time()
    while 1:
        motd += to_native(irc.recv(1024))
        # The server might send back a shorter nick than we specified (due to NICKLEN),
        #  so grab that and use it from now on (assuming we find the 00[1-4] response).
        match = re.search(r'^:\S+ 00[1-4] (?P<nick>\S+) :', motd, flags=re.M)
        if match:
            nick = match.group('nick')
            break
        elif time.time() - start > timeout:
            raise Exception('Timeout waiting for IRC server welcome response')
        time.sleep(0.5)

    if key:
        irc.send(to_bytes('JOIN %s %s\r\n' % (channel, key)))
    else:
        irc.send(to_bytes('JOIN %s\r\n' % channel))

    join = ''
    start = time.time()
    while 1:
        join += to_native(irc.recv(1024))
        if re.search(r'^:\S+ 366 %s %s :' % (nick, channel),
                     join,
                     flags=re.M | re.I):
            break
        elif time.time() - start > timeout:
            raise Exception('Timeout waiting for IRC JOIN response')
        time.sleep(0.5)

    if topic is not None:
        irc.send(to_bytes('TOPIC %s :%s\r\n' % (channel, topic)))
        time.sleep(1)

    if nick_to:
        for nick in nick_to:
            irc.send(to_bytes('PRIVMSG %s :%s\r\n' % (nick, message)))
    if channel:
        irc.send(to_bytes('PRIVMSG %s :%s\r\n' % (channel, message)))
    time.sleep(1)
    if part:
        irc.send(to_bytes('PART %s\r\n' % channel))
        irc.send(to_bytes('QUIT\r\n'))
        time.sleep(1)
    irc.close()
def main():

    global module

    module = AnsibleModule(
        argument_spec=dict(
            state=dict(type='str',
                       choices=[
                           'absent', 'directory', 'file', 'hard', 'link',
                           'touch'
                       ]),
            path=dict(type='path', required=True, aliases=['dest', 'name']),
            _original_basename=dict(
                type='str'),  # Internal use only, for recursive ops
            recurse=dict(type='bool', default=False),
            force=dict(type='bool', default=False
                       ),  # Note: Should not be in file_common_args in future
            follow=dict(
                type='bool',
                default=True),  # Note: Different default than file_common_args
            _diff_peek=dict(
                type='bool'
            ),  # Internal use only, for internal checks in the action plugins
            src=dict(type='path'
                     ),  # Note: Should not be in file_common_args in future
            modification_time=dict(type='str'),
            modification_time_format=dict(type='str', default='%Y%m%d%H%M.%S'),
            access_time=dict(type='str'),
            access_time_format=dict(type='str', default='%Y%m%d%H%M.%S'),
        ),
        add_file_common_args=True,
        supports_check_mode=True,
    )

    # When we rewrite basic.py, we will do something similar to this on instantiating an AnsibleModule
    sys.excepthook = _ansible_excepthook
    additional_parameter_handling(module.params)
    params = module.params

    state = params['state']
    recurse = params['recurse']
    force = params['force']
    follow = params['follow']
    path = params['path']
    src = params['src']

    timestamps = {}
    timestamps[
        'modification_time'] = keep_backward_compatibility_on_timestamps(
            params['modification_time'], state)
    timestamps['modification_time_format'] = params['modification_time_format']
    timestamps['access_time'] = keep_backward_compatibility_on_timestamps(
        params['access_time'], state)
    timestamps['access_time_format'] = params['access_time_format']

    # short-circuit for diff_peek
    if params['_diff_peek'] is not None:
        appears_binary = execute_diff_peek(
            to_bytes(path, errors='surrogate_or_strict'))
        module.exit_json(path=path,
                         changed=False,
                         appears_binary=appears_binary)

    if state == 'file':
        result = ensure_file_attributes(path, follow, timestamps)
    elif state == 'directory':
        result = ensure_directory(path, follow, recurse, timestamps)
    elif state == 'link':
        result = ensure_symlink(path, src, follow, force, timestamps)
    elif state == 'hard':
        result = ensure_hardlink(path, src, follow, force, timestamps)
    elif state == 'touch':
        result = execute_touch(path, follow, timestamps)
    elif state == 'absent':
        result = ensure_absent(path)

    module.exit_json(**result)