def _get_connection(self, variables): ''' Reads the connection property for the host, and returns the correct connection object from the list of connection plugins ''' # FIXME: delegate_to calculation should be done here # FIXME: calculation of connection params/auth stuff should be done here self._connection_info.remote_addr = self._host.ipv4_address if self._task.delegate_to is not None: self._compute_delegate(variables) # FIXME: add all port/connection type munging here (accelerated mode, # fixing up options for ssh, etc.)? and 'smart' conversion conn_type = self._connection_info.connection if conn_type == 'smart': conn_type = 'ssh' connection = connection_loader.get(conn_type, self._connection_info) if not connection: raise AnsibleError("the connection plugin '%s' was not found" % conn_type) connection.connect() return connection
def _get_connection(self, variables): ''' Reads the connection property for the host, and returns the correct connection object from the list of connection plugins ''' # FIXME: delegate_to calculation should be done here # FIXME: calculation of connection params/auth stuff should be done here self._connection_info.remote_addr = self._host.ipv4_address if self._task.delegate_to is not None: self._compute_delegate(variables) conn_type = self._connection_info.connection if conn_type == 'smart': conn_type = 'ssh' if sys.platform.startswith('darwin') and self._connection_info.remote_pass: # due to a current bug in sshpass on OSX, which can trigger # a kernel panic even for non-privileged users, we revert to # paramiko on that OS when a SSH password is specified conn_type = "paramiko" else: # see if SSH can support ControlPersist if not use paramiko cmd = subprocess.Popen(['ssh','-o','ControlPersist'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (out, err) = cmd.communicate() if "Bad configuration option" in err: conn_type = "paramiko" connection = connection_loader.get(conn_type, self._connection_info, self._new_stdin) if not connection: raise AnsibleError("the connection plugin '%s' was not found" % conn_type) return connection
def run(self, tmp=None, task_vars=None): ''' generates params and passes them on to the rsync module ''' # When modifying this function be aware of the tricky convolutions # your thoughts have to go through: # # In normal ansible, we connect from controller to inventory_hostname # (playbook's hosts: field) or controller to delegate_to host and run # a module on one of those hosts. # # So things that are directly related to the core of ansible are in # terms of that sort of connection that always originate on the # controller. # # In synchronize we use ansible to connect to either the controller or # to the delegate_to host and then run rsync which makes its own # connection from controller to inventory_hostname or delegate_to to # inventory_hostname. # # That means synchronize needs to have some knowledge of the # controller to inventory_host/delegate host that ansible typically # establishes and use those to construct a command line for rsync to # connect from the inventory_host to the controller/delegate. The # challenge for coders is remembering which leg of the trip is # associated with the conditions that you're checking at any one time. if task_vars is None: task_vars = dict() result = super(ActionModule, self).run(tmp, task_vars) # self._connection accounts for delegate_to so # remote_transport is the transport ansible thought it would need # between the controller and the delegate_to host or the controller # and the remote_host if delegate_to isn't set. remote_transport = False if self._connection.transport != 'local': remote_transport = True try: delegate_to = self._task.delegate_to except (AttributeError, KeyError): delegate_to = None # ssh paramiko and local are fully supported transports. Anything # else only works with delegate_to if delegate_to is None and self._connection.transport not in ('ssh', 'paramiko', 'local'): result['failed'] = True result['msg'] = "synchronize uses rsync to function. rsync needs to connect to the remote host via ssh or a direct filesystem copy. This remote host is being accessed via %s instead so it cannot work." % self._connection.transport return result use_ssh_args = self._task.args.pop('use_ssh_args', None) # Parameter name needed by the ansible module self._task.args['_local_rsync_path'] = task_vars.get('ansible_rsync_path') or 'rsync' # rsync thinks that one end of the connection is localhost and the # other is the host we're running the task for (Note: We use # ansible's delegate_to mechanism to determine which host rsync is # running on so localhost could be a non-controller machine if # delegate_to is used) src_host = '127.0.0.1' inventory_hostname = task_vars.get('inventory_hostname') dest_host_inventory_vars = task_vars['hostvars'].get(inventory_hostname) try: dest_host = dest_host_inventory_vars['ansible_host'] except KeyError: dest_host = dest_host_inventory_vars.get('ansible_ssh_host', inventory_hostname) localhost_ports = set() for host in C.LOCALHOST: localhost_vars = task_vars['hostvars'].get(host, {}) for port_var in MAGIC_VARIABLE_MAPPING['port']: port = localhost_vars.get(port_var, None) if port: break else: port = C.DEFAULT_REMOTE_PORT localhost_ports.add(port) # dest_is_local tells us if the host rsync runs on is the same as the # host rsync puts the files on. This is about *rsync's connection*, # not about the ansible connection to run the module. dest_is_local = False if not delegate_to and remote_transport is False: dest_is_local = True elif delegate_to and delegate_to == dest_host: dest_is_local = True # CHECK FOR NON-DEFAULT SSH PORT inv_port = task_vars.get('ansible_ssh_port', None) or C.DEFAULT_REMOTE_PORT if self._task.args.get('dest_port', None) is None: if inv_port is not None: self._task.args['dest_port'] = inv_port # Set use_delegate if we are going to run rsync on a delegated host # instead of localhost use_delegate = False if dest_host == delegate_to: # edge case: explicit delegate and dest_host are the same # so we run rsync on the remote machine targeting its localhost # (itself) dest_host = '127.0.0.1' use_delegate = True elif delegate_to is not None and remote_transport: # If we're delegating to a remote host then we need to use the # delegate_to settings use_delegate = True # Delegate to localhost as the source of the rsync unless we've been # told (via delegate_to) that a different host is the source of the # rsync if not use_delegate and remote_transport: # Create a connection to localhost to run rsync on new_stdin = self._connection._new_stdin # Unike port, there can be only one shell localhost_shell = None for host in C.LOCALHOST: localhost_vars = task_vars['hostvars'].get(host, {}) for shell_var in MAGIC_VARIABLE_MAPPING['shell']: localhost_shell = localhost_vars.get(shell_var, None) if localhost_shell: break if localhost_shell: break else: localhost_shell = os.path.basename(C.DEFAULT_EXECUTABLE) self._play_context.shell = localhost_shell new_connection = connection_loader.get('local', self._play_context, new_stdin) self._connection = new_connection self._override_module_replaced_vars(task_vars) # SWITCH SRC AND DEST HOST PER MODE if self._task.args.get('mode', 'push') == 'pull': (dest_host, src_host) = (src_host, dest_host) # MUNGE SRC AND DEST PER REMOTE_HOST INFO src = self._task.args.get('src', None) dest = self._task.args.get('dest', None) if not dest_is_local: # Private key handling if use_delegate: private_key = task_vars.get('ansible_ssh_private_key_file') or self._play_context.private_key_file else: private_key = task_vars.get('ansible_ssh_private_key_file') or self._play_context.private_key_file if private_key is not None: private_key = os.path.expanduser(private_key) self._task.args['private_key'] = private_key # Src and dest rsync "path" handling # Determine if we need a user@ user = None if boolean(self._task.args.get('set_remote_user', 'yes')): if use_delegate: user = task_vars.get('ansible_delegated_vars', dict()).get('ansible_ssh_user', None) if not user: user = C.DEFAULT_REMOTE_USER else: user = task_vars.get('ansible_ssh_user') or self._play_context.remote_user # use the mode to define src and dest's url if self._task.args.get('mode', 'push') == 'pull': # src is a remote path: <user>@<host>, dest is a local path src = self._process_remote(src_host, src, user, inv_port in localhost_ports) dest = self._process_origin(dest_host, dest, user) else: # src is a local path, dest is a remote path: <user>@<host> src = self._process_origin(src_host, src, user) dest = self._process_remote(dest_host, dest, user, inv_port in localhost_ports) else: # Still need to munge paths (to account for roles) even if we aren't # copying files between hosts if not src.startswith('/'): src = self._get_absolute_path(path=src) if not dest.startswith('/'): dest = self._get_absolute_path(path=dest) self._task.args['src'] = src self._task.args['dest'] = dest # Allow custom rsync path argument rsync_path = self._task.args.get('rsync_path', None) if not dest_is_local: if self._play_context.become and not rsync_path: # If no rsync_path is set, become was originally set, and dest is # remote then add privilege escalation here. if self._play_context.become_method == 'sudo': rsync_path = 'sudo rsync' # TODO: have to add in the rest of the become methods here # We cannot use privilege escalation on the machine running the # module. Instead we run it on the machine rsync is connecting # to. self._play_context.become = False # make sure rsync path is quoted. if rsync_path: self._task.args['rsync_path'] = '"%s"' % rsync_path if use_ssh_args: ssh_args = [ getattr(self._play_context, 'ssh_args', ''), getattr(self._play_context, 'ssh_common_args', ''), getattr(self._play_context, 'ssh_extra_args', ''), ] self._task.args['ssh_args'] = ' '.join([a for a in ssh_args if a]) # run the module and store the result result.update(self._execute_module('synchronize', task_vars=task_vars)) if 'SyntaxError' in result.get('exception', result.get('msg', '')): # Emit a warning about using python3 because synchronize is # somewhat unique in running on localhost result['exception'] = result['msg'] result['msg'] = 'SyntaxError parsing module. Perhaps invoking "python" on your local (or delegate_to) machine invokes python3. You can set ansible_python_interpreter for localhost (or the delegate_to machine) to the location of python2 to fix this' return result
def run(self, tmp=None, task_vars=dict()): ''' generates params and passes them on to the rsync module ''' original_transport = task_vars.get('ansible_connection') or self._play_context.connection remote_transport = False if original_transport != 'local': remote_transport = True try: delegate_to = self._task.delegate_to except (AttributeError, KeyError): delegate_to = None use_ssh_args = self._task.args.pop('use_ssh_args', None) # Parameter name needed by the ansible module self._task.args['_local_rsync_path'] = task_vars.get('ansible_rsync_path') or 'rsync' # rsync thinks that one end of the connection is localhost and the # other is the host we're running the task for (Note: We use # ansible's delegate_to mechanism to determine which host rsync is # running on so localhost could be a non-controller machine if # delegate_to is used) src_host = '127.0.0.1' dest_host = task_vars.get('ansible_ssh_host') or task_vars.get('inventory_hostname') dest_is_local = dest_host in C.LOCALHOST # CHECK FOR NON-DEFAULT SSH PORT if self._task.args.get('dest_port', None) is None: inv_port = task_vars.get('ansible_ssh_port', None) or C.DEFAULT_REMOTE_PORT if inv_port is not None: self._task.args['dest_port'] = inv_port # Set use_delegate if we are going to run rsync on a delegated host # instead of localhost use_delegate = False if dest_host == delegate_to: # edge case: explicit delegate and dest_host are the same # so we run rsync on the remote machine targetting its localhost # (itself) dest_host = '127.0.0.1' use_delegate = True elif delegate_to is not None and remote_transport: # If we're delegating to a remote host then we need to use the # delegate_to settings use_delegate = True # Delegate to localhost as the source of the rsync unless we've been # told (via delegate_to) that a different host is the source of the # rsync transport_overridden = False if not use_delegate and remote_transport: # Create a connection to localhost to run rsync on new_stdin = self._connection._new_stdin new_connection = connection_loader.get('local', self._play_context, new_stdin) self._connection = new_connection transport_overridden = True self._override_module_replaced_vars(task_vars) # COMPARE DELEGATE, HOST AND TRANSPORT between_multiple_hosts = False if dest_host != src_host and remote_transport: # We're not copying two filesystem trees on the same host so we # need to correctly format the paths for rsync (like # user@host:path/to/tree between_multiple_hosts = True # SWITCH SRC AND DEST HOST PER MODE if self._task.args.get('mode', 'push') == 'pull': (dest_host, src_host) = (src_host, dest_host) # MUNGE SRC AND DEST PER REMOTE_HOST INFO src = self._task.args.get('src', None) dest = self._task.args.get('dest', None) if between_multiple_hosts or use_delegate: # Private key handling if use_delegate: private_key = task_vars.get('ansible_ssh_private_key_file') or self._play_context.private_key_file else: private_key = task_vars.get('ansible_ssh_private_key_file') or self._play_context.private_key_file if private_key is not None: private_key = os.path.expanduser(private_key) self._task.args['private_key'] = private_key # Src and dest rsync "path" handling # Determine if we need a user@ user = None if boolean(self._task.args.get('set_remote_user', 'yes')): if use_delegate: user = task_vars.get('ansible_delegated_vars', dict()).get('ansible_ssh_user', None) if not user: user = C.DEFAULT_REMOTE_USER else: user = task_vars.get('ansible_ssh_user') or self._play_context.remote_user # use the mode to define src and dest's url if self._task.args.get('mode', 'push') == 'pull': # src is a remote path: <user>@<host>, dest is a local path src = self._process_remote(src_host, src, user) dest = self._process_origin(dest_host, dest, user) else: # src is a local path, dest is a remote path: <user>@<host> src = self._process_origin(src_host, src, user) dest = self._process_remote(dest_host, dest, user) else: # Still need to munge paths (to account for roles) even if we aren't # copying files between hosts if not src.startswith('/'): src = self._get_absolute_path(path=src) if not dest.startswith('/'): dest = self._get_absolute_path(path=dest) self._task.args['src'] = src self._task.args['dest'] = dest # Allow custom rsync path argument rsync_path = self._task.args.get('rsync_path', None) # If no rsync_path is set, sudo was originally set, and dest is remote then add 'sudo rsync' argument if not rsync_path and transport_overridden and self._play_context.become and self._play_context.become_method == 'sudo' and not dest_is_local: rsync_path = 'sudo rsync' # make sure rsync path is quoted. if rsync_path: self._task.args['rsync_path'] = '"%s"' % rsync_path if use_ssh_args: self._task.args['ssh_args'] = C.ANSIBLE_SSH_ARGS # run the module and store the result result = self._execute_module('synchronize', task_vars=task_vars) if 'SyntaxError' in result['msg']: # Emit a warning about using python3 because synchronize is # somewhat unique in running on localhost result['traceback'] = result['msg'] result['msg'] = 'SyntaxError parsing module. Perhaps invoking "python" on your local (or delegate_to) machine invokes python3. You can set ansible_python_interpreter for localhost (or the delegate_to machine) to the location of python2 to fix this' return result
def _get_socket_path(self, play_context): ssh = connection_loader.get('ssh', class_only=True) cp = ssh._create_control_path(play_context.remote_addr, play_context.port, play_context.remote_user) path = unfrackpath("$HOME/.ansible/pc") return cp % dict(directory=path)
def run(self, tmp=None, task_vars=None): ''' generates params and passes them on to the rsync module ''' # When modifying this function be aware of the tricky convolutions # your thoughts have to go through: # # In normal ansible, we connect from controller to inventory_hostname # (playbook's hosts: field) or controller to delegate_to host and run # a module on one of those hosts. # # So things that are directly related to the core of ansible are in # terms of that sort of connection that always originate on the # controller. # # In synchronize we use ansible to connect to either the controller or # to the delegate_to host and then run rsync which makes its own # connection from controller to inventory_hostname or delegate_to to # inventory_hostname. # # That means synchronize needs to have some knowledge of the # controller to inventory_host/delegate host that ansible typically # establishes and use those to construct a command line for rsync to # connect from the inventory_host to the controller/delegate. The # challenge for coders is remembering which leg of the trip is # associated with the conditions that you're checking at any one time. if task_vars is None: task_vars = dict() result = super(ActionModule, self).run(tmp, task_vars) # Store remote connection type self._remote_transport = self._connection.transport # Handle docker connection options if self._remote_transport == 'docker': self._docker_cmd = self._connection.docker_cmd if self._play_context.docker_extra_args: self._docker_cmd = "%s %s" % ( self._docker_cmd, self._play_context.docker_extra_args) # self._connection accounts for delegate_to so # remote_transport is the transport ansible thought it would need # between the controller and the delegate_to host or the controller # and the remote_host if delegate_to isn't set. remote_transport = False if self._connection.transport != 'local': remote_transport = True try: delegate_to = self._task.delegate_to except (AttributeError, KeyError): delegate_to = None # ssh paramiko docker and local are fully supported transports. Anything # else only works with delegate_to if delegate_to is None and self._connection.transport not in ( 'ssh', 'paramiko', 'local', 'docker'): result['failed'] = True result[ 'msg'] = "synchronize uses rsync to function. rsync needs to connect to the remote host via ssh, docker client or a direct filesystem copy. This remote host is being accessed via %s instead so it cannot work." % self._connection.transport return result use_ssh_args = self._task.args.pop('use_ssh_args', None) # Parameter name needed by the ansible module self._task.args['_local_rsync_path'] = task_vars.get( 'ansible_rsync_path') or 'rsync' # rsync thinks that one end of the connection is localhost and the # other is the host we're running the task for (Note: We use # ansible's delegate_to mechanism to determine which host rsync is # running on so localhost could be a non-controller machine if # delegate_to is used) src_host = '127.0.0.1' inventory_hostname = task_vars.get('inventory_hostname') dest_host_inventory_vars = task_vars['hostvars'].get( inventory_hostname) try: dest_host = dest_host_inventory_vars['ansible_host'] except KeyError: dest_host = dest_host_inventory_vars.get('ansible_ssh_host', inventory_hostname) localhost_ports = set() for host in C.LOCALHOST: localhost_vars = task_vars['hostvars'].get(host, {}) for port_var in MAGIC_VARIABLE_MAPPING['port']: port = localhost_vars.get(port_var, None) if port: break else: port = C.DEFAULT_REMOTE_PORT localhost_ports.add(port) # dest_is_local tells us if the host rsync runs on is the same as the # host rsync puts the files on. This is about *rsync's connection*, # not about the ansible connection to run the module. dest_is_local = False if not delegate_to and remote_transport is False: dest_is_local = True elif delegate_to and delegate_to == dest_host: dest_is_local = True # CHECK FOR NON-DEFAULT SSH PORT inv_port = task_vars.get('ansible_ssh_port', None) or C.DEFAULT_REMOTE_PORT if self._task.args.get('dest_port', None) is None: if inv_port is not None: self._task.args['dest_port'] = inv_port # Set use_delegate if we are going to run rsync on a delegated host # instead of localhost use_delegate = False if dest_host == delegate_to: # edge case: explicit delegate and dest_host are the same # so we run rsync on the remote machine targeting its localhost # (itself) dest_host = '127.0.0.1' use_delegate = True elif delegate_to is not None and remote_transport: # If we're delegating to a remote host then we need to use the # delegate_to settings use_delegate = True # Delegate to localhost as the source of the rsync unless we've been # told (via delegate_to) that a different host is the source of the # rsync if not use_delegate and remote_transport: # Create a connection to localhost to run rsync on new_stdin = self._connection._new_stdin # Unike port, there can be only one shell localhost_shell = None for host in C.LOCALHOST: localhost_vars = task_vars['hostvars'].get(host, {}) for shell_var in MAGIC_VARIABLE_MAPPING['shell']: localhost_shell = localhost_vars.get(shell_var, None) if localhost_shell: break if localhost_shell: break else: localhost_shell = os.path.basename(C.DEFAULT_EXECUTABLE) self._play_context.shell = localhost_shell new_connection = connection_loader.get('local', self._play_context, new_stdin) self._connection = new_connection self._override_module_replaced_vars(task_vars) # SWITCH SRC AND DEST HOST PER MODE if self._task.args.get('mode', 'push') == 'pull': (dest_host, src_host) = (src_host, dest_host) # MUNGE SRC AND DEST PER REMOTE_HOST INFO src = self._task.args.get('src', None) dest = self._task.args.get('dest', None) if src is None or dest is None: return dict( failed=True, msg="synchronize requires both src and dest parameters are set" ) if not dest_is_local: # Private key handling private_key = self._play_context.private_key_file if private_key is not None: private_key = os.path.expanduser(private_key) self._task.args['private_key'] = private_key # Src and dest rsync "path" handling # Determine if we need a user@ user = None if boolean(self._task.args.get('set_remote_user', 'yes')): if use_delegate: user = task_vars.get('ansible_delegated_vars', dict()).get('ansible_ssh_user', None) if not user: user = C.DEFAULT_REMOTE_USER else: user = task_vars.get( 'ansible_ssh_user') or self._play_context.remote_user # use the mode to define src and dest's url if self._task.args.get('mode', 'push') == 'pull': # src is a remote path: <user>@<host>, dest is a local path src = self._process_remote(src_host, src, user, inv_port in localhost_ports) dest = self._process_origin(dest_host, dest, user) else: # src is a local path, dest is a remote path: <user>@<host> src = self._process_origin(src_host, src, user) dest = self._process_remote(dest_host, dest, user, inv_port in localhost_ports) else: # Still need to munge paths (to account for roles) even if we aren't # copying files between hosts if not src.startswith('/'): src = self._get_absolute_path(path=src) if not dest.startswith('/'): dest = self._get_absolute_path(path=dest) self._task.args['src'] = src self._task.args['dest'] = dest # Allow custom rsync path argument rsync_path = self._task.args.get('rsync_path', None) if not dest_is_local: if self._play_context.become and not rsync_path: # If no rsync_path is set, become was originally set, and dest is # remote then add privilege escalation here. if self._play_context.become_method == 'sudo': rsync_path = 'sudo rsync' # TODO: have to add in the rest of the become methods here # We cannot use privilege escalation on the machine running the # module. Instead we run it on the machine rsync is connecting # to. self._play_context.become = False # make sure rsync path is quoted. if rsync_path: self._task.args['rsync_path'] = '"%s"' % rsync_path if use_ssh_args: ssh_args = [ getattr(self._play_context, 'ssh_args', ''), getattr(self._play_context, 'ssh_common_args', ''), getattr(self._play_context, 'ssh_extra_args', ''), ] self._task.args['ssh_args'] = ' '.join([a for a in ssh_args if a]) # If launching synchronize against docker container # use rsync_opts to support container to override rsh options if self._remote_transport in ['docker']: if not isinstance(self._task.args.get('rsync_opts'), list): self._task.args['rsync_opts'] = [] self._task.args['rsync_opts'].append('--blocking-io') if user is not None: self._task.args['rsync_opts'].append( "--rsh='%s exec -u %s -i'" % (self._docker_cmd, user)) else: self._task.args['rsync_opts'].append("--rsh='%s exec -i'" % self._docker_cmd) # run the module and store the result result.update(self._execute_module('synchronize', task_vars=task_vars)) if 'SyntaxError' in result.get('exception', result.get('msg', '')): # Emit a warning about using python3 because synchronize is # somewhat unique in running on localhost result['exception'] = result['msg'] result[ 'msg'] = 'SyntaxError parsing module. Perhaps invoking "python" on your local (or delegate_to) machine invokes python3. You can set ansible_python_interpreter for localhost (or the delegate_to machine) to the location of python2 to fix this' return result
def _execute_meta(self, task, play_context, iterator, target_host): # meta tasks store their args in the _raw_params field of args, # since they do not use k=v pairs, so get that meta_action = task.args.get('_raw_params') # FIXME(s): # * raise an error or show a warning when a conditional is used # on a meta task that doesn't support them def _evaluate_conditional(h): all_vars = self._variable_manager.get_vars(loader=self._loader, play=iterator._play, host=h, task=task) templar = Templar(loader=self._loader, variables=all_vars) return task.evaluate_conditional(templar, all_vars) skipped = False msg = '' if meta_action == 'noop': # FIXME: issue a callback for the noop here? msg="noop" elif meta_action == 'flush_handlers': self.run_handlers(iterator, play_context) msg = "ran handlers" elif meta_action == 'refresh_inventory': self._inventory.refresh_inventory() msg = "inventory successfully refreshed" elif meta_action == 'clear_facts': if _evaluate_conditional(target_host): for host in self._inventory.get_hosts(iterator._play.hosts): self._variable_manager.clear_facts(host) msg = "facts cleared" else: skipped = True elif meta_action == 'clear_host_errors': if _evaluate_conditional(target_host): for host in self._inventory.get_hosts(iterator._play.hosts): self._tqm._failed_hosts.pop(host.name, False) self._tqm._unreachable_hosts.pop(host.name, False) iterator._host_states[host.name].fail_state = iterator.FAILED_NONE msg="cleared host errors" else: skipped = True elif meta_action == 'end_play': if _evaluate_conditional(target_host): for host in self._inventory.get_hosts(iterator._play.hosts): if not host.name in self._tqm._unreachable_hosts: iterator._host_states[host.name].run_state = iterator.ITERATING_COMPLETE msg="ending play" elif meta_action == 'reset_connection': connection = connection_loader.get(play_context.connection, play_context, os.devnull) if connection: connection.reset() msg= 'reset connection' else: msg= 'no connection, nothing to reset' else: raise AnsibleError("invalid meta action requested: %s" % meta_action, obj=task._ds) result = { 'msg': msg } if skipped: result['skipped'] = True else: result['changed'] = False display.vv("META: %s" % msg) return [TaskResult(target_host, task, result)]
def run(self, tmp=None, task_vars=None): ''' generates params and passes them on to the rsync module ''' if task_vars is None: task_vars = dict() result = super(ActionModule, self).run(tmp, task_vars) original_transport = task_vars.get( 'ansible_connection') or self._play_context.connection remote_transport = False if original_transport != 'local': remote_transport = True try: delegate_to = self._task.delegate_to except (AttributeError, KeyError): delegate_to = None use_ssh_args = self._task.args.pop('use_ssh_args', None) # Parameter name needed by the ansible module self._task.args['_local_rsync_path'] = task_vars.get( 'ansible_rsync_path') or 'rsync' # rsync thinks that one end of the connection is localhost and the # other is the host we're running the task for (Note: We use # ansible's delegate_to mechanism to determine which host rsync is # running on so localhost could be a non-controller machine if # delegate_to is used) src_host = '127.0.0.1' inventory_hostname = task_vars.get('inventory_hostname') dest_host_inventory_vars = task_vars['hostvars'].get( inventory_hostname) try: dest_host = dest_host_inventory_vars['ansible_host'] except KeyError: dest_host = dest_host_inventory_vars.get('ansible_ssh_host', inventory_hostname) dest_is_local = dest_host in C.LOCALHOST # CHECK FOR NON-DEFAULT SSH PORT if self._task.args.get('dest_port', None) is None: inv_port = task_vars.get('ansible_ssh_port', None) or C.DEFAULT_REMOTE_PORT if inv_port is not None: self._task.args['dest_port'] = inv_port # Set use_delegate if we are going to run rsync on a delegated host # instead of localhost use_delegate = False if dest_host == delegate_to: # edge case: explicit delegate and dest_host are the same # so we run rsync on the remote machine targeting its localhost # (itself) dest_host = '127.0.0.1' use_delegate = True elif delegate_to is not None and remote_transport: # If we're delegating to a remote host then we need to use the # delegate_to settings use_delegate = True # Delegate to localhost as the source of the rsync unless we've been # told (via delegate_to) that a different host is the source of the # rsync transport_overridden = False if not use_delegate and remote_transport: # Create a connection to localhost to run rsync on new_stdin = self._connection._new_stdin new_connection = connection_loader.get('local', self._play_context, new_stdin) self._connection = new_connection transport_overridden = True self._override_module_replaced_vars(task_vars) # COMPARE DELEGATE, HOST AND TRANSPORT between_multiple_hosts = False if dest_host != src_host and remote_transport: # We're not copying two filesystem trees on the same host so we # need to correctly format the paths for rsync (like # user@host:path/to/tree between_multiple_hosts = True # SWITCH SRC AND DEST HOST PER MODE if self._task.args.get('mode', 'push') == 'pull': (dest_host, src_host) = (src_host, dest_host) # MUNGE SRC AND DEST PER REMOTE_HOST INFO src = self._task.args.get('src', None) dest = self._task.args.get('dest', None) if between_multiple_hosts: # Private key handling if use_delegate: private_key = task_vars.get( 'ansible_ssh_private_key_file' ) or self._play_context.private_key_file else: private_key = task_vars.get( 'ansible_ssh_private_key_file' ) or self._play_context.private_key_file if private_key is not None: private_key = os.path.expanduser(private_key) self._task.args['private_key'] = private_key # Src and dest rsync "path" handling # Determine if we need a user@ user = None if boolean(self._task.args.get('set_remote_user', 'yes')): if use_delegate: user = task_vars.get('ansible_delegated_vars', dict()).get('ansible_ssh_user', None) if not user: user = C.DEFAULT_REMOTE_USER else: user = task_vars.get( 'ansible_ssh_user') or self._play_context.remote_user # use the mode to define src and dest's url if self._task.args.get('mode', 'push') == 'pull': # src is a remote path: <user>@<host>, dest is a local path src = self._process_remote(src_host, src, user) dest = self._process_origin(dest_host, dest, user) else: # src is a local path, dest is a remote path: <user>@<host> src = self._process_origin(src_host, src, user) dest = self._process_remote(dest_host, dest, user) else: # Still need to munge paths (to account for roles) even if we aren't # copying files between hosts if not src.startswith('/'): src = self._get_absolute_path(path=src) if not dest.startswith('/'): dest = self._get_absolute_path(path=dest) self._task.args['src'] = src self._task.args['dest'] = dest # Allow custom rsync path argument rsync_path = self._task.args.get('rsync_path', None) # If no rsync_path is set, sudo was originally set, and dest is remote then add 'sudo rsync' argument if not rsync_path and transport_overridden and self._play_context.become and self._play_context.become_method == 'sudo' and not dest_is_local: rsync_path = 'sudo rsync' # make sure rsync path is quoted. if rsync_path: self._task.args['rsync_path'] = '"%s"' % rsync_path if use_ssh_args: self._task.args['ssh_args'] = C.ANSIBLE_SSH_ARGS # run the module and store the result result.update(self._execute_module('synchronize', task_vars=task_vars)) if 'SyntaxError' in result['msg']: # Emit a warning about using python3 because synchronize is # somewhat unique in running on localhost result['traceback'] = result['msg'] result[ 'msg'] = 'SyntaxError parsing module. Perhaps invoking "python" on your local (or delegate_to) machine invokes python3. You can set ansible_python_interpreter for localhost (or the delegate_to machine) to the location of python2 to fix this' return result
def run(self, tmp=None, task_vars=dict()): ''' generates params and passes them on to the rsync module ''' original_transport = task_vars.get( 'ansible_connection') or self._play_context.connection transport_overridden = False if task_vars.get('delegate_to') is None: task_vars['delegate_to'] = '127.0.0.1' # IF original transport is not local, override transport and disable sudo. if original_transport != 'local': task_vars['ansible_connection'] = 'local' transport_overridden = True self._play_context.become = False use_ssh_args = self._task.args.pop('use_ssh_args', None) # Parameter name needed by the ansible module self._task.args['_local_rsync_path'] = task_vars.get( 'ansible_rsync_path') or 'rsync' # from the perspective of the rsync call the delegate is the localhost src_host = '127.0.0.1' dest_host = task_vars.get('ansible_ssh_host') or task_vars.get( 'inventory_hostname') ### FIXME: do we still need to explicitly template ansible_ssh_host here in v2? dest_is_local = dest_host in ['127.0.0.1', 'localhost'] # CHECK FOR NON-DEFAULT SSH PORT dest_port = task_vars.get('ansible_ssh_port') or self._task.args.get( 'dest_port') or 22 # CHECK DELEGATE HOST INFO use_delegate = False if dest_host == task_vars.get('delegate_to'): # edge case: explicit delegate and dest_host are the same dest_host = '127.0.0.1' use_delegate = True else: if 'hostvars' in task_vars: if task_vars.get('delegate_to') in task_vars[ 'hostvars'] and original_transport != 'local': # use a delegate host instead of localhost use_delegate = True # COMPARE DELEGATE, HOST AND TRANSPORT process_args = False if not dest_host is src_host and original_transport != 'local': # interpret and task_vars remote host info into src or dest process_args = True # SWITCH SRC AND DEST PER MODE if self._task.args.get('mode', 'push') == 'pull': (dest_host, src_host) = (src_host, dest_host) # Delegate to localhost as the source of the rsync unless we've been # told (via delegate_to) that a different host is the source of the # rsync if not use_delegate: # Create a connection to localhost to run rsync on ### FIXME: Do we have to dupe stdin or is this sufficient? new_stdin = self._connection._new_stdin new_connection = connection_loader.get('local', self._play_context, new_stdin) self._connection = new_connection # MUNGE SRC AND DEST PER REMOTE_HOST INFO src = self._task.args.get('src', None) dest = self._task.args.get('dest', None) if process_args or use_delegate: user = None if boolean(task_vars.get('set_remote_user', 'yes')): if use_delegate: user = task_vars['hostvars'][conn.delegate].get( 'ansible_ssh_user') if not use_delegate or not user: user = task_vars.get( 'ansible_ssh_user') or self._play_context.remote_user if use_delegate: # FIXME private_key = task_vars.get( 'ansible_ssh_private_key_file' ) or self._play_context.private_key_file else: private_key = task_vars.get( 'ansible_ssh_private_key_file' ) or self._play_context.private_key_file if private_key is not None: private_key = os.path.expanduser(private_key) self._task.args['private_key'] = private_key # use the mode to define src and dest's url if self._task.args.get('mode', 'push') == 'pull': # src is a remote path: <user>@<host>, dest is a local path src = self._process_remote(src_host, src, user) dest = self._process_origin(dest_host, dest, user) else: # src is a local path, dest is a remote path: <user>@<host> src = self._process_origin(src_host, src, user) dest = self._process_remote(dest_host, dest, user) self._task.args['src'] = src self._task.args['dest'] = dest # Remove mode as it is handled purely in this action module if 'mode' in self._task.args: del self._task.args['mode'] # Allow custom rsync path argument. rsync_path = self._task.args.get('rsync_path', None) # If no rsync_path is set, sudo was originally set, and dest is remote then add 'sudo rsync' argument. if not rsync_path and transport_overridden and self._play_context.become and self._play_context.become_method == 'sudo' and not dest_is_local: rsync_path = 'sudo rsync' # make sure rsync path is quoted. if rsync_path: self._task.args['rsync_path'] = '"%s"' % rsync_path if use_ssh_args: self._task.args['ssh_args'] = constants.ANSIBLE_SSH_ARGS # run the module and store the result result = self._execute_module('synchronize', task_vars=task_vars) return result
def run(self, tmp=None, task_vars=None): """ generates params and passes them on to the rsync module """ # When modifying this function be aware of the tricky convolutions # your thoughts have to go through: # # In normal ansible, we connect from controller to inventory_hostname # (playbook's hosts: field) or controller to delegate_to host and run # a module on one of those hosts. # # So things that are directly related to the core of ansible are in # terms of that sort of connection that always originate on the # controller. # # In synchronize we use ansible to connect to either the controller or # to the delegate_to host and then run rsync which makes its own # connection from controller to inventory_hostname or delegate_to to # inventory_hostname. # # That means synchronize needs to have some knowledge of the # controller to inventory_host/delegate host that ansible typically # establishes and use those to construct a command line for rsync to # connect from the inventory_host to the controller/delegate. The # challenge for coders is remembering which leg of the trip is # associated with the conditions that you're checking at any one time. if task_vars is None: task_vars = dict() # We make a copy of the args here because we may fail and be asked to # retry. If that happens we don't want to pass the munged args through # to our next invocation. Munged args are single use only. _tmp_args = self._task.args.copy() result = super(ActionModule, self).run(tmp, task_vars) # Store remote connection type self._remote_transport = self._connection.transport # Handle docker connection options if self._remote_transport == "docker": self._docker_cmd = self._connection.docker_cmd if self._play_context.docker_extra_args: self._docker_cmd = "%s %s" % (self._docker_cmd, self._play_context.docker_extra_args) # self._connection accounts for delegate_to so # remote_transport is the transport ansible thought it would need # between the controller and the delegate_to host or the controller # and the remote_host if delegate_to isn't set. remote_transport = False if self._connection.transport != "local": remote_transport = True try: delegate_to = self._task.delegate_to except (AttributeError, KeyError): delegate_to = None # ssh paramiko docker and local are fully supported transports. Anything # else only works with delegate_to if delegate_to is None and self._connection.transport not in ("ssh", "paramiko", "local", "docker"): result["failed"] = True result["msg"] = ( "synchronize uses rsync to function. rsync needs to connect to the remote host via ssh, docker client or a direct filesystem copy. This remote host is being accessed via %s instead so it cannot work." % self._connection.transport ) return result use_ssh_args = _tmp_args.pop("use_ssh_args", None) # Parameter name needed by the ansible module _tmp_args["_local_rsync_path"] = task_vars.get("ansible_rsync_path") or "rsync" # rsync thinks that one end of the connection is localhost and the # other is the host we're running the task for (Note: We use # ansible's delegate_to mechanism to determine which host rsync is # running on so localhost could be a non-controller machine if # delegate_to is used) src_host = "127.0.0.1" inventory_hostname = task_vars.get("inventory_hostname") dest_host_inventory_vars = task_vars["hostvars"].get(inventory_hostname) try: dest_host = dest_host_inventory_vars["ansible_host"] except KeyError: dest_host = dest_host_inventory_vars.get("ansible_ssh_host", inventory_hostname) localhost_ports = set() for host in C.LOCALHOST: localhost_vars = task_vars["hostvars"].get(host, {}) for port_var in MAGIC_VARIABLE_MAPPING["port"]: port = localhost_vars.get(port_var, None) if port: break else: port = C.DEFAULT_REMOTE_PORT localhost_ports.add(port) # dest_is_local tells us if the host rsync runs on is the same as the # host rsync puts the files on. This is about *rsync's connection*, # not about the ansible connection to run the module. dest_is_local = False if not delegate_to and remote_transport is False: dest_is_local = True elif delegate_to and delegate_to == dest_host: dest_is_local = True # CHECK FOR NON-DEFAULT SSH PORT inv_port = task_vars.get("ansible_ssh_port", None) or C.DEFAULT_REMOTE_PORT if _tmp_args.get("dest_port", None) is None: if inv_port is not None: _tmp_args["dest_port"] = inv_port # Set use_delegate if we are going to run rsync on a delegated host # instead of localhost use_delegate = False if dest_host == delegate_to: # edge case: explicit delegate and dest_host are the same # so we run rsync on the remote machine targeting its localhost # (itself) dest_host = "127.0.0.1" use_delegate = True elif delegate_to is not None and remote_transport: # If we're delegating to a remote host then we need to use the # delegate_to settings use_delegate = True # Delegate to localhost as the source of the rsync unless we've been # told (via delegate_to) that a different host is the source of the # rsync if not use_delegate and remote_transport: # Create a connection to localhost to run rsync on new_stdin = self._connection._new_stdin # Unike port, there can be only one shell localhost_shell = None for host in C.LOCALHOST: localhost_vars = task_vars["hostvars"].get(host, {}) for shell_var in MAGIC_VARIABLE_MAPPING["shell"]: localhost_shell = localhost_vars.get(shell_var, None) if localhost_shell: break if localhost_shell: break else: localhost_shell = os.path.basename(C.DEFAULT_EXECUTABLE) self._play_context.shell = localhost_shell new_connection = connection_loader.get("local", self._play_context, new_stdin) self._connection = new_connection self._override_module_replaced_vars(task_vars) # SWITCH SRC AND DEST HOST PER MODE if _tmp_args.get("mode", "push") == "pull": (dest_host, src_host) = (src_host, dest_host) # MUNGE SRC AND DEST PER REMOTE_HOST INFO src = _tmp_args.get("src", None) dest = _tmp_args.get("dest", None) if src is None or dest is None: return dict(failed=True, msg="synchronize requires both src and dest parameters are set") if not dest_is_local: # Private key handling private_key = self._play_context.private_key_file if private_key is not None: private_key = os.path.expanduser(private_key) _tmp_args["private_key"] = private_key # Src and dest rsync "path" handling # Determine if we need a user@ user = None if boolean(_tmp_args.get("set_remote_user", "yes")): if use_delegate: user = task_vars.get("ansible_delegated_vars", dict()).get("ansible_ssh_user", None) if not user: user = C.DEFAULT_REMOTE_USER else: user = task_vars.get("ansible_ssh_user") or self._play_context.remote_user # use the mode to define src and dest's url if _tmp_args.get("mode", "push") == "pull": # src is a remote path: <user>@<host>, dest is a local path src = self._process_remote(_tmp_args, src_host, src, user, inv_port in localhost_ports) dest = self._process_origin(dest_host, dest, user) else: # src is a local path, dest is a remote path: <user>@<host> src = self._process_origin(src_host, src, user) dest = self._process_remote(_tmp_args, dest_host, dest, user, inv_port in localhost_ports) else: # Still need to munge paths (to account for roles) even if we aren't # copying files between hosts if not src.startswith("/"): src = self._get_absolute_path(path=src) if not dest.startswith("/"): dest = self._get_absolute_path(path=dest) _tmp_args["src"] = src _tmp_args["dest"] = dest # Allow custom rsync path argument rsync_path = _tmp_args.get("rsync_path", None) if not dest_is_local: if self._play_context.become and not rsync_path: # If no rsync_path is set, become was originally set, and dest is # remote then add privilege escalation here. if self._play_context.become_method == "sudo": rsync_path = "sudo rsync" # TODO: have to add in the rest of the become methods here # We cannot use privilege escalation on the machine running the # module. Instead we run it on the machine rsync is connecting # to. self._play_context.become = False # make sure rsync path is quoted. if rsync_path: _tmp_args["rsync_path"] = '"%s"' % rsync_path if use_ssh_args: ssh_args = [ getattr(self._play_context, "ssh_args", ""), getattr(self._play_context, "ssh_common_args", ""), getattr(self._play_context, "ssh_extra_args", ""), ] _tmp_args["ssh_args"] = " ".join([a for a in ssh_args if a]) # If launching synchronize against docker container # use rsync_opts to support container to override rsh options if self._remote_transport in ["docker"]: if not isinstance(self._task.args.get("rsync_opts"), list): self._task.args["rsync_opts"] = self._task.args.get("rsync_opts", "").split(" ") if "--blocking-io" not in self._task.args["rsync_opts"]: self._task.args["rsync_opts"].append("--blocking-io") if user is not None: self._task.args["rsync_opts"].append("--rsh='%s exec -u %s -i'" % (self._docker_cmd, user)) else: self._task.args["rsync_opts"].append("--rsh='%s exec -i'" % self._docker_cmd) # run the module and store the result result.update(self._execute_module("synchronize", module_args=_tmp_args, task_vars=task_vars)) if "SyntaxError" in result.get("exception", result.get("msg", "")): # Emit a warning about using python3 because synchronize is # somewhat unique in running on localhost result["exception"] = result["msg"] result[ "msg" ] = 'SyntaxError parsing module. Perhaps invoking "python" on your local (or delegate_to) machine invokes python3. You can set ansible_python_interpreter for localhost (or the delegate_to machine) to the location of python2 to fix this' return result