def run(self, tmp=None, task_vars=None): self._supports_check_mode = False result = super(ActionModule, self).run(tmp, task_vars) if task_vars is None: task_vars = dict() src = self._task.args.get('src', None) dest = self._task.args.get('dest', None) delimiter = self._task.args.get('delimiter', None) remote_src = self._task.args.get('remote_src', 'yes') regexp = self._task.args.get('regexp', None) follow = self._task.args.get('follow', False) ignore_hidden = self._task.args.get('ignore_hidden', False) decrypt = self._task.args.get('decrypt', True) if src is None or dest is None: result['failed'] = True result['msg'] = "src and dest are required" return result if boolean(remote_src, strict=False): result.update(self._execute_module(tmp=tmp, task_vars=task_vars)) return result else: try: src = self._find_needle('files', src) except AnsibleError as e: result['failed'] = True result['msg'] = to_native(e) return result if not tmp: tmp = self._make_tmp_path() if not os.path.isdir(src): result['failed'] = True result['msg'] = u"Source (%s) is not a directory" % src return result _re = None if regexp is not None: _re = re.compile(regexp) # Does all work assembling the file path = self._assemble_from_fragments(src, delimiter, _re, ignore_hidden, decrypt) path_checksum = checksum_s(path) dest = self._remote_expand_user(dest) dest_stat = self._execute_remote_stat(dest, all_vars=task_vars, follow=follow, tmp=tmp) diff = {} # setup args for running modules new_module_args = self._task.args.copy() # clean assemble specific options for opt in ['remote_src', 'regexp', 'delimiter', 'ignore_hidden', 'decrypt']: if opt in new_module_args: del new_module_args[opt] new_module_args.update( dict( dest=dest, original_basename=os.path.basename(src), ) ) if path_checksum != dest_stat['checksum']: if self._play_context.diff: diff = self._get_diff_data(dest, path, task_vars) remote_path = self._connection._shell.join_path(tmp, 'src') xfered = self._transfer_file(path, remote_path) # fix file permissions when the copy is done as a different user self._fixup_perms2((tmp, remote_path)) new_module_args.update(dict(src=xfered,)) res = self._execute_module(module_name='copy', module_args=new_module_args, task_vars=task_vars, tmp=tmp, delete_remote_tmp=False) if diff: res['diff'] = diff result.update(res) else: result.update(self._execute_module(module_name='file', module_args=new_module_args, task_vars=task_vars, tmp=tmp, delete_remote_tmp=False)) self._remove_tmp_path(tmp) return result
def run(self, tmp=None, task_vars=dict()): ''' handler for template operations ''' source = self._task.args.get('src', None) dest = self._task.args.get('dest', None) faf = self._task.first_available_file if (source is None and faf is not None) or dest is None: return dict(failed=True, msg="src and dest are required") if tmp is None: tmp = self._make_tmp_path() if faf: source = self._get_first_available_file(faf, task_vars.get('_original_file', None, 'templates')) if source is None: return dict(failed=True, msg="could not find src in first_available_file list") else: if self._task._role is not None: source = self._loader.path_dwim_relative(self._task._role._role_path, 'templates', source) else: source = self._loader.path_dwim_relative(self._loader.get_basedir(), 'templates', source) # Expand any user home dir specification dest = self._remote_expand_user(dest) directory_prepended = False if dest.endswith(os.sep): directory_prepended = True base = os.path.basename(source) dest = os.path.join(dest, base) # template the source data locally & get ready to transfer try: with open(source, 'r') as f: template_data = to_unicode(f.read()) try: template_uid = pwd.getpwuid(os.stat(source).st_uid).pw_name except: template_uid = os.stat(source).st_uid temp_vars = task_vars.copy() temp_vars['template_host'] = os.uname()[1] temp_vars['template_path'] = source temp_vars['template_mtime'] = datetime.datetime.fromtimestamp(os.path.getmtime(source)) temp_vars['template_uid'] = template_uid temp_vars['template_fullpath'] = os.path.abspath(source) temp_vars['template_run_date'] = datetime.datetime.now() managed_default = C.DEFAULT_MANAGED_STR managed_str = managed_default.format( host = temp_vars['template_host'], uid = temp_vars['template_uid'], file = to_bytes(temp_vars['template_path']) ) temp_vars['ansible_managed'] = time.strftime( managed_str, time.localtime(os.path.getmtime(source)) ) # Create a new searchpath list to assign to the templar environment's file # loader, so that it knows about the other paths to find template files searchpath = [self._loader._basedir, os.path.dirname(source)] if self._task._role is not None: searchpath.insert(1, C.DEFAULT_ROLES_PATH) searchpath.insert(1, self._task._role._role_path) self._templar.environment.loader.searchpath = searchpath old_vars = self._templar._available_variables self._templar.set_available_variables(temp_vars) resultant = self._templar.template(template_data, preserve_trailing_newlines=True, escape_backslashes=False, convert_data=False) self._templar.set_available_variables(old_vars) except Exception as e: return dict(failed=True, msg=type(e).__name__ + ": " + str(e)) local_checksum = checksum_s(resultant) remote_checksum = self.get_checksum(dest, task_vars, not directory_prepended, source=source) if isinstance(remote_checksum, dict): # Error from remote_checksum is a dict. Valid return is a str return remote_checksum diff = {} new_module_args = self._task.args.copy() if local_checksum != remote_checksum: dest_contents = '' # if showing diffs, we need to get the remote value if self._play_context.diff: diff = self._get_diff_data(dest, resultant, task_vars, source_file=False) if not self._play_context.check_mode: # do actual work thorugh copy xfered = self._transfer_data(self._connection._shell.join_path(tmp, 'source'), resultant) # fix file permissions when the copy is done as a different user if self._play_context.become and self._play_context.become_user != 'root': self._remote_chmod('a+r', xfered) # run the copy module new_module_args.update( dict( src=xfered, dest=dest, original_basename=os.path.basename(source), follow=True, ), ) result = self._execute_module(module_name='copy', module_args=new_module_args, task_vars=task_vars) else: result=dict(changed=True) if result.get('changed', False) and self._play_context.diff: result['diff'] = diff # result['diff'] = dict(before=dest_contents, after=resultant, before_header=dest, after_header=source) return result else: # when running the file module based on the template data, we do # not want the source filename (the name of the template) to be used, # since this would mess up links, so we clear the src param and tell # the module to follow links. When doing that, we have to set # original_basename to the template just in case the dest is # a directory. new_module_args.update( dict( src=None, original_basename=os.path.basename(source), follow=True, ), ) return self._execute_module(module_name='file', module_args=new_module_args, task_vars=task_vars)
def run(self, tmp=None, task_vars=None): ''' handler for fetch operations ''' if task_vars is None: task_vars = dict() result = super(ActionModule, self).run(tmp, task_vars) del tmp # tmp no longer has any effect try: if self._play_context.check_mode: result['skipped'] = True result['msg'] = 'check mode not (yet) supported for this module' return result source = self._task.args.get('src', None) dest = self._task.args.get('dest', None) flat = boolean(self._task.args.get('flat'), strict=False) fail_on_missing = boolean(self._task.args.get('fail_on_missing', True), strict=False) validate_checksum = boolean(self._task.args.get('validate_checksum', self._task.args.get('validate_md5', True)), strict=False) # validate source and dest are strings FIXME: use basic.py and module specs if not isinstance(source, string_types): result['msg'] = "Invalid type supplied for source option, it must be a string" if not isinstance(dest, string_types): result['msg'] = "Invalid type supplied for dest option, it must be a string" # validate_md5 is the deprecated way to specify validate_checksum if 'validate_md5' in self._task.args and 'validate_checksum' in self._task.args: result['msg'] = "validate_checksum and validate_md5 cannot both be specified" if 'validate_md5' in self._task.args: display.deprecated('Use validate_checksum instead of validate_md5', version='2.8') if source is None or dest is None: result['msg'] = "src and dest are required" if result.get('msg'): result['failed'] = True return result source = self._connection._shell.join_path(source) source = self._remote_expand_user(source) remote_checksum = None if not self._play_context.become: # calculate checksum for the remote file, don't bother if using become as slurp will be used # Force remote_checksum to follow symlinks because fetch always follows symlinks remote_checksum = self._remote_checksum(source, all_vars=task_vars, follow=True) # use slurp if permissions are lacking or privilege escalation is needed remote_data = None if remote_checksum in ('1', '2', None): slurpres = self._execute_module(module_name='slurp', module_args=dict(src=source), task_vars=task_vars) if slurpres.get('failed'): if not fail_on_missing and (slurpres.get('msg').startswith('file not found') or remote_checksum == '1'): result['msg'] = "the remote file does not exist, not transferring, ignored" result['file'] = source result['changed'] = False else: result.update(slurpres) return result else: if slurpres['encoding'] == 'base64': remote_data = base64.b64decode(slurpres['content']) if remote_data is not None: remote_checksum = checksum_s(remote_data) # the source path may have been expanded on the # target system, so we compare it here and use the # expanded version if it's different remote_source = slurpres.get('source') if remote_source and remote_source != source: source = remote_source # calculate the destination name if os.path.sep not in self._connection._shell.join_path('a', ''): source = self._connection._shell._unquote(source) source_local = source.replace('\\', '/') else: source_local = source dest = os.path.expanduser(dest) if flat: if os.path.isdir(to_bytes(dest, errors='surrogate_or_strict')) and not dest.endswith(os.sep): result['msg'] = "dest is an existing directory, use a trailing slash if you want to fetch src into that directory" result['file'] = dest result['failed'] = True return result if dest.endswith(os.sep): # if the path ends with "/", we'll use the source filename as the # destination filename base = os.path.basename(source_local) dest = os.path.join(dest, base) if not dest.startswith("/"): # if dest does not start with "/", we'll assume a relative path dest = self._loader.path_dwim(dest) else: # files are saved in dest dir, with a subdir for each host, then the filename if 'inventory_hostname' in task_vars: target_name = task_vars['inventory_hostname'] else: target_name = self._play_context.remote_addr dest = "%s/%s/%s" % (self._loader.path_dwim(dest), target_name, source_local) dest = dest.replace("//", "/") if remote_checksum in ('0', '1', '2', '3', '4', '5'): result['changed'] = False result['file'] = source if remote_checksum == '0': result['msg'] = "unable to calculate the checksum of the remote file" elif remote_checksum == '1': result['msg'] = "the remote file does not exist" elif remote_checksum == '2': result['msg'] = "no read permission on remote file" elif remote_checksum == '3': result['msg'] = "remote file is a directory, fetch cannot work on directories" elif remote_checksum == '4': result['msg'] = "python isn't present on the system. Unable to compute checksum" elif remote_checksum == '5': result['msg'] = "stdlib json or simplejson was not found on the remote machine. Only the raw module can work without those installed" # Historically, these don't fail because you may want to transfer # a log file that possibly MAY exist but keep going to fetch other # log files. Today, this is better achieved by adding # ignore_errors or failed_when to the task. Control the behaviour # via fail_when_missing if fail_on_missing: result['failed'] = True del result['changed'] else: result['msg'] += ", not transferring, ignored" return result # calculate checksum for the local file local_checksum = checksum(dest) if remote_checksum != local_checksum: # create the containing directories, if needed makedirs_safe(os.path.dirname(dest)) # fetch the file and check for changes if remote_data is None: self._connection.fetch_file(source, dest) else: try: f = open(to_bytes(dest, errors='surrogate_or_strict'), 'wb') f.write(remote_data) f.close() except (IOError, OSError) as e: raise AnsibleError("Failed to fetch the file: %s" % e) new_checksum = secure_hash(dest) # For backwards compatibility. We'll return None on FIPS enabled systems try: new_md5 = md5(dest) except ValueError: new_md5 = None if validate_checksum and new_checksum != remote_checksum: result.update(dict(failed=True, md5sum=new_md5, msg="checksum mismatch", file=source, dest=dest, remote_md5sum=None, checksum=new_checksum, remote_checksum=remote_checksum)) else: result.update({'changed': True, 'md5sum': new_md5, 'dest': dest, 'remote_md5sum': None, 'checksum': new_checksum, 'remote_checksum': remote_checksum}) else: # For backwards compatibility. We'll return None on FIPS enabled systems try: local_md5 = md5(dest) except ValueError: local_md5 = None result.update(dict(changed=False, md5sum=local_md5, file=source, dest=dest, checksum=local_checksum)) finally: self._remove_tmp_path(self._connection._shell.tmpdir) return result
def run(self, tmp=None, task_vars=None): ''' handler for fetch operations ''' if task_vars is None: task_vars = dict() result = super(ActionModule, self).run(tmp, task_vars) if self._play_context.check_mode: result['skipped'] = True result['msg'] = 'check mode not (yet) supported for this module' return result source = self._task.args.get('src', None) dest = self._task.args.get('dest', None) flat = boolean(self._task.args.get('flat')) fail_on_missing = boolean(self._task.args.get('fail_on_missing')) validate_checksum = boolean( self._task.args.get('validate_checksum', self._task.args.get('validate_md5'))) if 'validate_md5' in self._task.args and 'validate_checksum' in self._task.args: result['failed'] = True result[ 'msg'] = "validate_checksum and validate_md5 cannot both be specified" return result if source is None or dest is None: result['failed'] = True result['msg'] = "src and dest are required" return result source = self._connection._shell.join_path(source) source = self._remote_expand_user(source) remote_checksum = None if not self._play_context.become: # calculate checksum for the remote file, don't bother if using become as slurp will be used # Force remote_checksum to follow symlinks because fetch always follows symlinks remote_checksum = self._remote_checksum(source, all_vars=task_vars, follow=True) # use slurp if permissions are lacking or privilege escalation is needed remote_data = None if remote_checksum in ('1', '2', None): slurpres = self._execute_module(module_name='slurp', module_args=dict(src=source), task_vars=task_vars, tmp=tmp) if slurpres.get('failed'): if not fail_on_missing and ( slurpres.get('msg').startswith('file not found') or remote_checksum == '1'): result[ 'msg'] = "the remote file does not exist, not transferring, ignored" result['file'] = source result['changed'] = False else: result.update(slurpres) return result else: if slurpres['encoding'] == 'base64': remote_data = base64.b64decode(slurpres['content']) if remote_data is not None: remote_checksum = checksum_s(remote_data) # the source path may have been expanded on the # target system, so we compare it here and use the # expanded version if it's different remote_source = slurpres.get('source') if remote_source and remote_source != source: source = remote_source # calculate the destination name if os.path.sep not in self._connection._shell.join_path('a', ''): source = self._connection._shell._unquote(source) source_local = source.replace('\\', '/') else: source_local = source dest = os.path.expanduser(dest) if flat: if dest.endswith(os.sep): # if the path ends with "/", we'll use the source filename as the # destination filename base = os.path.basename(source_local) dest = os.path.join(dest, base) if not dest.startswith("/"): # if dest does not start with "/", we'll assume a relative path dest = self._loader.path_dwim(dest) else: # files are saved in dest dir, with a subdir for each host, then the filename if 'inventory_hostname' in task_vars: target_name = task_vars['inventory_hostname'] else: target_name = self._play_context.remote_addr dest = "%s/%s/%s" % (self._loader.path_dwim(dest), target_name, source_local) dest = dest.replace("//", "/") if remote_checksum in ('0', '1', '2', '3', '4'): # these don't fail because you may want to transfer a log file that # possibly MAY exist but keep going to fetch other log files if remote_checksum == '0': result[ 'msg'] = "unable to calculate the checksum of the remote file" result['file'] = source result['changed'] = False elif remote_checksum == '1': if fail_on_missing: result['failed'] = True result['msg'] = "the remote file does not exist" result['file'] = source else: result[ 'msg'] = "the remote file does not exist, not transferring, ignored" result['file'] = source result['changed'] = False elif remote_checksum == '2': result[ 'msg'] = "no read permission on remote file, not transferring, ignored" result['file'] = source result['changed'] = False elif remote_checksum == '3': result[ 'msg'] = "remote file is a directory, fetch cannot work on directories" result['file'] = source result['changed'] = False elif remote_checksum == '4': result[ 'msg'] = "python isn't present on the system. Unable to compute checksum" result['file'] = source result['changed'] = False return result # calculate checksum for the local file local_checksum = checksum(dest) if remote_checksum != local_checksum: # create the containing directories, if needed makedirs_safe(os.path.dirname(dest)) # fetch the file and check for changes if remote_data is None: self._connection.fetch_file(source, dest) else: try: f = open(to_bytes(dest, errors='strict'), 'w') f.write(remote_data) f.close() except (IOError, OSError) as e: raise AnsibleError("Failed to fetch the file: %s" % e) new_checksum = secure_hash(dest) # For backwards compatibility. We'll return None on FIPS enabled systems try: new_md5 = md5(dest) except ValueError: new_md5 = None if validate_checksum and new_checksum != remote_checksum: result.update( dict(failed=True, md5sum=new_md5, msg="checksum mismatch", file=source, dest=dest, remote_md5sum=None, checksum=new_checksum, remote_checksum=remote_checksum)) else: result.update( dict(changed=True, md5sum=new_md5, dest=dest, remote_md5sum=None, checksum=new_checksum, remote_checksum=remote_checksum)) else: # For backwards compatibility. We'll return None on FIPS enabled systems try: local_md5 = md5(dest) except ValueError: local_md5 = None result.update( dict(changed=False, md5sum=local_md5, file=source, dest=dest, checksum=local_checksum)) return result
def run(self, tmp=None, task_vars=dict()): ''' handler for template operations ''' source = self._task.args.get('src', None) dest = self._task.args.get('dest', None) if (source is None and 'first_available_file' not in task_vars) or dest is None: return dict(failed=True, msg="src and dest are required") if tmp is None: tmp = self._make_tmp_path() ################################################################################################## # FIXME: this all needs to be sorted out ################################################################################################## # if we have first_available_file in our vars # look up the files and use the first one we find as src #if 'first_available_file' in task_vars: # found = False # for fn in task_vars.get('first_available_file'): # fn_orig = fn # fnt = template.template(self.runner.basedir, fn, task_vars) # fnd = utils.path_dwim(self.runner.basedir, fnt) # if not os.path.exists(fnd) and '_original_file' in task_vars: # fnd = utils.path_dwim_relative(task_vars['_original_file'], 'templates', fnt, self.runner.basedir, check=False) # if os.path.exists(fnd): # source = fnd # found = True # break # if not found: # result = dict(failed=True, msg="could not find src in first_available_file list") # return ReturnData(conn=conn, comm_ok=False, result=result) #else: if 1: if self._task._role is not None: source = self._loader.path_dwim_relative(self._task._role._role_path, 'templates', source) else: source = self._loader.path_dwim(source) ################################################################################################## # END FIXME ################################################################################################## # Expand any user home dir specification dest = self._remote_expand_user(dest, tmp) directory_prepended = False if dest.endswith(os.sep): directory_prepended = True base = os.path.basename(source) dest = os.path.join(dest, base) # template the source data locally & get ready to transfer templar = Templar(loader=self._loader, variables=task_vars) try: with open(source, 'r') as f: template_data = f.read() resultant = templar.template(template_data, preserve_trailing_newlines=True) except Exception as e: return dict(failed=True, msg=type(e).__name__ + ": " + str(e)) local_checksum = checksum_s(resultant) remote_checksum = self.get_checksum(tmp, dest, not directory_prepended, source=source) if isinstance(remote_checksum, dict): # Error from remote_checksum is a dict. Valid return is a str return remote_checksum if local_checksum != remote_checksum: # if showing diffs, we need to get the remote value dest_contents = '' # FIXME: still need to implement diff mechanism #if self.runner.diff: # # using persist_files to keep the temp directory around to avoid needing to grab another # dest_result = self.runner._execute_module(conn, tmp, 'slurp', "path=%s" % dest, task_vars=task_vars, persist_files=True) # if 'content' in dest_result.result: # dest_contents = dest_result.result['content'] # if dest_result.result['encoding'] == 'base64': # dest_contents = base64.b64decode(dest_contents) # else: # raise Exception("unknown encoding, failed: %s" % dest_result.result) xfered = self._transfer_data(self._shell.join_path(tmp, 'source'), resultant) # fix file permissions when the copy is done as a different user if self._connection_info.become and self._connection_info.become_user != 'root': self._remote_chmod('a+r', xfered, tmp) # run the copy module new_module_args = self._task.args.copy() new_module_args.update( dict( src=xfered, dest=dest, original_basename=os.path.basename(source), follow=True, ), ) # FIXME: noop stuff needs to be sorted out #if self.runner.noop_on_check(task_vars): # return ReturnData(conn=conn, comm_ok=True, result=dict(changed=True), diff=dict(before_header=dest, after_header=source, before=dest_contents, after=resultant)) #else: # res = self.runner._execute_module(conn, tmp, 'copy', module_args_tmp, task_vars=task_vars, complex_args=complex_args) # if res.result.get('changed', False): # res.diff = dict(before=dest_contents, after=resultant) # return res result = self._execute_module(module_name='copy', module_args=new_module_args) if result.get('changed', False): result['diff'] = dict(before=dest_contents, after=resultant) return result else: # when running the file module based on the template data, we do # not want the source filename (the name of the template) to be used, # since this would mess up links, so we clear the src param and tell # the module to follow links. When doing that, we have to set # original_basename to the template just in case the dest is # a directory. new_module_args = self._task.args.copy() new_module_args.update( dict( src=None, original_basename=os.path.basename(source), follow=True, ), ) # FIXME: this may not be required anymore, as the checkmod params # should be in the regular module args? # be sure to task_vars the check mode param into the module args and # rely on the file module to report its changed status #if self.runner.noop_on_check(task_vars): # new_module_args['CHECKMODE'] = True return self._execute_module(module_name='file', module_args=new_module_args)
def run(self, tmp=None, task_vars=dict()): """ handler for fetch operations """ if self._connection_info.check_mode: return dict(skipped=True, msg="check mode not (yet) supported for this module") source = self._task.args.get("src", None) dest = self._task.args.get("dest", None) flat = boolean(self._task.args.get("flat")) fail_on_missing = boolean(self._task.args.get("fail_on_missing")) validate_checksum = boolean(self._task.args.get("validate_checksum", self._task.args.get("validate_md5"))) if "validate_md5" in self._task.args and "validate_checksum" in self._task.args: return dict(failed=True, msg="validate_checksum and validate_md5 cannot both be specified") if source is None or dest is None: return dict(failed=True, msg="src and dest are required") source = self._connection._shell.join_path(source) source = self._remote_expand_user(source, tmp) # calculate checksum for the remote file remote_checksum = self._remote_checksum(tmp, source) # use slurp if sudo and permissions are lacking remote_data = None if remote_checksum in ("1", "2") or self._connection_info.become: slurpres = self._execute_module( module_name="slurp", module_args=dict(src=source), task_vars=task_vars, tmp=tmp ) if slurpres.get("rc") == 0: if slurpres["encoding"] == "base64": remote_data = base64.b64decode(slurpres["content"]) if remote_data is not None: remote_checksum = checksum_s(remote_data) # the source path may have been expanded on the # target system, so we compare it here and use the # expanded version if it's different remote_source = slurpres.get("source") if remote_source and remote_source != source: source = remote_source else: # FIXME: should raise an error here? the old code did nothing pass # calculate the destination name if os.path.sep not in self._connection._shell.join_path("a", ""): source_local = source.replace("\\", "/") else: source_local = source dest = os.path.expanduser(dest) if flat: if dest.endswith(os.sep): # if the path ends with "/", we'll use the source filename as the # destination filename base = os.path.basename(source_local) dest = os.path.join(dest, base) if not dest.startswith("/"): # if dest does not start with "/", we'll assume a relative path dest = self._loader.path_dwim(dest) else: # files are saved in dest dir, with a subdir for each host, then the filename if "inventory_hostname" in task_vars: target_name = task_vars["inventory_hostname"] else: target_name = self._connection_info.remote_addr dest = "%s/%s/%s" % (self._loader.path_dwim(dest), target_name, source_local) dest = dest.replace("//", "/") if remote_checksum in ("0", "1", "2", "3", "4"): # these don't fail because you may want to transfer a log file that possibly MAY exist # but keep going to fetch other log files if remote_checksum == "0": result = dict(msg="unable to calculate the checksum of the remote file", file=source, changed=False) elif remote_checksum == "1": if fail_on_missing: result = dict(failed=True, msg="the remote file does not exist", file=source) else: result = dict( msg="the remote file does not exist, not transferring, ignored", file=source, changed=False ) elif remote_checksum == "2": result = dict( msg="no read permission on remote file, not transferring, ignored", file=source, changed=False ) elif remote_checksum == "3": result = dict( msg="remote file is a directory, fetch cannot work on directories", file=source, changed=False ) elif remote_checksum == "4": result = dict( msg="python isn't present on the system. Unable to compute checksum", file=source, changed=False ) return result # calculate checksum for the local file local_checksum = checksum(dest) if remote_checksum != local_checksum: # create the containing directories, if needed makedirs_safe(os.path.dirname(dest)) # fetch the file and check for changes if remote_data is None: self._connection.fetch_file(source, dest) else: f = open(dest, "w") f.write(remote_data) f.close() new_checksum = secure_hash(dest) # For backwards compatibility. We'll return None on FIPS enabled # systems try: new_md5 = md5(dest) except ValueError: new_md5 = None if validate_checksum and new_checksum != remote_checksum: return dict( failed=True, md5sum=new_md5, msg="checksum mismatch", file=source, dest=dest, remote_md5sum=None, checksum=new_checksum, remote_checksum=remote_checksum, ) return dict( changed=True, md5sum=new_md5, dest=dest, remote_md5sum=None, checksum=new_checksum, remote_checksum=remote_checksum, ) else: # For backwards compatibility. We'll return None on FIPS enabled # systems try: local_md5 = md5(dest) except ValueError: local_md5 = None return dict(changed=False, md5sum=local_md5, file=source, dest=dest, checksum=local_checksum)
def run(self, tmp=None, task_vars=None): ''' handler for fetch operations ''' if task_vars is None: task_vars = dict() result = super(ActionModule, self).run(tmp, task_vars) if self._play_context.check_mode: result['skipped'] = True result['msg'] = 'check mode not (yet) supported for this module' return result source = self._task.args.get('src', None) dest = self._task.args.get('dest', None) flat = boolean(self._task.args.get('flat')) fail_on_missing = boolean(self._task.args.get('fail_on_missing')) validate_checksum = boolean(self._task.args.get('validate_checksum', self._task.args.get('validate_md5'))) if 'validate_md5' in self._task.args and 'validate_checksum' in self._task.args: result['failed'] = True result['msg'] = "validate_checksum and validate_md5 cannot both be specified" return result if source is None or dest is None: result['failed'] = True result['msg'] = "src and dest are required" return result source = self._connection._shell.join_path(source) source = self._remote_expand_user(source) remote_checksum = None if not self._play_context.become: # calculate checksum for the remote file, don't bother if using become as slurp will be used remote_checksum = self._remote_checksum(source, all_vars=task_vars) # use slurp if permissions are lacking or privilege escalation is needed remote_data = None if remote_checksum in ('1', '2', None): slurpres = self._execute_module(module_name='slurp', module_args=dict(src=source), task_vars=task_vars, tmp=tmp) if slurpres.get('failed'): if not fail_on_missing and (slurpres.get('msg').startswith('file not found') or remote_checksum == '1'): result['msg'] = "the remote file does not exist, not transferring, ignored" result['file'] = source result['changed'] = False else: result.update(slurpres) return result else: if slurpres['encoding'] == 'base64': remote_data = base64.b64decode(slurpres['content']) if remote_data is not None: remote_checksum = checksum_s(remote_data) # the source path may have been expanded on the # target system, so we compare it here and use the # expanded version if it's different remote_source = slurpres.get('source') if remote_source and remote_source != source: source = remote_source # calculate the destination name if os.path.sep not in self._connection._shell.join_path('a', ''): source = self._connection._shell._unquote(source) source_local = source.replace('\\', '/') else: source_local = source dest = os.path.expanduser(dest) if flat: if dest.endswith(os.sep): # if the path ends with "/", we'll use the source filename as the # destination filename base = os.path.basename(source_local) dest = os.path.join(dest, base) if not dest.startswith("/"): # if dest does not start with "/", we'll assume a relative path dest = self._loader.path_dwim(dest) else: # files are saved in dest dir, with a subdir for each host, then the filename if 'inventory_hostname' in task_vars: target_name = task_vars['inventory_hostname'] else: target_name = self._play_context.remote_addr dest = "%s/%s/%s" % (self._loader.path_dwim(dest), target_name, source_local) dest = dest.replace("//","/") if remote_checksum in ('0', '1', '2', '3', '4'): # these don't fail because you may want to transfer a log file that # possibly MAY exist but keep going to fetch other log files if remote_checksum == '0': result['msg'] = "unable to calculate the checksum of the remote file" result['file'] = source result['changed'] = False elif remote_checksum == '1': if fail_on_missing: result['failed'] = True result['msg'] = "the remote file does not exist" result['file'] = source else: result['msg'] = "the remote file does not exist, not transferring, ignored" result['file'] = source result['changed'] = False elif remote_checksum == '2': result['msg'] = "no read permission on remote file, not transferring, ignored" result['file'] = source result['changed'] = False elif remote_checksum == '3': result['msg'] = "remote file is a directory, fetch cannot work on directories" result['file'] = source result['changed'] = False elif remote_checksum == '4': result['msg'] = "python isn't present on the system. Unable to compute checksum" result['file'] = source result['changed'] = False return result # calculate checksum for the local file local_checksum = checksum(dest) if remote_checksum != local_checksum: # create the containing directories, if needed makedirs_safe(os.path.dirname(dest)) # fetch the file and check for changes if remote_data is None: self._connection.fetch_file(source, dest) else: try: f = open(to_bytes(dest, errors='strict'), 'w') f.write(remote_data) f.close() except (IOError, OSError) as e: raise AnsibleError("Failed to fetch the file: %s" % e) new_checksum = secure_hash(dest) # For backwards compatibility. We'll return None on FIPS enabled systems try: new_md5 = md5(dest) except ValueError: new_md5 = None if validate_checksum and new_checksum != remote_checksum: result.update(dict(failed=True, md5sum=new_md5, msg="checksum mismatch", file=source, dest=dest, remote_md5sum=None, checksum=new_checksum, remote_checksum=remote_checksum)) else: result.update(dict(changed=True, md5sum=new_md5, dest=dest, remote_md5sum=None, checksum=new_checksum, remote_checksum=remote_checksum)) else: # For backwards compatibility. We'll return None on FIPS enabled systems try: local_md5 = md5(dest) except ValueError: local_md5 = None result.update(dict(changed=False, md5sum=local_md5, file=source, dest=dest, checksum=local_checksum)) return result
def run(self, tmp=None, task_vars=dict()): ''' handler for fetch operations ''' # FIXME: is this even required anymore? #if self.runner.noop_on_check(inject): # return ReturnData(conn=conn, comm_ok=True, result=dict(skipped=True, msg='check mode not (yet) supported for this module')) source = self._task.args.get('src', None) dest = self._task.args.get('dest', None) flat = boolean(self._task.args.get('flat')) fail_on_missing = boolean(self._task.args.get('fail_on_missing')) validate_checksum = boolean( self._task.args.get('validate_checksum', self._task.args.get('validate_md5'))) if 'validate_md5' in self._task.args and 'validate_checksum' in self._task.args: return dict( failed=True, msg= "validate_checksum and validate_md5 cannot both be specified") if source is None or dest is None: return dict(failed=True, msg="src and dest are required") source = self._shell.join_path(source) source = self._remote_expand_user(source, tmp) # calculate checksum for the remote file remote_checksum = self._remote_checksum(tmp, source) # use slurp if sudo and permissions are lacking remote_data = None if remote_checksum in ('1', '2') or self._connection_info.become: slurpres = self._execute_module(module_name='slurp', module_args=dict(src=source), tmp=tmp) if slurpres.get('rc') == 0: if slurpres['encoding'] == 'base64': remote_data = base64.b64decode(slurpres['content']) if remote_data is not None: remote_checksum = checksum_s(remote_data) # the source path may have been expanded on the # target system, so we compare it here and use the # expanded version if it's different remote_source = slurpres.get('source') if remote_source and remote_source != source: source = remote_source else: # FIXME: should raise an error here? the old code did nothing pass # calculate the destination name if os.path.sep not in self._shell.join_path('a', ''): source_local = source.replace('\\', '/') else: source_local = source dest = os.path.expanduser(dest) if flat: if dest.endswith("/"): # if the path ends with "/", we'll use the source filename as the # destination filename base = os.path.basename(source_local) dest = os.path.join(dest, base) if not dest.startswith("/"): # if dest does not start with "/", we'll assume a relative path dest = self._loader.path_dwim(dest) else: # files are saved in dest dir, with a subdir for each host, then the filename dest = "%s/%s/%s" % (self._loader.path_dwim(dest), self._connection_info.remote_addr, source_local) dest = dest.replace("//", "/") if remote_checksum in ('0', '1', '2', '3', '4'): # these don't fail because you may want to transfer a log file that possibly MAY exist # but keep going to fetch other log files if remote_checksum == '0': result = dict( msg="unable to calculate the checksum of the remote file", file=source, changed=False) elif remote_checksum == '1': if fail_on_missing: result = dict(failed=True, msg="the remote file does not exist", file=source) else: result = dict( msg= "the remote file does not exist, not transferring, ignored", file=source, changed=False) elif remote_checksum == '2': result = dict( msg= "no read permission on remote file, not transferring, ignored", file=source, changed=False) elif remote_checksum == '3': result = dict( msg= "remote file is a directory, fetch cannot work on directories", file=source, changed=False) elif remote_checksum == '4': result = dict( msg= "python isn't present on the system. Unable to compute checksum", file=source, changed=False) return result # calculate checksum for the local file local_checksum = checksum(dest) if remote_checksum != local_checksum: # create the containing directories, if needed if not os.path.isdir(os.path.dirname(dest)): os.makedirs(os.path.dirname(dest)) # fetch the file and check for changes if remote_data is None: self._connection.fetch_file(source, dest) else: f = open(dest, 'w') f.write(remote_data) f.close() new_checksum = secure_hash(dest) # For backwards compatibility. We'll return None on FIPS enabled # systems try: new_md5 = md5(dest) except ValueError: new_md5 = None if validate_checksum and new_checksum != remote_checksum: return dict(failed=True, md5sum=new_md5, msg="checksum mismatch", file=source, dest=dest, remote_md5sum=None, checksum=new_checksum, remote_checksum=remote_checksum) return dict(changed=True, md5sum=new_md5, dest=dest, remote_md5sum=None, checksum=new_checksum, remote_checksum=remote_checksum) else: # For backwards compatibility. We'll return None on FIPS enabled # systems try: local_md5 = md5(dest) except ValueError: local_md5 = None return dict(changed=False, md5sum=local_md5, file=source, dest=dest, checksum=local_checksum)
def run(self, tmp=None, task_vars=None): ''' handler for fetch operations ''' if task_vars is None: task_vars = dict() result = super(ActionModule, self).run(tmp, task_vars) del tmp # tmp no longer has any effect try: if self._play_context.check_mode: raise AnsibleActionSkip( 'check mode not (yet) supported for this module') source = self._task.args.get('src', None) original_dest = dest = self._task.args.get('dest', None) flat = boolean(self._task.args.get('flat'), strict=False) fail_on_missing = boolean(self._task.args.get( 'fail_on_missing', True), strict=False) validate_checksum = boolean(self._task.args.get( 'validate_checksum', True), strict=False) msg = '' # validate source and dest are strings FIXME: use basic.py and module specs if not isinstance(source, string_types): msg = "Invalid type supplied for source option, it must be a string" if not isinstance(dest, string_types): msg = "Invalid type supplied for dest option, it must be a string" if source is None or dest is None: msg = "src and dest are required" if msg: raise AnsibleActionFail(msg) source = self._connection._shell.join_path(source) source = self._remote_expand_user(source) remote_checksum = None if not self._connection.become: # calculate checksum for the remote file, don't bother if using become as slurp will be used # Force remote_checksum to follow symlinks because fetch always follows symlinks remote_checksum = self._remote_checksum(source, all_vars=task_vars, follow=True) # use slurp if permissions are lacking or privilege escalation is needed remote_data = None if remote_checksum in ('1', '2', None): slurpres = self._execute_module( module_name='ansible.legacy.slurp', module_args=dict(src=source), task_vars=task_vars) if slurpres.get('failed'): if not fail_on_missing and ( slurpres.get('msg').startswith('file not found') or remote_checksum == '1'): result[ 'msg'] = "the remote file does not exist, not transferring, ignored" result['file'] = source result['changed'] = False else: result.update(slurpres) return result else: if slurpres['encoding'] == 'base64': remote_data = base64.b64decode(slurpres['content']) if remote_data is not None: remote_checksum = checksum_s(remote_data) # calculate the destination name if os.path.sep not in self._connection._shell.join_path('a', ''): source = self._connection._shell._unquote(source) source_local = source.replace('\\', '/') else: source_local = source # ensure we only use file name, avoid relative paths if not is_subpath(dest, original_dest): # TODO: ? dest = os.path.expanduser(dest.replace(('../',''))) raise AnsibleActionFail( "Detected directory traversal, expected to be contained in '%s' but got '%s'" % (original_dest, dest)) if flat: if os.path.isdir(to_bytes(dest, errors='surrogate_or_strict') ) and not dest.endswith(os.sep): raise AnsibleActionFail( "dest is an existing directory, use a trailing slash if you want to fetch src into that directory" ) if dest.endswith(os.sep): # if the path ends with "/", we'll use the source filename as the # destination filename base = os.path.basename(source_local) dest = os.path.join(dest, base) if not dest.startswith("/"): # if dest does not start with "/", we'll assume a relative path dest = self._loader.path_dwim(dest) else: # files are saved in dest dir, with a subdir for each host, then the filename if 'inventory_hostname' in task_vars: target_name = task_vars['inventory_hostname'] else: target_name = self._play_context.remote_addr dest = "%s/%s/%s" % (self._loader.path_dwim(dest), target_name, source_local) if remote_checksum in ('0', '1', '2', '3', '4', '5'): result['changed'] = False result['file'] = source if remote_checksum == '0': result[ 'msg'] = "unable to calculate the checksum of the remote file" elif remote_checksum == '1': result['msg'] = "the remote file does not exist" elif remote_checksum == '2': result['msg'] = "no read permission on remote file" elif remote_checksum == '3': result[ 'msg'] = "remote file is a directory, fetch cannot work on directories" elif remote_checksum == '4': result[ 'msg'] = "python isn't present on the system. Unable to compute checksum" elif remote_checksum == '5': result[ 'msg'] = "stdlib json was not found on the remote machine. Only the raw module can work without those installed" # Historically, these don't fail because you may want to transfer # a log file that possibly MAY exist but keep going to fetch other # log files. Today, this is better achieved by adding # ignore_errors or failed_when to the task. Control the behaviour # via fail_when_missing if fail_on_missing: result['failed'] = True del result['changed'] else: result['msg'] += ", not transferring, ignored" return result dest = os.path.normpath(dest) # calculate checksum for the local file local_checksum = checksum(dest) if remote_checksum != local_checksum: # create the containing directories, if needed makedirs_safe(os.path.dirname(dest)) # fetch the file and check for changes if remote_data is None: self._connection.fetch_file(source, dest) else: try: f = open(to_bytes(dest, errors='surrogate_or_strict'), 'wb') f.write(remote_data) f.close() except (IOError, OSError) as e: raise AnsibleActionFail( "Failed to fetch the file: %s" % e) new_checksum = secure_hash(dest) # For backwards compatibility. We'll return None on FIPS enabled systems try: new_md5 = md5(dest) except ValueError: new_md5 = None if validate_checksum and new_checksum != remote_checksum: result.update( dict(failed=True, md5sum=new_md5, msg="checksum mismatch", file=source, dest=dest, remote_md5sum=None, checksum=new_checksum, remote_checksum=remote_checksum)) else: result.update({ 'changed': True, 'md5sum': new_md5, 'dest': dest, 'remote_md5sum': None, 'checksum': new_checksum, 'remote_checksum': remote_checksum }) else: # For backwards compatibility. We'll return None on FIPS enabled systems try: local_md5 = md5(dest) except ValueError: local_md5 = None result.update( dict(changed=False, md5sum=local_md5, file=source, dest=dest, checksum=local_checksum)) finally: self._remove_tmp_path(self._connection._shell.tmpdir) return result
def run(self, tmp=None, task_vars=dict()): ''' handler for template operations ''' source = self._task.args.get('src', None) dest = self._task.args.get('dest', None) faf = task_vars.get('first_available_file', None) if (source is None and faf is not None) or dest is None: return dict(failed=True, msg="src and dest are required") if tmp is None: tmp = self._make_tmp_path() if faf: #FIXME: issue deprecation warning for first_available_file, use with_first_found or lookup('first_found',...) instead found = False for fn in faf: fn_orig = fn fnt = self._templar.template(fn) fnd = self._loader.path_dwim(self._task._role_._role_path, 'templates', fnt) if not os.path.exists(fnd): of = task_vars.get('_original_file', None) if of is not None: fnd = self._loader.path_dwim(self._task._role_._role_path, 'templates', of) if os.path.exists(fnd): source = fnd found = True break if not found: return dict(failed=True, msg="could not find src in first_available_file list") else: if self._task._role is not None: source = self._loader.path_dwim_relative(self._task._role._role_path, 'templates', source) else: source = self._loader.path_dwim(source) # Expand any user home dir specification dest = self._remote_expand_user(dest, tmp) directory_prepended = False if dest.endswith(os.sep): directory_prepended = True base = os.path.basename(source) dest = os.path.join(dest, base) # template the source data locally & get ready to transfer try: with open(source, 'r') as f: template_data = f.read() resultant = self._templar.template(template_data, preserve_trailing_newlines=True) except Exception as e: return dict(failed=True, msg=type(e).__name__ + ": " + str(e)) local_checksum = checksum_s(resultant) remote_checksum = self.get_checksum(tmp, dest, not directory_prepended, source=source) if isinstance(remote_checksum, dict): # Error from remote_checksum is a dict. Valid return is a str return remote_checksum if local_checksum != remote_checksum: # if showing diffs, we need to get the remote value dest_contents = '' # FIXME: still need to implement diff mechanism #if self.runner.diff: # # using persist_files to keep the temp directory around to avoid needing to grab another # dest_result = self.runner._execute_module(conn, tmp, 'slurp', "path=%s" % dest, task_vars=task_vars, persist_files=True) # if 'content' in dest_result.result: # dest_contents = dest_result.result['content'] # if dest_result.result['encoding'] == 'base64': # dest_contents = base64.b64decode(dest_contents) # else: # raise Exception("unknown encoding, failed: %s" % dest_result.result) xfered = self._transfer_data(self._connection._shell.join_path(tmp, 'source'), resultant) # fix file permissions when the copy is done as a different user if self._connection_info.become and self._connection_info.become_user != 'root': self._remote_chmod('a+r', xfered, tmp) # run the copy module new_module_args = self._task.args.copy() new_module_args.update( dict( src=xfered, dest=dest, original_basename=os.path.basename(source), follow=True, ), ) result = self._execute_module(module_name='copy', module_args=new_module_args, task_vars=task_vars) if result.get('changed', False): result['diff'] = dict(before=dest_contents, after=resultant) return result else: # when running the file module based on the template data, we do # not want the source filename (the name of the template) to be used, # since this would mess up links, so we clear the src param and tell # the module to follow links. When doing that, we have to set # original_basename to the template just in case the dest is # a directory. new_module_args = self._task.args.copy() new_module_args.update( dict( src=None, original_basename=os.path.basename(source), follow=True, ), ) return self._execute_module(module_name='file', module_args=new_module_args, task_vars=task_vars)
def run(self, tmp=None, task_vars=None): ''' handler for template operations ''' if task_vars is None: task_vars = dict() result = super(ActionModule, self).run(tmp, task_vars) source = self._task.args.get('src', None) dest = self._task.args.get('dest', None) force = boolean(self._task.args.get('force', True)) state = self._task.args.get('state', None) newline_sequence = self._task.args.get('newline_sequence', self.DEFAULT_NEWLINE_SEQUENCE) variable_start_string = self._task.args.get('variable_start_string', None) variable_end_string = self._task.args.get('variable_end_string', None) block_start_string = self._task.args.get('block_start_string', None) block_end_string = self._task.args.get('block_end_string', None) trim_blocks = self._task.args.get('trim_blocks', None) wrong_sequences = ["\\n", "\\r", "\\r\\n"] allowed_sequences = ["\n", "\r", "\r\n"] # We need to convert unescaped sequences to proper escaped sequences for Jinja2 if newline_sequence in wrong_sequences: newline_sequence = allowed_sequences[wrong_sequences.index( newline_sequence)] if state is not None: result['failed'] = True result['msg'] = "'state' cannot be specified on a template" elif source is None or dest is None: result['failed'] = True result['msg'] = "src and dest are required" elif newline_sequence not in allowed_sequences: result['failed'] = True result[ 'msg'] = "newline_sequence needs to be one of: \n, \r or \r\n" else: try: source = self._find_needle('templates', source) except AnsibleError as e: result['failed'] = True result['msg'] = to_text(e) if 'failed' in result: return result # Expand any user home dir specification dest = self._remote_expand_user(dest) directory_prepended = False if dest.endswith(os.sep): # Optimization. trailing slash means we know it's a directory directory_prepended = True dest = self._connection._shell.join_path(dest, os.path.basename(source)) else: # Find out if it's a directory dest_stat = self._execute_remote_stat(dest, task_vars, True, tmp=tmp) if dest_stat['exists'] and dest_stat['isdir']: dest = self._connection._shell.join_path( dest, os.path.basename(source)) # Get vault decrypted tmp file try: tmp_source = self._loader.get_real_file(source) except AnsibleFileNotFound as e: result['failed'] = True result['msg'] = "could not find src=%s, %s" % (source, e) self._remove_tmp_path(tmp) return result # template the source data locally & get ready to transfer try: with open(tmp_source, 'r') as f: template_data = to_text(f.read()) # set jinja2 internal search path for includes searchpath = task_vars.get('ansible_search_path', []) searchpath.extend([self._loader._basedir, os.path.dirname(source)]) # We want to search into the 'templates' subdir of each search path in # addition to our original search paths. newsearchpath = [] for p in searchpath: newsearchpath.append(os.path.join(p, 'templates')) newsearchpath.append(p) searchpath = newsearchpath self._templar.environment.loader.searchpath = searchpath self._templar.environment.newline_sequence = newline_sequence if block_start_string is not None: self._templar.environment.block_start_string = block_start_string if block_end_string is not None: self._templar.environment.block_end_string = block_end_string if variable_start_string is not None: self._templar.environment.variable_start_string = variable_start_string if variable_end_string is not None: self._templar.environment.variable_end_string = variable_end_string if trim_blocks is not None: self._templar.environment.trim_blocks = bool(trim_blocks) # add ansible 'template' vars temp_vars = task_vars.copy() temp_vars.update(generate_ansible_template_vars(source)) old_vars = self._templar._available_variables self._templar.set_available_variables(temp_vars) resultant = self._templar.do_template( template_data, preserve_trailing_newlines=True, escape_backslashes=False) self._templar.set_available_variables(old_vars) except Exception as e: result['failed'] = True result['msg'] = "%s: %s" % (type(e).__name__, to_text(e)) return result finally: self._loader.cleanup_tmp_file(tmp_source) if not tmp: tmp = self._make_tmp_path() local_checksum = checksum_s(resultant) remote_checksum = self.get_checksum(dest, task_vars, not directory_prepended, source=source, tmp=tmp) if isinstance(remote_checksum, dict): # Error from remote_checksum is a dict. Valid return is a str result.update(remote_checksum) return result diff = {} new_module_args = self._task.args.copy() # remove newline_sequence from standard arguments new_module_args.pop('newline_sequence', None) new_module_args.pop('block_start_string', None) new_module_args.pop('block_end_string', None) new_module_args.pop('variable_start_string', None) new_module_args.pop('variable_end_string', None) new_module_args.pop('trim_blocks', None) if (remote_checksum == '1') or (force and local_checksum != remote_checksum): result['changed'] = True # if showing diffs, we need to get the remote value if self._play_context.diff: diff = self._get_diff_data(dest, resultant, task_vars, source_file=False) if not self._play_context.check_mode: # do actual work through copy xfered = self._transfer_data( self._connection._shell.join_path(tmp, 'source'), resultant) # fix file permissions when the copy is done as a different user self._fixup_perms2((tmp, xfered)) # run the copy module new_module_args.update( dict( src=xfered, dest=dest, original_basename=os.path.basename(source), follow=True, ), ) result.update( self._execute_module(module_name='copy', module_args=new_module_args, task_vars=task_vars, tmp=tmp, delete_remote_tmp=False)) if result.get('changed', False) and self._play_context.diff: result['diff'] = diff else: # when running the file module based on the template data, we do # not want the source filename (the name of the template) to be used, # since this would mess up links, so we clear the src param and tell # the module to follow links. When doing that, we have to set # original_basename to the template just in case the dest is # a directory. new_module_args.update( dict( src=None, original_basename=os.path.basename(source), follow=True, ), ) result.update( self._execute_module(module_name='file', module_args=new_module_args, task_vars=task_vars, tmp=tmp, delete_remote_tmp=False)) self._remove_tmp_path(tmp) return result
def run(self, tmp=None, task_vars=dict()): ''' handler for template operations ''' source = self._task.args.get('src', None) dest = self._task.args.get('dest', None) faf = self._task.first_available_file if (source is None and faf is not None) or dest is None: return dict(failed=True, msg="src and dest are required") if tmp is None: tmp = self._make_tmp_path() if faf: source = self._get_first_available_file( faf, task_vars.get('_original_file', None, 'templates')) if source is None: return dict( failed=True, msg="could not find src in first_available_file list") else: if self._task._role is not None: source = self._loader.path_dwim_relative( self._task._role._role_path, 'templates', source) else: source = self._loader.path_dwim_relative( self._loader.get_basedir(), 'templates', source) # Expand any user home dir specification dest = self._remote_expand_user(dest) directory_prepended = False if dest.endswith(os.sep): directory_prepended = True base = os.path.basename(source) dest = os.path.join(dest, base) # template the source data locally & get ready to transfer try: with open(source, 'r') as f: template_data = to_unicode(f.read()) try: template_uid = pwd.getpwuid(os.stat(source).st_uid).pw_name except: template_uid = os.stat(source).st_uid temp_vars = task_vars.copy() temp_vars['template_host'] = os.uname()[1] temp_vars['template_path'] = source temp_vars['template_mtime'] = datetime.datetime.fromtimestamp( os.path.getmtime(source)) temp_vars['template_uid'] = template_uid temp_vars['template_fullpath'] = os.path.abspath(source) temp_vars['template_run_date'] = datetime.datetime.now() managed_default = C.DEFAULT_MANAGED_STR managed_str = managed_default.format( host=temp_vars['template_host'], uid=temp_vars['template_uid'], file=to_bytes(temp_vars['template_path'])) temp_vars['ansible_managed'] = time.strftime( managed_str, time.localtime(os.path.getmtime(source))) # Create a new searchpath list to assign to the templar environment's file # loader, so that it knows about the other paths to find template files searchpath = [self._loader._basedir, os.path.dirname(source)] if self._task._role is not None: searchpath.insert(1, C.DEFAULT_ROLES_PATH) searchpath.insert(1, self._task._role._role_path) self._templar.environment.loader.searchpath = searchpath old_vars = self._templar._available_variables self._templar.set_available_variables(temp_vars) resultant = self._templar.template(template_data, preserve_trailing_newlines=True, escape_backslashes=False, convert_data=False) self._templar.set_available_variables(old_vars) except Exception as e: return dict(failed=True, msg=type(e).__name__ + ": " + str(e)) local_checksum = checksum_s(resultant) remote_checksum = self.get_checksum(dest, task_vars, not directory_prepended, source=source) if isinstance(remote_checksum, dict): # Error from remote_checksum is a dict. Valid return is a str return remote_checksum diff = {} new_module_args = self._task.args.copy() if local_checksum != remote_checksum: dest_contents = '' # if showing diffs, we need to get the remote value if self._play_context.diff: diff = self._get_diff_data(dest, resultant, task_vars, source_file=False) if not self._play_context.check_mode: # do actual work thorugh copy xfered = self._transfer_data( self._connection._shell.join_path(tmp, 'source'), resultant) # fix file permissions when the copy is done as a different user if self._play_context.become and self._play_context.become_user != 'root': self._remote_chmod('a+r', xfered) # run the copy module new_module_args.update( dict( src=xfered, dest=dest, original_basename=os.path.basename(source), follow=True, ), ) result = self._execute_module(module_name='copy', module_args=new_module_args, task_vars=task_vars) else: result = dict(changed=True) if result.get('changed', False) and self._play_context.diff: result['diff'] = diff # result['diff'] = dict(before=dest_contents, after=resultant, before_header=dest, after_header=source) return result else: # when running the file module based on the template data, we do # not want the source filename (the name of the template) to be used, # since this would mess up links, so we clear the src param and tell # the module to follow links. When doing that, we have to set # original_basename to the template just in case the dest is # a directory. new_module_args.update( dict( src=None, original_basename=os.path.basename(source), follow=True, ), ) return self._execute_module(module_name='file', module_args=new_module_args, task_vars=task_vars)
def run(self, tmp=None, task_vars=None): ''' handler for template operations ''' if task_vars is None: task_vars = dict() res = self.create_aeriscloud_directory(tmp, task_vars) if 'failed' in res: return res result = super(ActionModule, self).run(tmp, task_vars) name = self._task.args.get('name', None) services = self._task.args.get('services', None) data = { 'services': [] } for service in services: if 'when' in service and not self._task.evaluate_conditional(service['when'], task_vars): continue if 'path' in service and 'protocol' not in service: service['protocol'] = 'http' if 'path' not in service: service['path'] = '' if 'protocol' not in service: service['protocol'] = 'tcp' data['services'].append(service) template = jinja2.Template("""{%- for service in services -%} {{ service['name'] }},{{ service['port'] }},{{ service['path'] }},{{ service['protocol'] }} {% endfor %}""") resultant = template.render(data) # Expand any user home dir specification dest = self._remote_expand_user(os.path.join('/etc/aeriscloud.d', name)) directory_prepended = True remote_user = task_vars.get('ansible_ssh_user') or self._play_context.remote_user if not tmp: tmp = self._make_tmp_path(remote_user) self._cleanup_remote_tmp = True local_checksum = checksum_s(resultant) remote_checksum = self.get_checksum(dest, task_vars, not directory_prepended, source=dest, tmp=tmp) if isinstance(remote_checksum, dict): # Error from remote_checksum is a dict. Valid return is a str result.update(remote_checksum) return result diff = {} new_module_args = { 'mode': 0644, 'owner': 'root', 'group': 'root' } if (remote_checksum == '1') or (local_checksum != remote_checksum): result['changed'] = True # if showing diffs, we need to get the remote value if self._play_context.diff: diff = self._get_diff_data(dest, resultant, task_vars, source_file=False) if not self._play_context.check_mode: # do actual work through copy xfered = self._transfer_data(self._connection._shell.join_path(tmp, 'source'), resultant) # fix file permissions when the copy is done as a different user self._fixup_perms(xfered, remote_user) # run the copy module new_module_args.update( dict( src=xfered, dest=dest, original_basename=os.path.basename(dest), follow=True, ), ) result.update(self._execute_module( module_name='copy', module_args=new_module_args, task_vars=task_vars, tmp=tmp, delete_remote_tmp=False)) if result.get('changed', False) and self._play_context.diff: result['diff'] = diff else: # when running the file module based on the template data, we do # not want the source filename (the name of the template) to be used, # since this would mess up links, so we clear the src param and tell # the module to follow links. When doing that, we have to set # original_basename to the template just in case the dest is # a directory. new_module_args.update( dict( path=dest, src=None, original_basename=os.path.basename(dest), follow=True, ), ) result.update(self._execute_module( module_name='file', module_args=new_module_args, task_vars=task_vars, tmp=tmp, delete_remote_tmp=False)) self._remove_tmp_path(tmp) return result
def run(self, tmp=None, task_vars=None): self._supports_check_mode = False result = super(ActionModule, self).run(tmp, task_vars) del tmp # tmp no longer has any effect if task_vars is None: task_vars = dict() src = self._task.args.get('src', None) dest = self._task.args.get('dest', None) delimiter = self._task.args.get('delimiter', None) remote_src = self._task.args.get('remote_src', 'yes') regexp = self._task.args.get('regexp', None) follow = self._task.args.get('follow', False) ignore_hidden = self._task.args.get('ignore_hidden', False) decrypt = self._task.args.get('decrypt', True) try: if src is None or dest is None: raise AnsibleActionFail("src and dest are required") if boolean(remote_src, strict=False): result.update( self._execute_module(module_name='assemble', task_vars=task_vars)) raise _AnsibleActionDone() else: try: src = self._find_needle('files', src) except AnsibleError as e: raise AnsibleActionFail(to_native(e)) if not os.path.isdir(src): raise AnsibleActionFail(u"Source (%s) is not a directory" % src) _re = None if regexp is not None: _re = re.compile(regexp) # Does all work assembling the file path = self._assemble_from_fragments(src, delimiter, _re, ignore_hidden, decrypt) path_checksum = checksum_s(path) dest = self._remote_expand_user(dest) dest_stat = self._execute_remote_stat(dest, all_vars=task_vars, follow=follow) diff = {} # setup args for running modules new_module_args = self._task.args.copy() # clean assemble specific options for opt in [ 'remote_src', 'regexp', 'delimiter', 'ignore_hidden', 'decrypt' ]: if opt in new_module_args: del new_module_args[opt] new_module_args['dest'] = dest if path_checksum != dest_stat['checksum']: if self._play_context.diff: diff = self._get_diff_data(dest, path, task_vars) remote_path = self._connection._shell.join_path( self._connection._shell.tmpdir, 'src') xfered = self._transfer_file(path, remote_path) # fix file permissions when the copy is done as a different user self._fixup_perms2( (self._connection._shell.tmpdir, remote_path)) new_module_args.update(dict(src=xfered, )) res = self._execute_module(module_name='copy', module_args=new_module_args, task_vars=task_vars) if diff: res['diff'] = diff result.update(res) else: result.update( self._execute_module(module_name='file', module_args=new_module_args, task_vars=task_vars)) except AnsibleAction as e: result.update(e.result) finally: self._remove_tmp_path(self._connection._shell.tmpdir) return result
def run(self, tmp=None, task_vars=dict()): src = self._task.args.get('src', None) dest = self._task.args.get('dest', None) delimiter = self._task.args.get('delimiter', None) remote_src = self._task.args.get('remote_src', 'yes') regexp = self._task.args.get('regexp', None) if src is None or dest is None: return dict(failed=True, msg="src and dest are required") if boolean(remote_src): return self._execute_module(tmp=tmp) elif self._task._role is not None: src = self._loader.path_dwim_relative(self._task._role._role_path, 'files', src) else: # the source is local, so expand it here src = self._loader.path_dwim(os.path.expanduser(src)) _re = None if regexp is not None: _re = re.compile(regexp) # Does all work assembling the file path = self._assemble_from_fragments(src, delimiter, _re) path_checksum = checksum_s(path) dest = self._remote_expand_user(dest, tmp) remote_checksum = self._remote_checksum(tmp, dest) if path_checksum != remote_checksum: resultant = file(path).read() # FIXME: diff needs to be moved somewhere else #if self.runner.diff: # dest_result = self._execute_module(module_name='slurp', module_args=dict(path=dest), tmp=tmp, persist_files=True) # if 'content' in dest_result: # dest_contents = dest_result['content'] # if dest_result['encoding'] == 'base64': # dest_contents = base64.b64decode(dest_contents) # else: # raise Exception("unknown encoding, failed: %s" % dest_result) xfered = self._transfer_data('src', resultant) # fix file permissions when the copy is done as a different user if self._connection_info.become and self._connection_info.become_user != 'root': self._remote_chmod('a+r', xfered, tmp) # run the copy module new_module_args = self._task.args.copy() new_module_args.update( dict( src=xfered, dest=dest, original_basename=os.path.basename(src), ) ) # FIXME: checkmode stuff #if self.runner.noop_on_check(inject): # return ReturnData(conn=conn, comm_ok=True, result=dict(changed=True), diff=dict(before_header=dest, after_header=src, after=resultant)) #else: # res = self.runner._execute_module(conn, tmp, 'copy', module_args_tmp, inject=inject) # res.diff = dict(after=resultant) # return res res = self._execute_module(module_name='copy', module_args=new_module_args, tmp=tmp) #res.diff = dict(after=resultant) return res else: new_module_args = self._task.args.copy() new_module_args.update( dict( src=xfered, dest=dest, original_basename=os.path.basename(src), ) ) return self._execute_module(module_name='file', module_args=new_module_args, tmp=tmp)
def run(self, tmp=None, task_vars=None): if task_vars is None: task_vars = dict() result = super(ActionModule, self).run(tmp, task_vars) if self._play_context.check_mode: result['skipped'] = True result['msg'] = "skipped, this module does not support check_mode." return result src = self._task.args.get('src', None) dest = self._task.args.get('dest', None) delimiter = self._task.args.get('delimiter', None) remote_src = self._task.args.get('remote_src', 'yes') regexp = self._task.args.get('regexp', None) follow = self._task.args.get('follow', False) ignore_hidden = self._task.args.get('ignore_hidden', False) if src is None or dest is None: result['failed'] = True result['msg'] = "src and dest are required" return result remote_user = task_vars.get('ansible_ssh_user') or self._play_context.remote_user if not tmp: tmp = self._make_tmp_path(remote_user) self._cleanup_remote_tmp = True if boolean(remote_src): result.update(self._execute_module(tmp=tmp, task_vars=task_vars, delete_remote_tmp=False)) self._remove_tmp_path(tmp) return result else: try: src = self._find_needle('files', src) except AnsibleError as e: result['failed'] = True result['msg'] = to_str(e) return result if not os.path.isdir(src): result['failed'] = True result['msg'] = "Source (%s) is not a directory" % src return result _re = None if regexp is not None: _re = re.compile(regexp) # Does all work assembling the file path = self._assemble_from_fragments(src, delimiter, _re, ignore_hidden) path_checksum = checksum_s(path) dest = self._remote_expand_user(dest) dest_stat = self._execute_remote_stat(dest, all_vars=task_vars, follow=follow, tmp=tmp) diff = {} # setup args for running modules new_module_args = self._task.args.copy() # clean assemble specific options for opt in ['remote_src', 'regexp', 'delimiter', 'ignore_hidden']: if opt in new_module_args: del new_module_args[opt] new_module_args.update( dict( dest=dest, original_basename=os.path.basename(src), ) ) if path_checksum != dest_stat['checksum']: if self._play_context.diff: diff = self._get_diff_data(dest, path, task_vars) remote_path = self._connection._shell.join_path(tmp, 'src') xfered = self._transfer_file(path, remote_path) # fix file permissions when the copy is done as a different user self._fixup_perms((tmp, remote_path), remote_user) new_module_args.update( dict( src=xfered,)) res = self._execute_module(module_name='copy', module_args=new_module_args, task_vars=task_vars, tmp=tmp, delete_remote_tmp=False) if diff: res['diff'] = diff result.update(res) else: result.update(self._execute_module(module_name='file', module_args=new_module_args, task_vars=task_vars, tmp=tmp, delete_remote_tmp=False)) self._remove_tmp_path(tmp) return result
def run(self, tmp=None, task_vars=None): ''' handler for template operations ''' if task_vars is None: task_vars = dict() result = super(ActionModule, self).run(tmp, task_vars) source = self._task.args.get('src', None) dest = self._task.args.get('dest', None) faf = self._task.first_available_file force = boolean(self._task.args.get('force', True)) state = self._task.args.get('state', None) if state is not None: result['failed'] = True result['msg'] = "'state' cannot be specified on a template" return result elif (source is None and faf is not None) or dest is None: result['failed'] = True result['msg'] = "src and dest are required" return result if faf: source = self._get_first_available_file(faf, task_vars.get('_original_file', None, 'templates')) if source is None: result['failed'] = True result['msg'] = "could not find src in first_available_file list" return result else: if self._task._role is not None: source = self._loader.path_dwim_relative(self._task._role._role_path, 'templates', source) else: source = self._loader.path_dwim_relative(self._loader.get_basedir(), 'templates', source) # Expand any user home dir specification dest = self._remote_expand_user(dest) directory_prepended = False if dest.endswith(os.sep): directory_prepended = True base = os.path.basename(source) dest = os.path.join(dest, base) # template the source data locally & get ready to transfer try: with open(source, 'r') as f: template_data = to_unicode(f.read()) temp_vars = task_vars.copy() if 'hostvars' in temp_vars['vars']: del temp_vars['vars']['hostvars'] # Create a new searchpath list to assign to the templar environment's file # loader, so that it knows about the other paths to find template files searchpath = [self._loader._basedir, os.path.dirname(source)] if self._task._role is not None: if C.DEFAULT_ROLES_PATH: searchpath[:0] = C.DEFAULT_ROLES_PATH searchpath.insert(1, self._task._role._role_path) resultant = _jsonnet.evaluate_snippet( source, template_data, tla_codes={'cfg': json.dumps(temp_vars['vars'])}, import_callback=import_callback(searchpath), ) except Exception as e: result['failed'] = True result['msg'] = type(e).__name__ + ": " + str(e) return result remote_user = task_vars.get('ansible_ssh_user') or self._play_context.remote_user if not tmp: tmp = self._make_tmp_path(remote_user) self._cleanup_remote_tmp = True local_checksum = checksum_s(resultant) remote_checksum = self.get_checksum(dest, task_vars, not directory_prepended, source=source, tmp=tmp) if isinstance(remote_checksum, dict): # Error from remote_checksum is a dict. Valid return is a str result.update(remote_checksum) return result diff = {} new_module_args = self._task.args.copy() if (remote_checksum == '1') or (force and local_checksum != remote_checksum): result['changed'] = True # if showing diffs, we need to get the remote value if self._play_context.diff: diff = self._get_diff_data(dest, resultant, task_vars, source_file=False) if not self._play_context.check_mode: # do actual work thorugh copy xfered = self._transfer_data(self._connection._shell.join_path(tmp, 'source'), resultant) # fix file permissions when the copy is done as a different user self._fixup_perms(tmp, remote_user, recursive=True) # run the copy module new_module_args.update( dict( src=xfered, dest=dest, original_basename=os.path.basename(source), follow=True, ), ) result.update(self._execute_module(module_name='copy', module_args=new_module_args, task_vars=task_vars, tmp=tmp, delete_remote_tmp=False)) if result.get('changed', False) and self._play_context.diff: result['diff'] = diff else: # when running the file module based on the template data, we do # not want the source filename (the name of the template) to be used, # since this would mess up links, so we clear the src param and tell # the module to follow links. When doing that, we have to set # original_basename to the template just in case the dest is # a directory. new_module_args.update( dict( src=None, original_basename=os.path.basename(source), follow=True, ), ) result.update(self._execute_module(module_name='file', module_args=new_module_args, task_vars=task_vars, tmp=tmp, delete_remote_tmp=False)) self._remove_tmp_path(tmp) return result
def run(self, tmp=None, task_vars=dict()): ''' handler for package operations ''' dest = self._task.args.get('dest', None) url = self._task.args.get('src', None) method = self._task.args.get('method', 'GET') headers = self._task.args.get('headers', dict()) uri_params = ['method', 'headers'] if tmp is None: tmp = self._make_tmp_path() source = tmp + '/temp.j2' new_module_args = self._task.args.copy() new_module_args.update( dict( url=url, dest=source, headers=headers, method=method ), ) result = self._execute_module(module_name='uri', module_args=new_module_args, task_vars=task_vars) directory_prepended = False if dest.endswith(os.sep): directory_prepended = True base = os.path.basename(source) dest = os.path.join(dest, base) # template the source data locally & get ready to transfer try: with open(source, 'r') as f: template_data = to_unicode(f.read()) try: template_uid = pwd.getpwuid(os.stat(source).st_uid).pw_name except: template_uid = os.stat(source).st_uid temp_vars = task_vars.copy() temp_vars['template_host'] = os.uname()[1] temp_vars['template_path'] = source temp_vars['template_mtime'] = datetime.datetime.fromtimestamp(os.path.getmtime(source)) temp_vars['template_uid'] = template_uid temp_vars['template_fullpath'] = os.path.abspath(source) temp_vars['template_run_date'] = datetime.datetime.now() managed_default = C.DEFAULT_MANAGED_STR managed_str = managed_default.format( host = temp_vars['template_host'], uid = temp_vars['template_uid'], file = to_bytes(temp_vars['template_path']) ) temp_vars['ansible_managed'] = time.strftime( managed_str, time.localtime(os.path.getmtime(source)) ) old_vars = self._templar._available_variables self._templar.set_available_variables(temp_vars) resultant = self._templar.template(template_data, preserve_trailing_newlines=True, convert_data=False) self._templar.set_available_variables(old_vars) except Exception as e: return dict(failed=True, msg=type(e).__name__ + ": " + str(e)) local_checksum = checksum_s(resultant) remote_checksum = self.get_checksum(tmp, dest, not directory_prepended, source=source) if isinstance(remote_checksum, dict): # Error from remote_checksum is a dict. Valid return is a str return remote_checksum if local_checksum != remote_checksum: # if showing diffs, we need to get the remote value dest_contents = '' xfered = self._transfer_data(self._connection._shell.join_path(tmp, 'source'), resultant) # fix file permissions when the copy is done as a different user if self._play_context.become and self._play_context.become_user != 'root': self._remote_chmod('a+r', xfered, tmp) # run the copy module new_module_args = self._task.args.copy() for param in uri_params: if param in new_module_args: del new_module_args[param] new_module_args.update( dict( src=xfered, dest=dest, original_basename=os.path.basename(source), follow=True, ), ) result = self._execute_module(module_name='copy', module_args=new_module_args, task_vars=task_vars) if result.get('changed', False): result['diff'] = dict(before=dest_contents, after=resultant) return result else: # when running the file module based on the template data, we do # not want the source filename (the name of the template) to be used, # since this would mess up links, so we clear the src param and tell # the module to follow links. When doing that, we have to set # original_basename to the template just in case the dest is # a directory. new_module_args = self._task.args.copy() for param in uri_params: if param in new_module_args: del new_module_args[param] new_module_args.update( dict( src=None, original_basename=os.path.basename(source), follow=True, ), ) return self._execute_module(module_name='file', module_args=new_module_args, task_vars=task_vars)
def run(self, tmp=None, task_vars=None): if task_vars is None: task_vars = dict() result = super(ActionModule, self).run(tmp, task_vars) if self._play_context.check_mode: result['skipped'] = True result['msg'] = "skipped, this module does not support check_mode." return result src = self._task.args.get('src', None) dest = self._task.args.get('dest', None) delimiter = self._task.args.get('delimiter', None) remote_src = self._task.args.get('remote_src', 'yes') regexp = self._task.args.get('regexp', None) follow = self._task.args.get('follow', False) ignore_hidden = self._task.args.get('ignore_hidden', False) if src is None or dest is None: result['failed'] = True result['msg'] = "src and dest are required" return result if boolean(remote_src): result.update(self._execute_module(tmp=tmp, task_vars=task_vars)) return result elif self._task._role is not None: src = self._loader.path_dwim_relative(self._task._role._role_path, 'files', src) else: src = self._loader.path_dwim_relative(self._loader.get_basedir(), 'files', src) _re = None if regexp is not None: _re = re.compile(regexp) if not os.path.isdir(src): result['failed'] = True result['msg'] = "Source (%s) is not a directory" % src return result # Does all work assembling the file path = self._assemble_from_fragments(src, delimiter, _re, ignore_hidden) path_checksum = checksum_s(path) dest = self._remote_expand_user(dest) dest_stat = self._execute_remote_stat(dest, all_vars=task_vars, follow=follow) diff = {} if path_checksum != dest_stat['checksum']: resultant = file(path).read() if self._play_context.diff: diff = self._get_diff_data(dest, path, task_vars) xfered = self._transfer_data('src', resultant) # fix file permissions when the copy is done as a different user if self._play_context.become and self._play_context.become_user != 'root': self._remote_chmod('a+r', xfered) # run the copy module new_module_args = self._task.args.copy() new_module_args.update( dict( src=xfered, dest=dest, original_basename=os.path.basename(src), ) ) res = self._execute_module(module_name='copy', module_args=new_module_args, task_vars=task_vars, tmp=tmp) if diff: res['diff'] = diff result.update(res) return result else: new_module_args = self._task.args.copy() new_module_args.update( dict( src=xfered, dest=dest, original_basename=os.path.basename(src), ) ) result.update(self._execute_module(module_name='file', module_args=new_module_args, task_vars=task_vars, tmp=tmp)) return result
def run(self, tmp=None, task_vars=dict()): ''' handler for fetch operations ''' # FIXME: is this even required anymore? #if self.runner.noop_on_check(inject): # return ReturnData(conn=conn, comm_ok=True, result=dict(skipped=True, msg='check mode not (yet) supported for this module')) source = self._task.args.get('src', None) dest = self._task.args.get('dest', None) flat = boolean(self._task.args.get('flat')) fail_on_missing = boolean(self._task.args.get('fail_on_missing')) validate_checksum = boolean(self._task.args.get('validate_checksum', self._task.args.get('validate_md5'))) if 'validate_md5' in self._task.args and 'validate_checksum' in self._task.args: return dict(failed=True, msg="validate_checksum and validate_md5 cannot both be specified") if source is None or dest is None: return dict(failed=True, msg="src and dest are required") source = self._shell.join_path(source) source = self._remote_expand_user(source, tmp) # calculate checksum for the remote file remote_checksum = self._remote_checksum(tmp, source) # use slurp if sudo and permissions are lacking remote_data = None if remote_checksum in ('1', '2') or self._connection_info.sudo: slurpres = self._execute_module(module_name='slurp', module_args=dict(src=source), tmp=tmp) if slurpres.get('rc') == 0: if slurpres['encoding'] == 'base64': remote_data = base64.b64decode(slurpres['content']) if remote_data is not None: remote_checksum = checksum_s(remote_data) # the source path may have been expanded on the # target system, so we compare it here and use the # expanded version if it's different remote_source = slurpres.get('source') if remote_source and remote_source != source: source = remote_source else: # FIXME: should raise an error here? the old code did nothing pass # calculate the destination name if os.path.sep not in self._shell.join_path('a', ''): source_local = source.replace('\\', '/') else: source_local = source dest = os.path.expanduser(dest) if flat: if dest.endswith("/"): # if the path ends with "/", we'll use the source filename as the # destination filename base = os.path.basename(source_local) dest = os.path.join(dest, base) if not dest.startswith("/"): # if dest does not start with "/", we'll assume a relative path dest = self._loader.path_dwim(dest) else: # files are saved in dest dir, with a subdir for each host, then the filename dest = "%s/%s/%s" % (self._loader.path_dwim(dest), self._connection_info.remote_addr, source_local) dest = dest.replace("//","/") if remote_checksum in ('0', '1', '2', '3', '4'): # these don't fail because you may want to transfer a log file that possibly MAY exist # but keep going to fetch other log files if remote_checksum == '0': result = dict(msg="unable to calculate the checksum of the remote file", file=source, changed=False) elif remote_checksum == '1': if fail_on_missing: result = dict(failed=True, msg="the remote file does not exist", file=source) else: result = dict(msg="the remote file does not exist, not transferring, ignored", file=source, changed=False) elif remote_checksum == '2': result = dict(msg="no read permission on remote file, not transferring, ignored", file=source, changed=False) elif remote_checksum == '3': result = dict(msg="remote file is a directory, fetch cannot work on directories", file=source, changed=False) elif remote_checksum == '4': result = dict(msg="python isn't present on the system. Unable to compute checksum", file=source, changed=False) return result # calculate checksum for the local file local_checksum = checksum(dest) if remote_checksum != local_checksum: # create the containing directories, if needed if not os.path.isdir(os.path.dirname(dest)): os.makedirs(os.path.dirname(dest)) # fetch the file and check for changes if remote_data is None: self._connection.fetch_file(source, dest) else: f = open(dest, 'w') f.write(remote_data) f.close() new_checksum = secure_hash(dest) # For backwards compatibility. We'll return None on FIPS enabled # systems try: new_md5 = md5(dest) except ValueError: new_md5 = None if validate_checksum and new_checksum != remote_checksum: return dict(failed=True, md5sum=new_md5, msg="checksum mismatch", file=source, dest=dest, remote_md5sum=None, checksum=new_checksum, remote_checksum=remote_checksum) return dict(changed=True, md5sum=new_md5, dest=dest, remote_md5sum=None, checksum=new_checksum, remote_checksum=remote_checksum) else: # For backwards compatibility. We'll return None on FIPS enabled # systems try: local_md5 = md5(dest) except ValueError: local_md5 = None return dict(changed=False, md5sum=local_md5, file=source, dest=dest, checksum=local_checksum)
def run(self, tmp=None, task_vars=None): ''' handler for template operations ''' if task_vars is None: task_vars = dict() result = super(ActionModule, self).run(tmp, task_vars) source = self._task.args.get('src', None) dest = self._task.args.get('dest', None) force = boolean(self._task.args.get('force', True)) state = self._task.args.get('state', None) if state is not None: result['failed'] = True result['msg'] = "'state' cannot be specified on a template" elif source is None or dest is None: result['failed'] = True result['msg'] = "src and dest are required" else: try: source = self._find_needle('templates', source) except AnsibleError as e: result['failed'] = True result['msg'] = to_native(e) if 'failed' in result: return result # Expand any user home dir specification dest = self._remote_expand_user(dest) directory_prepended = False if dest.endswith(os.sep): directory_prepended = True base = os.path.basename(source) dest = os.path.join(dest, base) # template the source data locally & get ready to transfer b_source = to_bytes(source) try: with open(b_source, 'r') as f: template_data = to_text(f.read()) try: template_uid = pwd.getpwuid(os.stat(b_source).st_uid).pw_name except: template_uid = os.stat(b_source).st_uid temp_vars = task_vars.copy() temp_vars['template_host'] = os.uname()[1] temp_vars['template_path'] = source temp_vars['template_mtime'] = datetime.datetime.fromtimestamp(os.path.getmtime(b_source)) temp_vars['template_uid'] = template_uid temp_vars['template_fullpath'] = os.path.abspath(source) temp_vars['template_run_date'] = datetime.datetime.now() managed_default = C.DEFAULT_MANAGED_STR managed_str = managed_default.format( host = temp_vars['template_host'], uid = temp_vars['template_uid'], file = to_bytes(temp_vars['template_path']) ) temp_vars['ansible_managed'] = time.strftime( managed_str, time.localtime(os.path.getmtime(b_source)) ) # Create a new searchpath list to assign to the templar environment's file # loader, so that it knows about the other paths to find template files searchpath = [self._loader._basedir, os.path.dirname(source)] if self._task._role is not None: if C.DEFAULT_ROLES_PATH: searchpath[:0] = C.DEFAULT_ROLES_PATH searchpath.insert(1, self._task._role._role_path) self._templar.environment.loader.searchpath = searchpath old_vars = self._templar._available_variables self._templar.set_available_variables(temp_vars) resultant = self._templar.template(template_data, preserve_trailing_newlines=True, escape_backslashes=False, convert_data=False) self._templar.set_available_variables(old_vars) except Exception as e: result['failed'] = True result['msg'] = type(e).__name__ + ": " + str(e) return result remote_user = task_vars.get('ansible_ssh_user') or self._play_context.remote_user if not tmp: tmp = self._make_tmp_path(remote_user) self._cleanup_remote_tmp = True local_checksum = checksum_s(resultant) remote_checksum = self.get_checksum(dest, task_vars, not directory_prepended, source=source, tmp=tmp) if isinstance(remote_checksum, dict): # Error from remote_checksum is a dict. Valid return is a str result.update(remote_checksum) return result diff = {} new_module_args = self._task.args.copy() if (remote_checksum == '1') or (force and local_checksum != remote_checksum): result['changed'] = True # if showing diffs, we need to get the remote value if self._play_context.diff: diff = self._get_diff_data(dest, resultant, task_vars, source_file=False) if not self._play_context.check_mode: # do actual work through copy xfered = self._transfer_data(self._connection._shell.join_path(tmp, 'source'), resultant) # fix file permissions when the copy is done as a different user self._fixup_perms2((tmp, xfered), remote_user) # run the copy module new_module_args.update( dict( src=xfered, dest=dest, original_basename=os.path.basename(source), follow=True, ), ) result.update(self._execute_module(module_name='copy', module_args=new_module_args, task_vars=task_vars, tmp=tmp, delete_remote_tmp=False)) if result.get('changed', False) and self._play_context.diff: result['diff'] = diff else: # when running the file module based on the template data, we do # not want the source filename (the name of the template) to be used, # since this would mess up links, so we clear the src param and tell # the module to follow links. When doing that, we have to set # original_basename to the template just in case the dest is # a directory. new_module_args.update( dict( src=None, original_basename=os.path.basename(source), follow=True, ), ) result.update(self._execute_module(module_name='file', module_args=new_module_args, task_vars=task_vars, tmp=tmp, delete_remote_tmp=False)) self._remove_tmp_path(tmp) return result
def run(self, tmp=None, task_vars=dict()): ''' handler for template operations ''' source = self._task.args.get('src', None) dest = self._task.args.get('dest', None) faf = self._task.first_available_file if (source is None and faf is not None) or dest is None: return dict(failed=True, msg="src and dest are required") if tmp is None: tmp = self._make_tmp_path() if faf: #FIXME: issue deprecation warning for first_available_file, use with_first_found or lookup('first_found',...) instead found = False for fn in faf: fn_orig = fn fnt = self._templar.template(fn) fnd = self._loader.path_dwim(self._task._role_._role_path, 'templates', fnt) if not os.path.exists(fnd): of = task_vars.get('_original_file', None) if of is not None: fnd = self._loader.path_dwim( self._task._role_._role_path, 'templates', of) if os.path.exists(fnd): source = fnd found = True break if not found: return dict( failed=True, msg="could not find src in first_available_file list") else: if self._task._role is not None: source = self._loader.path_dwim_relative( self._task._role._role_path, 'templates', source) else: source = self._loader.path_dwim(source) # Expand any user home dir specification dest = self._remote_expand_user(dest, tmp) directory_prepended = False if dest.endswith(os.sep): directory_prepended = True base = os.path.basename(source) dest = os.path.join(dest, base) # template the source data locally & get ready to transfer try: with open(source, 'r') as f: template_data = f.read() try: template_uid = pwd.getpwuid(os.stat(source).st_uid).pw_name except: template_uid = os.stat(source).st_uid vars = task_vars.copy() vars['template_host'] = os.uname()[1] vars['template_path'] = source vars['template_mtime'] = datetime.datetime.fromtimestamp( os.path.getmtime(source)) vars['template_uid'] = template_uid vars['template_fullpath'] = os.path.abspath(source) vars['template_run_date'] = datetime.datetime.now() managed_default = C.DEFAULT_MANAGED_STR managed_str = managed_default.format(host=vars['template_host'], uid=vars['template_uid'], file=to_bytes( vars['template_path'])) vars['ansible_managed'] = time.strftime( managed_str, time.localtime(os.path.getmtime(source))) old_vars = self._templar._available_variables self._templar.set_available_variables(vars) resultant = self._templar.template(template_data, preserve_trailing_newlines=True) self._templar.set_available_variables(old_vars) except Exception as e: return dict(failed=True, msg=type(e).__name__ + ": " + str(e)) local_checksum = checksum_s(resultant) remote_checksum = self.get_checksum(tmp, dest, not directory_prepended, source=source) if isinstance(remote_checksum, dict): # Error from remote_checksum is a dict. Valid return is a str return remote_checksum if local_checksum != remote_checksum: # if showing diffs, we need to get the remote value dest_contents = '' # FIXME: still need to implement diff mechanism #if self.runner.diff: # # using persist_files to keep the temp directory around to avoid needing to grab another # dest_result = self.runner._execute_module(conn, tmp, 'slurp', "path=%s" % dest, task_vars=task_vars, persist_files=True) # if 'content' in dest_result.result: # dest_contents = dest_result.result['content'] # if dest_result.result['encoding'] == 'base64': # dest_contents = base64.b64decode(dest_contents) # else: # raise Exception("unknown encoding, failed: %s" % dest_result.result) xfered = self._transfer_data( self._connection._shell.join_path(tmp, 'source'), resultant) # fix file permissions when the copy is done as a different user if self._connection_info.become and self._connection_info.become_user != 'root': self._remote_chmod('a+r', xfered, tmp) # run the copy module new_module_args = self._task.args.copy() new_module_args.update( dict( src=xfered, dest=dest, original_basename=os.path.basename(source), follow=True, ), ) result = self._execute_module(module_name='copy', module_args=new_module_args, task_vars=task_vars) if result.get('changed', False): result['diff'] = dict(before=dest_contents, after=resultant) return result else: # when running the file module based on the template data, we do # not want the source filename (the name of the template) to be used, # since this would mess up links, so we clear the src param and tell # the module to follow links. When doing that, we have to set # original_basename to the template just in case the dest is # a directory. new_module_args = self._task.args.copy() new_module_args.update( dict( src=None, original_basename=os.path.basename(source), follow=True, ), ) return self._execute_module(module_name='file', module_args=new_module_args, task_vars=task_vars)