summaryrefslogtreecommitdiff
path: root/lib/ansible/modules/files
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ansible/modules/files')
-rw-r--r--lib/ansible/modules/files/acl.py385
-rw-r--r--lib/ansible/modules/files/synchronize.py618
2 files changed, 0 insertions, 1003 deletions
diff --git a/lib/ansible/modules/files/acl.py b/lib/ansible/modules/files/acl.py
deleted file mode 100644
index 8c65273603..0000000000
--- a/lib/ansible/modules/files/acl.py
+++ /dev/null
@@ -1,385 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# Copyright: (c) 2017, Ansible Project
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['stableinterface'],
- 'supported_by': 'core'}
-
-DOCUMENTATION = r'''
----
-module: acl
-version_added: '1.4'
-short_description: Set and retrieve file ACL information.
-description:
-- Set and retrieve file ACL information.
-options:
- path:
- description:
- - The full path of the file or object.
- type: path
- required: yes
- aliases: [ name ]
- state:
- description:
- - Define whether the ACL should be present or not.
- - The C(query) state gets the current ACL without changing it, for use in C(register) operations.
- choices: [ absent, present, query ]
- default: query
- follow:
- description:
- - Whether to follow symlinks on the path if a symlink is encountered.
- type: bool
- default: yes
- default:
- description:
- - If the target is a directory, setting this to C(yes) will make it the default ACL for entities created inside the directory.
- - Setting C(default) to C(yes) causes an error if the path is a file.
- type: bool
- default: no
- version_added: '1.5'
- entity:
- description:
- - The actual user or group that the ACL applies to when matching entity types user or group are selected.
- version_added: '1.5'
- etype:
- description:
- - The entity type of the ACL to apply, see C(setfacl) documentation for more info.
- choices: [ group, mask, other, user ]
- version_added: '1.5'
- permissions:
- description:
- - The permissions to apply/remove can be any combination of C(r), C(w) and C(x) (read, write and execute respectively)
- version_added: '1.5'
- entry:
- description:
- - DEPRECATED.
- - The ACL to set or remove.
- - This must always be quoted in the form of C(<etype>:<qualifier>:<perms>).
- - The qualifier may be empty for some types, but the type and perms are always required.
- - C(-) can be used as placeholder when you do not care about permissions.
- - This is now superseded by entity, type and permissions fields.
- recursive:
- description:
- - Recursively sets the specified ACL.
- - Incompatible with C(state=query).
- type: bool
- default: no
- version_added: '2.0'
- use_nfsv4_acls:
- description:
- - Use NFSv4 ACLs instead of POSIX ACLs.
- type: bool
- default: no
- version_added: '2.2'
- recalculate_mask:
- description:
- - Select if and when to recalculate the effective right masks of the files.
- - See C(setfacl) documentation for more info.
- - Incompatible with C(state=query).
- choices: [ default, mask, no_mask ]
- default: default
- version_added: '2.7'
-author:
-- Brian Coca (@bcoca)
-- Jérémie Astori (@astorije)
-notes:
-- The C(acl) module requires that ACLs are enabled on the target filesystem and that the C(setfacl) and C(getfacl) binaries are installed.
-- As of Ansible 2.0, this module only supports Linux distributions.
-- As of Ansible 2.3, the I(name) option has been changed to I(path) as default, but I(name) still works as well.
-'''
-
-EXAMPLES = r'''
-- name: Grant user Joe read access to a file
- acl:
- path: /etc/foo.conf
- entity: joe
- etype: user
- permissions: r
- state: present
-
-- name: Removes the ACL for Joe on a specific file
- acl:
- path: /etc/foo.conf
- entity: joe
- etype: user
- state: absent
-
-- name: Sets default ACL for joe on /etc/foo.d/
- acl:
- path: /etc/foo.d/
- entity: joe
- etype: user
- permissions: rw
- default: yes
- state: present
-
-- name: Same as previous but using entry shorthand
- acl:
- path: /etc/foo.d/
- entry: default:user:joe:rw-
- state: present
-
-- name: Obtain the ACL for a specific file
- acl:
- path: /etc/foo.conf
- register: acl_info
-'''
-
-RETURN = r'''
-acl:
- description: Current ACL on provided path (after changes, if any)
- returned: success
- type: list
- sample: [ "user::rwx", "group::rwx", "other::rwx" ]
-'''
-
-import os
-import platform
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-
-
-def split_entry(entry):
- ''' splits entry and ensures normalized return'''
-
- a = entry.split(':')
-
- d = None
- if entry.lower().startswith("d"):
- d = True
- a.pop(0)
-
- if len(a) == 2:
- a.append(None)
-
- t, e, p = a
- t = t.lower()
-
- if t.startswith("u"):
- t = "user"
- elif t.startswith("g"):
- t = "group"
- elif t.startswith("m"):
- t = "mask"
- elif t.startswith("o"):
- t = "other"
- else:
- t = None
-
- return [d, t, e, p]
-
-
-def build_entry(etype, entity, permissions=None, use_nfsv4_acls=False):
- '''Builds and returns an entry string. Does not include the permissions bit if they are not provided.'''
- if use_nfsv4_acls:
- return ':'.join([etype, entity, permissions, 'allow'])
-
- if permissions:
- return etype + ':' + entity + ':' + permissions
-
- return etype + ':' + entity
-
-
-def build_command(module, mode, path, follow, default, recursive, recalculate_mask, entry=''):
- '''Builds and returns a getfacl/setfacl command.'''
- if mode == 'set':
- cmd = [module.get_bin_path('setfacl', True)]
- cmd.extend(['-m', entry])
- elif mode == 'rm':
- cmd = [module.get_bin_path('setfacl', True)]
- cmd.extend(['-x', entry])
- else: # mode == 'get'
- cmd = [module.get_bin_path('getfacl', True)]
- # prevents absolute path warnings and removes headers
- if platform.system().lower() == 'linux':
- cmd.append('--omit-header')
- cmd.append('--absolute-names')
-
- if recursive:
- cmd.append('--recursive')
-
- if recalculate_mask == 'mask' and mode in ['set', 'rm']:
- cmd.append('--mask')
- elif recalculate_mask == 'no_mask' and mode in ['set', 'rm']:
- cmd.append('--no-mask')
-
- if not follow:
- if platform.system().lower() == 'linux':
- cmd.append('--physical')
- elif platform.system().lower() == 'freebsd':
- cmd.append('-h')
-
- if default:
- cmd.insert(1, '-d')
-
- cmd.append(path)
- return cmd
-
-
-def acl_changed(module, cmd):
- '''Returns true if the provided command affects the existing ACLs, false otherwise.'''
- # FreeBSD do not have a --test flag, so by default, it is safer to always say "true"
- if platform.system().lower() == 'freebsd':
- return True
-
- cmd = cmd[:] # lists are mutables so cmd would be overwritten without this
- cmd.insert(1, '--test')
- lines = run_acl(module, cmd)
-
- for line in lines:
- if not line.endswith('*,*'):
- return True
- return False
-
-
-def run_acl(module, cmd, check_rc=True):
-
- try:
- (rc, out, err) = module.run_command(cmd, check_rc=check_rc)
- except Exception as e:
- module.fail_json(msg=to_native(e))
-
- lines = []
- for l in out.splitlines():
- if not l.startswith('#'):
- lines.append(l.strip())
-
- if lines and not lines[-1].split():
- # trim last line only when it is empty
- return lines[:-1]
-
- return lines
-
-
-def main():
- module = AnsibleModule(
- argument_spec=dict(
- path=dict(type='path', required=True, aliases=['name']),
- entry=dict(type='str'),
- entity=dict(type='str', default=''),
- etype=dict(
- type='str',
- choices=['group', 'mask', 'other', 'user'],
- ),
- permissions=dict(type='str'),
- state=dict(
- type='str',
- default='query',
- choices=['absent', 'present', 'query'],
- ),
- follow=dict(type='bool', default=True),
- default=dict(type='bool', default=False),
- recursive=dict(type='bool', default=False),
- recalculate_mask=dict(
- type='str',
- default='default',
- choices=['default', 'mask', 'no_mask'],
- ),
- use_nfsv4_acls=dict(type='bool', default=False)
- ),
- supports_check_mode=True,
- )
-
- if platform.system().lower() not in ['linux', 'freebsd']:
- module.fail_json(msg="The acl module is not available on this system.")
-
- path = module.params.get('path')
- entry = module.params.get('entry')
- entity = module.params.get('entity')
- etype = module.params.get('etype')
- permissions = module.params.get('permissions')
- state = module.params.get('state')
- follow = module.params.get('follow')
- default = module.params.get('default')
- recursive = module.params.get('recursive')
- recalculate_mask = module.params.get('recalculate_mask')
- use_nfsv4_acls = module.params.get('use_nfsv4_acls')
-
- if not os.path.exists(path):
- module.fail_json(msg="Path not found or not accessible.")
-
- if state == 'query':
- if recursive:
- module.fail_json(msg="'recursive' MUST NOT be set when 'state=query'.")
-
- if recalculate_mask in ['mask', 'no_mask']:
- module.fail_json(msg="'recalculate_mask' MUST NOT be set to 'mask' or 'no_mask' when 'state=query'.")
-
- if not entry:
- if state == 'absent' and permissions:
- module.fail_json(msg="'permissions' MUST NOT be set when 'state=absent'.")
-
- if state == 'absent' and not entity:
- module.fail_json(msg="'entity' MUST be set when 'state=absent'.")
-
- if state in ['present', 'absent'] and not etype:
- module.fail_json(msg="'etype' MUST be set when 'state=%s'." % state)
-
- if entry:
- if etype or entity or permissions:
- module.fail_json(msg="'entry' MUST NOT be set when 'entity', 'etype' or 'permissions' are set.")
-
- if state == 'present' and not entry.count(":") in [2, 3]:
- module.fail_json(msg="'entry' MUST have 3 or 4 sections divided by ':' when 'state=present'.")
-
- if state == 'absent' and not entry.count(":") in [1, 2]:
- module.fail_json(msg="'entry' MUST have 2 or 3 sections divided by ':' when 'state=absent'.")
-
- if state == 'query':
- module.fail_json(msg="'entry' MUST NOT be set when 'state=query'.")
-
- default_flag, etype, entity, permissions = split_entry(entry)
- if default_flag is not None:
- default = default_flag
-
- if platform.system().lower() == 'freebsd':
- if recursive:
- module.fail_json(msg="recursive is not supported on that platform.")
-
- changed = False
- msg = ""
-
- if state == 'present':
- entry = build_entry(etype, entity, permissions, use_nfsv4_acls)
- command = build_command(
- module, 'set', path, follow,
- default, recursive, recalculate_mask, entry
- )
- changed = acl_changed(module, command)
-
- if changed and not module.check_mode:
- run_acl(module, command)
- msg = "%s is present" % entry
-
- elif state == 'absent':
- entry = build_entry(etype, entity, use_nfsv4_acls)
- command = build_command(
- module, 'rm', path, follow,
- default, recursive, recalculate_mask, entry
- )
- changed = acl_changed(module, command)
-
- if changed and not module.check_mode:
- run_acl(module, command, False)
- msg = "%s is absent" % entry
-
- elif state == 'query':
- msg = "current acl"
-
- acl = run_acl(
- module,
- build_command(module, 'get', path, follow, default, recursive, recalculate_mask)
- )
-
- module.exit_json(changed=changed, msg=msg, acl=acl)
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/files/synchronize.py b/lib/ansible/modules/files/synchronize.py
deleted file mode 100644
index e4c520b7cf..0000000000
--- a/lib/ansible/modules/files/synchronize.py
+++ /dev/null
@@ -1,618 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# Copyright: (c) 2012-2013, Timothy Appnel <tim@appnel.com>
-# Copyright: (c) 2017, Ansible Project
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'core'}
-
-DOCUMENTATION = r'''
----
-module: synchronize
-version_added: "1.4"
-short_description: A wrapper around rsync to make common tasks in your playbooks quick and easy
-description:
- - C(synchronize) is a wrapper around rsync to make common tasks in your playbooks quick and easy.
- - It is run and originates on the local host where Ansible is being run.
- - Of course, you could just use the C(command) action to call rsync yourself, but you also have to add a fair number of
- boilerplate options and host facts.
- - This module is not intended to provide access to the full power of rsync, but does make the most common
- invocations easier to implement. You `still` may need to call rsync directly via C(command) or C(shell) depending on your use case.
-options:
- src:
- description:
- - Path on the source host that will be synchronized to the destination.
- - The path can be absolute or relative.
- type: str
- required: true
- dest:
- description:
- - Path on the destination host that will be synchronized from the source.
- - The path can be absolute or relative.
- type: str
- required: true
- dest_port:
- description:
- - Port number for ssh on the destination host.
- - Prior to Ansible 2.0, the ansible_ssh_port inventory var took precedence over this value.
- - This parameter defaults to the value of C(ansible_ssh_port) or C(ansible_port),
- the C(remote_port) config setting or the value from ssh client configuration
- if none of the former have been set.
- type: int
- version_added: "1.5"
- mode:
- description:
- - Specify the direction of the synchronization.
- - In push mode the localhost or delegate is the source.
- - In pull mode the remote host in context is the source.
- type: str
- choices: [ pull, push ]
- default: push
- archive:
- description:
- - Mirrors the rsync archive flag, enables recursive, links, perms, times, owner, group flags and -D.
- type: bool
- default: yes
- checksum:
- description:
- - Skip based on checksum, rather than mod-time & size; Note that that "archive" option is still enabled by default - the "checksum" option will
- not disable it.
- type: bool
- default: no
- version_added: "1.6"
- compress:
- description:
- - Compress file data during the transfer.
- - In most cases, leave this enabled unless it causes problems.
- type: bool
- default: yes
- version_added: "1.7"
- existing_only:
- description:
- - Skip creating new files on receiver.
- type: bool
- default: no
- version_added: "1.5"
- delete:
- description:
- - Delete files in C(dest) that don't exist (after transfer, not before) in the C(src) path.
- - This option requires C(recursive=yes).
- - This option ignores excluded files and behaves like the rsync opt --delete-excluded.
- type: bool
- default: no
- dirs:
- description:
- - Transfer directories without recursing.
- type: bool
- default: no
- recursive:
- description:
- - Recurse into directories.
- - This parameter defaults to the value of the archive option.
- type: bool
- links:
- description:
- - Copy symlinks as symlinks.
- - This parameter defaults to the value of the archive option.
- type: bool
- copy_links:
- description:
- - Copy symlinks as the item that they point to (the referent) is copied, rather than the symlink.
- type: bool
- default: no
- perms:
- description:
- - Preserve permissions.
- - This parameter defaults to the value of the archive option.
- type: bool
- times:
- description:
- - Preserve modification times.
- - This parameter defaults to the value of the archive option.
- type: bool
- owner:
- description:
- - Preserve owner (super user only).
- - This parameter defaults to the value of the archive option.
- type: bool
- group:
- description:
- - Preserve group.
- - This parameter defaults to the value of the archive option.
- type: bool
- rsync_path:
- description:
- - Specify the rsync command to run on the remote host. See C(--rsync-path) on the rsync man page.
- - To specify the rsync command to run on the local host, you need to set this your task var C(ansible_rsync_path).
- type: str
- rsync_timeout:
- description:
- - Specify a C(--timeout) for the rsync command in seconds.
- type: int
- default: 0
- set_remote_user:
- description:
- - Put user@ for the remote paths.
- - If you have a custom ssh config to define the remote user for a host
- that does not match the inventory user, you should set this parameter to C(no).
- type: bool
- default: yes
- use_ssh_args:
- description:
- - Use the ssh_args specified in ansible.cfg.
- type: bool
- default: no
- version_added: "2.0"
- rsync_opts:
- description:
- - Specify additional rsync options by passing in an array.
- - Note that an empty string in C(rsync_opts) will end up transfer the current working directory.
- type: list
- default:
- version_added: "1.6"
- partial:
- description:
- - Tells rsync to keep the partial file which should make a subsequent transfer of the rest of the file much faster.
- type: bool
- default: no
- version_added: "2.0"
- verify_host:
- description:
- - Verify destination host key.
- type: bool
- default: no
- version_added: "2.0"
- private_key:
- description:
- - Specify the private key to use for SSH-based rsync connections (e.g. C(~/.ssh/id_rsa)).
- type: path
- version_added: "1.6"
- link_dest:
- description:
- - Add a destination to hard link against during the rsync.
- type: list
- default:
- version_added: "2.5"
-notes:
- - rsync must be installed on both the local and remote host.
- - For the C(synchronize) module, the "local host" is the host `the synchronize task originates on`, and the "destination host" is the host
- `synchronize is connecting to`.
- - The "local host" can be changed to a different host by using `delegate_to`. This enables copying between two remote hosts or entirely on one
- remote machine.
- - >
- The user and permissions for the synchronize `src` are those of the user running the Ansible task on the local host (or the remote_user for a
- delegate_to host when delegate_to is used).
- - The user and permissions for the synchronize `dest` are those of the `remote_user` on the destination host or the `become_user` if `become=yes` is active.
- - In Ansible 2.0 a bug in the synchronize module made become occur on the "local host". This was fixed in Ansible 2.0.1.
- - Currently, synchronize is limited to elevating permissions via passwordless sudo. This is because rsync itself is connecting to the remote machine
- and rsync doesn't give us a way to pass sudo credentials in.
- - Currently there are only a few connection types which support synchronize (ssh, paramiko, local, and docker) because a sync strategy has been
- determined for those connection types. Note that the connection for these must not need a password as rsync itself is making the connection and
- rsync does not provide us a way to pass a password to the connection.
- - Expect that dest=~/x will be ~<remote_user>/x even if using sudo.
- - Inspect the verbose output to validate the destination user/host/path are what was expected.
- - To exclude files and directories from being synchronized, you may add C(.rsync-filter) files to the source directory.
- - rsync daemon must be up and running with correct permission when using rsync protocol in source or destination path.
- - The C(synchronize) module forces `--delay-updates` to avoid leaving a destination in a broken in-between state if the underlying rsync process
- encounters an error. Those synchronizing large numbers of files that are willing to trade safety for performance should call rsync directly.
- - link_destination is subject to the same limitations as the underlying rsync daemon. Hard links are only preserved if the relative subtrees
- of the source and destination are the same. Attempts to hardlink into a directory that is a subdirectory of the source will be prevented.
-seealso:
-- module: copy
-- module: win_robocopy
-author:
-- Timothy Appnel (@tima)
-'''
-
-EXAMPLES = '''
-- name: Synchronization of src on the control machine to dest on the remote hosts
- synchronize:
- src: some/relative/path
- dest: /some/absolute/path
-
-- name: Synchronization using rsync protocol (push)
- synchronize:
- src: some/relative/path/
- dest: rsync://somehost.com/path/
-
-- name: Synchronization using rsync protocol (pull)
- synchronize:
- mode: pull
- src: rsync://somehost.com/path/
- dest: /some/absolute/path/
-
-- name: Synchronization using rsync protocol on delegate host (push)
- synchronize:
- src: /some/absolute/path/
- dest: rsync://somehost.com/path/
- delegate_to: delegate.host
-
-- name: Synchronization using rsync protocol on delegate host (pull)
- synchronize:
- mode: pull
- src: rsync://somehost.com/path/
- dest: /some/absolute/path/
- delegate_to: delegate.host
-
-- name: Synchronization without any --archive options enabled
- synchronize:
- src: some/relative/path
- dest: /some/absolute/path
- archive: no
-
-- name: Synchronization with --archive options enabled except for --recursive
- synchronize:
- src: some/relative/path
- dest: /some/absolute/path
- recursive: no
-
-- name: Synchronization with --archive options enabled except for --times, with --checksum option enabled
- synchronize:
- src: some/relative/path
- dest: /some/absolute/path
- checksum: yes
- times: no
-
-- name: Synchronization without --archive options enabled except use --links
- synchronize:
- src: some/relative/path
- dest: /some/absolute/path
- archive: no
- links: yes
-
-- name: Synchronization of two paths both on the control machine
- synchronize:
- src: some/relative/path
- dest: /some/absolute/path
- delegate_to: localhost
-
-- name: Synchronization of src on the inventory host to the dest on the localhost in pull mode
- synchronize:
- mode: pull
- src: some/relative/path
- dest: /some/absolute/path
-
-- name: Synchronization of src on delegate host to dest on the current inventory host.
- synchronize:
- src: /first/absolute/path
- dest: /second/absolute/path
- delegate_to: delegate.host
-
-- name: Synchronize two directories on one remote host.
- synchronize:
- src: /first/absolute/path
- dest: /second/absolute/path
- delegate_to: "{{ inventory_hostname }}"
-
-- name: Synchronize and delete files in dest on the remote host that are not found in src of localhost.
- synchronize:
- src: some/relative/path
- dest: /some/absolute/path
- delete: yes
- recursive: yes
-
-# This specific command is granted su privileges on the destination
-- name: Synchronize using an alternate rsync command
- synchronize:
- src: some/relative/path
- dest: /some/absolute/path
- rsync_path: su -c rsync
-
-# Example .rsync-filter file in the source directory
-# - var # exclude any path whose last part is 'var'
-# - /var # exclude any path starting with 'var' starting at the source directory
-# + /var/conf # include /var/conf even though it was previously excluded
-
-- name: Synchronize passing in extra rsync options
- synchronize:
- src: /tmp/helloworld
- dest: /var/www/helloworld
- rsync_opts:
- - "--no-motd"
- - "--exclude=.git"
-
-# Hardlink files if they didn't change
-- name: Use hardlinks when synchronizing filesystems
- synchronize:
- src: /tmp/path_a/foo.txt
- dest: /tmp/path_b/foo.txt
- link_dest: /tmp/path_a/
-
-# Specify the rsync binary to use on remote host and on local host
-- hosts: groupofhosts
- vars:
- ansible_rsync_path: /usr/gnu/bin/rsync
-
- tasks:
- - name: copy /tmp/localpath/ to remote location /tmp/remotepath
- synchronize:
- src: /tmp/localpath/
- dest: /tmp/remotepath
- rsync_path: /usr/gnu/bin/rsync
-'''
-
-
-import os
-import errno
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_bytes, to_native
-from ansible.module_utils.six.moves import shlex_quote
-
-
-client_addr = None
-
-
-def substitute_controller(path):
- global client_addr
- if not client_addr:
- ssh_env_string = os.environ.get('SSH_CLIENT', None)
- try:
- client_addr, _ = ssh_env_string.split(None, 1)
- except AttributeError:
- ssh_env_string = os.environ.get('SSH_CONNECTION', None)
- try:
- client_addr, _ = ssh_env_string.split(None, 1)
- except AttributeError:
- pass
- if not client_addr:
- raise ValueError
-
- if path.startswith('localhost:'):
- path = path.replace('localhost', client_addr, 1)
- return path
-
-
-def is_rsh_needed(source, dest):
- if source.startswith('rsync://') or dest.startswith('rsync://'):
- return False
- if ':' in source or ':' in dest:
- return True
- return False
-
-
-def main():
- module = AnsibleModule(
- argument_spec=dict(
- src=dict(type='str', required=True),
- dest=dict(type='str', required=True),
- dest_port=dict(type='int'),
- delete=dict(type='bool', default=False),
- private_key=dict(type='path'),
- rsync_path=dict(type='str'),
- _local_rsync_path=dict(type='path', default='rsync'),
- _local_rsync_password=dict(type='str', no_log=True),
- _substitute_controller=dict(type='bool', default=False),
- archive=dict(type='bool', default=True),
- checksum=dict(type='bool', default=False),
- compress=dict(type='bool', default=True),
- existing_only=dict(type='bool', default=False),
- dirs=dict(type='bool', default=False),
- recursive=dict(type='bool'),
- links=dict(type='bool'),
- copy_links=dict(type='bool', default=False),
- perms=dict(type='bool'),
- times=dict(type='bool'),
- owner=dict(type='bool'),
- group=dict(type='bool'),
- set_remote_user=dict(type='bool', default=True),
- rsync_timeout=dict(type='int', default=0),
- rsync_opts=dict(type='list', default=[]),
- ssh_args=dict(type='str'),
- partial=dict(type='bool', default=False),
- verify_host=dict(type='bool', default=False),
- mode=dict(type='str', default='push', choices=['pull', 'push']),
- link_dest=dict(type='list')
- ),
- supports_check_mode=True,
- )
-
- if module.params['_substitute_controller']:
- try:
- source = substitute_controller(module.params['src'])
- dest = substitute_controller(module.params['dest'])
- except ValueError:
- module.fail_json(msg='Could not determine controller hostname for rsync to send to')
- else:
- source = module.params['src']
- dest = module.params['dest']
- dest_port = module.params['dest_port']
- delete = module.params['delete']
- private_key = module.params['private_key']
- rsync_path = module.params['rsync_path']
- rsync = module.params.get('_local_rsync_path', 'rsync')
- rsync_password = module.params.get('_local_rsync_password')
- rsync_timeout = module.params.get('rsync_timeout', 'rsync_timeout')
- archive = module.params['archive']
- checksum = module.params['checksum']
- compress = module.params['compress']
- existing_only = module.params['existing_only']
- dirs = module.params['dirs']
- partial = module.params['partial']
- # the default of these params depends on the value of archive
- recursive = module.params['recursive']
- links = module.params['links']
- copy_links = module.params['copy_links']
- perms = module.params['perms']
- times = module.params['times']
- owner = module.params['owner']
- group = module.params['group']
- rsync_opts = module.params['rsync_opts']
- ssh_args = module.params['ssh_args']
- verify_host = module.params['verify_host']
- link_dest = module.params['link_dest']
-
- if '/' not in rsync:
- rsync = module.get_bin_path(rsync, required=True)
-
- cmd = [rsync, '--delay-updates', '-F']
- _sshpass_pipe = None
- if rsync_password:
- try:
- module.run_command(["sshpass"])
- except OSError:
- module.fail_json(
- msg="to use rsync connection with passwords, you must install the sshpass program"
- )
- _sshpass_pipe = os.pipe()
- cmd = ['sshpass', '-d' + to_native(_sshpass_pipe[0], errors='surrogate_or_strict')] + cmd
- if compress:
- cmd.append('--compress')
- if rsync_timeout:
- cmd.append('--timeout=%s' % rsync_timeout)
- if module.check_mode:
- cmd.append('--dry-run')
- if delete:
- cmd.append('--delete-after')
- if existing_only:
- cmd.append('--existing')
- if checksum:
- cmd.append('--checksum')
- if copy_links:
- cmd.append('--copy-links')
- if archive:
- cmd.append('--archive')
- if recursive is False:
- cmd.append('--no-recursive')
- if links is False:
- cmd.append('--no-links')
- if perms is False:
- cmd.append('--no-perms')
- if times is False:
- cmd.append('--no-times')
- if owner is False:
- cmd.append('--no-owner')
- if group is False:
- cmd.append('--no-group')
- else:
- if recursive is True:
- cmd.append('--recursive')
- if links is True:
- cmd.append('--links')
- if perms is True:
- cmd.append('--perms')
- if times is True:
- cmd.append('--times')
- if owner is True:
- cmd.append('--owner')
- if group is True:
- cmd.append('--group')
- if dirs:
- cmd.append('--dirs')
-
- if source.startswith('rsync://') and dest.startswith('rsync://'):
- module.fail_json(msg='either src or dest must be a localhost', rc=1)
-
- if is_rsh_needed(source, dest):
-
- # https://github.com/ansible/ansible/issues/15907
- has_rsh = False
- for rsync_opt in rsync_opts:
- if '--rsh' in rsync_opt:
- has_rsh = True
- break
-
- # if the user has not supplied an --rsh option go ahead and add ours
- if not has_rsh:
- ssh_cmd = [module.get_bin_path('ssh', required=True), '-S', 'none']
- if private_key is not None:
- ssh_cmd.extend(['-i', private_key])
- # If the user specified a port value
- # Note: The action plugin takes care of setting this to a port from
- # inventory if the user didn't specify an explicit dest_port
- if dest_port is not None:
- ssh_cmd.extend(['-o', 'Port=%s' % dest_port])
- if not verify_host:
- ssh_cmd.extend(['-o', 'StrictHostKeyChecking=no', '-o', 'UserKnownHostsFile=/dev/null'])
- ssh_cmd_str = ' '.join(shlex_quote(arg) for arg in ssh_cmd)
- if ssh_args:
- ssh_cmd_str += ' %s' % ssh_args
- cmd.append('--rsh=%s' % ssh_cmd_str)
-
- if rsync_path:
- cmd.append('--rsync-path=%s' % rsync_path)
-
- if rsync_opts:
- if '' in rsync_opts:
- module.warn('The empty string is present in rsync_opts which will cause rsync to'
- ' transfer the current working directory. If this is intended, use "."'
- ' instead to get rid of this warning. If this is unintended, check for'
- ' problems in your playbook leading to empty string in rsync_opts.')
- cmd.extend(rsync_opts)
-
- if partial:
- cmd.append('--partial')
-
- if link_dest:
- cmd.append('-H')
- # verbose required because rsync does not believe that adding a
- # hardlink is actually a change
- cmd.append('-vv')
- for x in link_dest:
- link_path = os.path.abspath(os.path.expanduser(x))
- destination_path = os.path.abspath(os.path.dirname(dest))
- if destination_path.find(link_path) == 0:
- module.fail_json(msg='Hardlinking into a subdirectory of the source would cause recursion. %s and %s' % (destination_path, dest))
- cmd.append('--link-dest=%s' % link_path)
-
- changed_marker = '<<CHANGED>>'
- cmd.append('--out-format=' + changed_marker + '%i %n%L')
-
- # expand the paths
- if '@' not in source:
- source = os.path.expanduser(source)
- if '@' not in dest:
- dest = os.path.expanduser(dest)
-
- cmd.append(source)
- cmd.append(dest)
- cmdstr = ' '.join(cmd)
-
- # If we are using password authentication, write the password into the pipe
- if rsync_password:
- def _write_password_to_pipe(proc):
- os.close(_sshpass_pipe[0])
- try:
- os.write(_sshpass_pipe[1], to_bytes(rsync_password) + b'\n')
- except OSError as exc:
- # Ignore broken pipe errors if the sshpass process has exited.
- if exc.errno != errno.EPIPE or proc.poll() is None:
- raise
-
- (rc, out, err) = module.run_command(
- cmd, pass_fds=_sshpass_pipe,
- before_communicate_callback=_write_password_to_pipe)
- else:
- (rc, out, err) = module.run_command(cmd)
-
- if rc:
- return module.fail_json(msg=err, rc=rc, cmd=cmdstr)
-
- if link_dest:
- # a leading period indicates no change
- changed = (changed_marker + '.') not in out
- else:
- changed = changed_marker in out
-
- out_clean = out.replace(changed_marker, '')
- out_lines = out_clean.split('\n')
- while '' in out_lines:
- out_lines.remove('')
- if module._diff:
- diff = {'prepared': out_clean}
- return module.exit_json(changed=changed, msg=out_clean,
- rc=rc, cmd=cmdstr, stdout_lines=out_lines,
- diff=diff)
-
- return module.exit_json(changed=changed, msg=out_clean,
- rc=rc, cmd=cmdstr, stdout_lines=out_lines)
-
-
-if __name__ == '__main__':
- main()