# (c) 2013-2014, Michael DeHaan # Stephen Fromm # Brian Coca # # This file is part of Ansible # # Ansible is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Ansible is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License import os import os.path import pipes import shutil import tempfile import base64 import re from ansible.plugins.action import ActionBase from ansible.utils.boolean import boolean from ansible.utils.hashing import checksum_s class ActionModule(ActionBase): TRANSFERS_FILES = True def _assemble_from_fragments(self, src_path, delimiter=None, compiled_regexp=None): ''' assemble a file from a directory of fragments ''' tmpfd, temp_path = tempfile.mkstemp() tmp = os.fdopen(tmpfd,'w') delimit_me = False add_newline = False for f in sorted(os.listdir(src_path)): if compiled_regexp and not compiled_regexp.search(f): continue fragment = "%s/%s" % (src_path, f) if not os.path.isfile(fragment): continue fragment_content = file(fragment).read() # always put a newline between fragments if the previous fragment didn't end with a newline. if add_newline: tmp.write('\n') # delimiters should only appear between fragments if delimit_me: if delimiter: # un-escape anything like newlines delimiter = delimiter.decode('unicode-escape') tmp.write(delimiter) # always make sure there's a newline after the # delimiter, so lines don't run together if delimiter[-1] != '\n': tmp.write('\n') tmp.write(fragment_content) delimit_me = True if fragment_content.endswith('\n'): add_newline = False else: add_newline = True tmp.close() return temp_path 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 = 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.sudo and self._connection_info.sudo_user != 'root' or self._connection_info.su and self._connection_info.su_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)