2014-09-26 01:01:01 +00:00
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2012-2013, Timothy Appnel <tim@appnel.com>
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
2017-03-14 16:07:22 +00:00
ANSIBLE_METADATA = { ' metadata_version ' : ' 1.0 ' ,
' status ' : [ ' preview ' ] ,
' supported_by ' : ' core ' }
2016-12-06 10:35:05 +00:00
2014-09-26 01:01:01 +00:00
DOCUMENTATION = '''
- - -
module : synchronize
version_added : " 1.4 "
2016-10-21 22:32:54 +00:00
short_description : A wrapper around rsync to make common tasks in your playbooks quick and easy .
2014-09-26 01:01:01 +00:00
description :
2016-10-21 22:32:54 +00:00
- 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 . C ( synchronize ) 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 .
2014-09-26 01:01:01 +00:00
options :
src :
description :
2015-09-12 20:13:48 +00:00
- Path on the source host that will be synchronized to the destination ; The path can be absolute or relative .
2014-09-26 01:01:01 +00:00
required : true
dest :
description :
2015-09-12 20:13:48 +00:00
- Path on the destination host that will be synchronized from the source ; The path can be absolute or relative .
2014-09-26 01:01:01 +00:00
required : true
dest_port :
description :
2015-08-05 19:46:01 +00:00
- Port number for ssh on the destination host . Prior to ansible 2.0 , the ansible_ssh_port inventory var took precedence over this value .
2016-08-08 15:59:25 +00:00
default : Value of ansible_ssh_port for this host , remote_port config setting , or the value from ssh client configuration if none of those are set
2014-09-26 01:01:01 +00:00
version_added : " 1.5 "
mode :
description :
2014-10-12 03:11:30 +00:00
- 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 .
2014-09-26 01:01:01 +00:00
required : false
choices : [ ' push ' , ' pull ' ]
default : ' push '
archive :
description :
- Mirrors the rsync archive flag , enables recursive , links , perms , times , owner , group flags and - D .
choices : [ ' yes ' , ' no ' ]
default : ' yes '
required : false
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 .
choices : [ ' yes ' , ' no ' ]
default : ' no '
required : false
version_added : " 1.6 "
compress :
description :
- Compress file data during the transfer . In most cases , leave this enabled unless it causes problems .
choices : [ ' yes ' , ' no ' ]
default : ' yes '
required : false
version_added : " 1.7 "
existing_only :
description :
- Skip creating new files on receiver .
choices : [ ' yes ' , ' no ' ]
default : ' no '
required : false
version_added : " 1.5 "
delete :
description :
2016-06-10 19:11:24 +00:00
- Delete files in C ( dest ) that don ' t exist (after transfer, not before) in the C(src) path. This option requires C(recursive=yes).
2014-09-26 01:01:01 +00:00
choices : [ ' yes ' , ' no ' ]
default : ' no '
required : false
dirs :
description :
- Transfer directories without recursing
choices : [ ' yes ' , ' no ' ]
default : ' no '
required : false
recursive :
description :
- Recurse into directories .
choices : [ ' yes ' , ' no ' ]
default : the value of the archive option
required : false
links :
description :
- Copy symlinks as symlinks .
choices : [ ' yes ' , ' no ' ]
default : the value of the archive option
required : false
copy_links :
description :
- Copy symlinks as the item that they point to ( the referent ) is copied , rather than the symlink .
choices : [ ' yes ' , ' no ' ]
default : ' no '
required : false
perms :
description :
- Preserve permissions .
choices : [ ' yes ' , ' no ' ]
default : the value of the archive option
required : false
times :
description :
- Preserve modification times
choices : [ ' yes ' , ' no ' ]
default : the value of the archive option
required : false
owner :
description :
- Preserve owner ( super user only )
choices : [ ' yes ' , ' no ' ]
default : the value of the archive option
required : false
group :
description :
- Preserve group
choices : [ ' yes ' , ' no ' ]
default : the value of the archive option
required : false
rsync_path :
description :
2015-09-12 20:13:48 +00:00
- Specify the rsync command to run on the remote host . See C ( - - rsync - path ) on the rsync man page .
2014-09-26 01:01:01 +00:00
required : false
rsync_timeout :
description :
2016-10-21 22:32:54 +00:00
- Specify a - - timeout for the rsync command in seconds .
2014-09-26 01:01:01 +00:00
default : 0
required : false
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 " no " .
default : yes
2015-04-03 14:42:23 +00:00
use_ssh_args :
description :
- Use the ssh_args specified in ansible . cfg
2016-03-09 17:12:21 +00:00
default : " no "
2015-04-03 14:42:23 +00:00
choices :
- " yes "
- " no "
2015-04-03 15:27:33 +00:00
version_added : " 2.0 "
2014-09-26 01:01:01 +00:00
rsync_opts :
description :
- Specify additional rsync options by passing in an array .
default :
required : false
version_added : " 1.6 "
2015-02-12 11:25:36 +00:00
partial :
description :
- Tells rsync to keep the partial file which should make a subsequent transfer of the rest of the file much faster .
default : no
required : false
2015-06-25 19:44:08 +00:00
version_added : " 2.0 "
2015-07-19 10:45:31 +00:00
verify_host :
description :
- Verify destination host key .
default : no
required : false
version_added : " 2.0 "
2014-09-26 01:01:01 +00:00
notes :
2015-09-12 20:13:48 +00:00
- 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 ` .
2016-01-21 16:37:15 +00:00
- 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 .
2016-01-21 16:24:33 +00:00
- In 2.0 .0 .0 a bug in the synchronize module made become occur on the " local host " . This was fixed in 2.0 .1 .
2017-03-07 18:22:24 +00:00
- 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 .
2015-09-12 20:13:48 +00:00
- Expect that dest = ~ / x will be ~ < remote_user > / x even if using sudo .
2014-09-26 01:01:01 +00:00
- Inspect the verbose output to validate the destination user / host / path
are what was expected .
2016-10-21 22:32:54 +00:00
- To exclude files and directories from being synchronized , you may add
2014-09-26 01:01:01 +00:00
C ( . rsync - filter ) files to the source directory .
2016-04-08 15:41:53 +00:00
- rsync daemon must be up and running with correct permission when using
rsync protocol in source or destination path .
2016-10-21 22:32:54 +00:00
- 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 .
2015-06-15 19:53:30 +00:00
author : " Timothy Appnel (@tima) "
2014-09-26 01:01:01 +00:00
'''
EXAMPLES = '''
# Synchronization of src on the control machine to dest on the remote hosts
2016-10-12 21:37:40 +00:00
- synchronize :
src : some / relative / path
dest : / some / absolute / path
2014-09-26 01:01:01 +00:00
2016-04-08 15:41:53 +00:00
# Synchronization using rsync protocol (push)
2016-10-12 21:37:40 +00:00
- synchronize :
src : some / relative / path /
dest : rsync : / / somehost . com / path /
2016-04-08 15:41:53 +00:00
# Synchronization using rsync protocol (pull)
2016-10-12 21:37:40 +00:00
- synchronize :
mode : pull
src : rsync : / / somehost . com / path /
dest : / some / absolute / path /
2016-04-08 15:41:53 +00:00
# Synchronization using rsync protocol on delegate host (push)
2016-10-12 21:37:40 +00:00
- synchronize :
src : / some / absolute / path /
dest : rsync : / / somehost . com / path /
delegate_to : delegate . host
2016-04-08 15:41:53 +00:00
# Synchronization using rsync protocol on delegate host (pull)
2016-10-12 21:37:40 +00:00
- synchronize :
mode : pull
src : rsync : / / somehost . com / path /
dest : / some / absolute / path /
delegate_to : delegate . host
2016-04-08 15:41:53 +00:00
2014-09-26 01:01:01 +00:00
# Synchronization without any --archive options enabled
2016-10-12 21:37:40 +00:00
- synchronize :
src : some / relative / path
dest : / some / absolute / path
archive : no
2014-09-26 01:01:01 +00:00
# Synchronization with --archive options enabled except for --recursive
2016-10-12 21:37:40 +00:00
- synchronize :
src : some / relative / path
dest : / some / absolute / path
recursive : no
2014-09-26 01:01:01 +00:00
# Synchronization with --archive options enabled except for --times, with --checksum option enabled
2016-10-12 21:37:40 +00:00
- synchronize :
src : some / relative / path
dest : / some / absolute / path
checksum : yes
times : no
2014-09-26 01:01:01 +00:00
# Synchronization without --archive options enabled except use --links
2016-10-12 21:37:40 +00:00
- synchronize :
src : some / relative / path
dest : / some / absolute / path
archive : no
links : yes
2014-09-26 01:01:01 +00:00
# Synchronization of two paths both on the control machine
2017-02-07 21:39:24 +00:00
- synchronize :
2016-10-12 21:37:40 +00:00
src : some / relative / path
dest : / some / absolute / path
delegate_to : localhost
2014-09-26 01:01:01 +00:00
2016-01-21 16:24:33 +00:00
# Synchronization of src on the inventory host to the dest on the localhost in pull mode
2016-10-12 21:37:40 +00:00
- synchronize :
mode : pull
src : some / relative / path
dest : / some / absolute / path
2014-09-26 01:01:01 +00:00
2014-09-29 21:02:08 +00:00
# Synchronization of src on delegate host to dest on the current inventory host.
2016-10-12 21:37:40 +00:00
- synchronize :
2016-01-21 16:24:33 +00:00
src : / first / absolute / path
dest : / second / absolute / path
2016-10-12 21:37:40 +00:00
delegate_to : delegate . host
2014-09-26 01:01:01 +00:00
2016-01-21 16:24:33 +00:00
# Synchronize two directories on one remote host.
2016-10-12 21:37:40 +00:00
- synchronize :
2016-01-21 16:24:33 +00:00
src : / first / absolute / path
dest : / second / absolute / path
2016-10-12 21:37:40 +00:00
delegate_to : " {{ inventory_hostname }} "
2016-01-21 16:24:33 +00:00
2014-09-26 01:01:01 +00:00
# Synchronize and delete files in dest on the remote host that are not found in src of localhost.
2016-10-12 21:37:40 +00:00
- synchronize :
src : some / relative / path
dest : / some / absolute / path
delete : yes
recursive : yes
2014-09-26 01:01:01 +00:00
# Synchronize using an alternate rsync command
2016-01-21 16:24:33 +00:00
# This specific command is granted su privileges on the destination
2016-10-12 21:37:40 +00:00
- synchronize :
src : some / relative / path
dest : / some / absolute / path
rsync_path : " su -c rsync "
2014-09-26 01:01:01 +00:00
# Example .rsync-filter file in the source directory
2017-02-07 21:39:24 +00:00
# - 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
2014-09-26 01:01:01 +00:00
# Synchronize passing in extra rsync options
2016-10-12 21:37:40 +00:00
- synchronize :
2015-09-22 20:59:37 +00:00
src : / tmp / helloworld
2016-10-13 14:47:50 +00:00
dest : / var / www / helloworld
2015-09-22 20:59:37 +00:00
rsync_opts :
- " --no-motd "
- " --exclude=.git "
2014-09-26 01:01:01 +00:00
'''
2016-01-23 05:05:09 +00:00
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
2014-09-26 01:01:01 +00:00
def main ( ) :
module = AnsibleModule (
argument_spec = dict (
src = dict ( required = True ) ,
dest = dict ( required = True ) ,
2016-08-08 15:59:25 +00:00
dest_port = dict ( default = None , type = ' int ' ) ,
2014-09-26 01:01:01 +00:00
delete = dict ( default = ' no ' , type = ' bool ' ) ,
private_key = dict ( default = None ) ,
rsync_path = dict ( default = None ) ,
2015-07-27 18:35:21 +00:00
_local_rsync_path = dict ( default = ' rsync ' , type = ' path ' ) ,
2016-01-23 05:05:09 +00:00
_substitute_controller = dict ( default = ' no ' , type = ' bool ' ) ,
2014-09-26 01:01:01 +00:00
archive = dict ( default = ' yes ' , type = ' bool ' ) ,
checksum = dict ( default = ' no ' , type = ' bool ' ) ,
compress = dict ( default = ' yes ' , type = ' bool ' ) ,
existing_only = dict ( default = ' no ' , type = ' bool ' ) ,
dirs = dict ( default = ' no ' , type = ' bool ' ) ,
recursive = dict ( type = ' bool ' ) ,
links = dict ( type = ' bool ' ) ,
copy_links = dict ( type = ' bool ' ) ,
perms = dict ( type = ' bool ' ) ,
times = dict ( type = ' bool ' ) ,
owner = dict ( type = ' bool ' ) ,
group = dict ( type = ' bool ' ) ,
set_remote_user = dict ( default = ' yes ' , type = ' bool ' ) ,
rsync_timeout = dict ( type = ' int ' , default = 0 ) ,
2015-04-03 14:42:23 +00:00
rsync_opts = dict ( type = ' list ' ) ,
ssh_args = dict ( type = ' str ' ) ,
2015-02-12 11:25:36 +00:00
partial = dict ( default = ' no ' , type = ' bool ' ) ,
2015-07-19 10:45:31 +00:00
verify_host = dict ( default = ' no ' , type = ' bool ' ) ,
2015-08-05 18:54:12 +00:00
mode = dict ( default = ' push ' , choices = [ ' push ' , ' pull ' ] ) ,
2014-09-26 01:01:01 +00:00
) ,
supports_check_mode = True
)
2016-01-23 05:05:09 +00:00
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 ' ] + ' " '
2014-09-26 01:01:01 +00:00
dest_port = module . params [ ' dest_port ' ]
delete = module . params [ ' delete ' ]
private_key = module . params [ ' private_key ' ]
rsync_path = module . params [ ' rsync_path ' ]
2015-07-27 18:35:21 +00:00
rsync = module . params . get ( ' _local_rsync_path ' , ' rsync ' )
2014-09-26 01:01:01 +00:00
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 ' ]
2015-02-12 11:25:36 +00:00
partial = module . params [ ' partial ' ]
2014-09-26 01:01:01 +00:00
# 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 ' ]
2015-04-03 14:42:23 +00:00
ssh_args = module . params [ ' ssh_args ' ]
2015-07-19 10:45:31 +00:00
verify_host = module . params [ ' verify_host ' ]
2014-09-26 01:01:01 +00:00
2016-01-26 17:43:38 +00:00
if ' / ' not in rsync :
rsync = module . get_bin_path ( rsync , required = True )
ssh = module . get_bin_path ( ' ssh ' , required = True )
2014-09-28 10:24:47 +00:00
cmd = ' %s --delay-updates -F ' % rsync
2014-09-26 01:01:01 +00:00
if compress :
cmd = cmd + ' --compress '
if rsync_timeout :
cmd = cmd + ' --timeout= %s ' % rsync_timeout
if module . check_mode :
cmd = cmd + ' --dry-run '
if delete :
cmd = cmd + ' --delete-after '
if existing_only :
cmd = cmd + ' --existing '
if checksum :
cmd = cmd + ' --checksum '
if archive :
cmd = cmd + ' --archive '
if recursive is False :
cmd = cmd + ' --no-recursive '
if links is False :
cmd = cmd + ' --no-links '
if copy_links is True :
cmd = cmd + ' --copy-links '
if perms is False :
cmd = cmd + ' --no-perms '
if times is False :
cmd = cmd + ' --no-times '
if owner is False :
cmd = cmd + ' --no-owner '
if group is False :
cmd = cmd + ' --no-group '
else :
if recursive is True :
cmd = cmd + ' --recursive '
if links is True :
cmd = cmd + ' --links '
if copy_links is True :
cmd = cmd + ' --copy-links '
if perms is True :
cmd = cmd + ' --perms '
if times is True :
cmd = cmd + ' --times '
if owner is True :
cmd = cmd + ' --owner '
if group is True :
cmd = cmd + ' --group '
if dirs :
cmd = cmd + ' --dirs '
if private_key is None :
private_key = ' '
else :
2017-02-17 13:18:41 +00:00
private_key = ' -i " %s " ' % private_key
2014-09-26 01:01:01 +00:00
2015-07-19 10:45:31 +00:00
ssh_opts = ' -S none '
if not verify_host :
2017-01-30 23:01:47 +00:00
ssh_opts = ' %s -o StrictHostKeyChecking=no ' % ssh_opts
2015-07-19 10:45:31 +00:00
2015-05-04 14:58:05 +00:00
if ssh_args :
2017-01-30 23:01:47 +00:00
ssh_opts = ' %s %s ' % ( ssh_opts , ssh_args )
2015-05-04 14:58:05 +00:00
2016-08-02 18:34:41 +00:00
if source . startswith ( ' " rsync:// ' ) and dest . startswith ( ' " rsync:// ' ) :
2016-04-08 15:41:53 +00:00
module . fail_json ( msg = ' either src or dest must be a localhost ' , rc = 1 )
2016-08-02 18:34:41 +00:00
if not source . startswith ( ' " rsync:// ' ) and not dest . startswith ( ' " rsync:// ' ) :
2016-08-08 15:59:25 +00:00
# If the user specified a port value
# Note: The action plugin takes care of setting this to a port from
2016-10-13 14:47:50 +00:00
# inventory if the user didn't specify an explicit dest_port
2016-08-08 15:59:25 +00:00
if dest_port is not None :
2016-04-08 15:41:53 +00:00
cmd + = " --rsh ' ssh %s %s -o Port= %s ' " % ( private_key , ssh_opts , dest_port )
else :
2016-08-08 15:59:25 +00:00
cmd + = " --rsh ' ssh %s %s ' " % ( private_key , ssh_opts )
2014-09-26 01:01:01 +00:00
if rsync_path :
cmd = cmd + " --rsync-path= %s " % ( rsync_path )
if rsync_opts :
cmd = cmd + " " + " " . join ( rsync_opts )
2015-02-12 11:25:36 +00:00
if partial :
cmd = cmd + " --partial "
2014-09-26 01:01:01 +00:00
changed_marker = ' <<CHANGED>> '
cmd = cmd + " --out-format= ' " + changed_marker + " %i % n % L ' "
# expand the paths
if ' @ ' not in source :
2016-10-21 22:32:54 +00:00
source = os . path . expanduser ( source )
2014-09-26 01:01:01 +00:00
if ' @ ' not in dest :
2016-10-21 22:32:54 +00:00
dest = os . path . expanduser ( dest )
2014-09-26 01:01:01 +00:00
cmd = ' ' . join ( [ cmd , source , dest ] )
cmdstr = cmd
( rc , out , err ) = module . run_command ( cmd )
if rc :
return module . fail_json ( msg = err , rc = rc , cmd = cmdstr )
else :
changed = changed_marker in out
out_clean = out . replace ( changed_marker , ' ' )
out_lines = out_clean . split ( ' \n ' )
2016-10-21 22:32:54 +00:00
while ' ' in out_lines :
2014-09-26 01:01:01 +00:00
out_lines . remove ( ' ' )
2016-01-25 12:05:02 +00:00
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 )
else :
return module . exit_json ( changed = changed , msg = out_clean ,
rc = rc , cmd = cmdstr , stdout_lines = out_lines )
2014-09-26 01:01:01 +00:00
# import module snippets
from ansible . module_utils . basic import *
2016-12-05 17:59:46 +00:00
if __name__ == ' __main__ ' :
main ( )