def update_timestamp_for_file(path, mtime, atime, diff=None): b_path = to_bytes(path, errors='surrogate_or_strict') try: # When mtime and atime are set to 'now', rely on utime(path, None) which does not require ownership of the file # https://github.com/ansible/ansible/issues/50943 if mtime is Sentinel and atime is Sentinel: # It's not exact but we can't rely on os.stat(path).st_mtime after setting os.utime(path, None) as it may # not be updated. Just use the current time for the diff values mtime = atime = time.time() previous_mtime = os.stat(b_path).st_mtime previous_atime = os.stat(b_path).st_atime set_time = None else: # If both parameters are None 'preserve', nothing to do if mtime is None and atime is None: return False previous_mtime = os.stat(b_path).st_mtime previous_atime = os.stat(b_path).st_atime if mtime is None: mtime = previous_mtime elif mtime is Sentinel: mtime = time.time() if atime is None: atime = previous_atime elif atime is Sentinel: atime = time.time() # If both timestamps are already ok, nothing to do if mtime == previous_mtime and atime == previous_atime: return False set_time = (atime, mtime) os.utime(b_path, set_time) if diff is not None: if 'before' not in diff: diff['before'] = {} if 'after' not in diff: diff['after'] = {} if mtime != previous_mtime: diff['before']['mtime'] = previous_mtime diff['after']['mtime'] = mtime if atime != previous_atime: diff['before']['atime'] = previous_atime diff['after']['atime'] = atime except OSError as e: raise AnsibleModuleError( results={ 'msg': 'Error while updating modification or access time: %s' % to_native(e, nonstring='simplerepr'), 'path': path }) return True
def _parse_pipeline_result(self, pipeline): """ PSRP doesn't have the same concept as other protocols with its output. We need some extra logic to convert the pipeline streams and host output into the format that Ansible understands. :param pipeline: The finished PowerShell pipeline that invoked our commands :return: rc, stdout, stderr based on the pipeline output """ # we try and get the rc from our host implementation, this is set if # exit or $host.SetShouldExit() is called in our pipeline, if not we # set to 0 if the pipeline had not errors and 1 if it did rc = self.host.rc or (1 if pipeline.had_errors else 0) # TODO: figure out a better way of merging this with the host output stdout_list = [] for output in pipeline.output: # Not all pipeline outputs are a string or contain a __str__ value, # we will create our own output based on the properties of the # complex object if that is the case. if isinstance(output, GenericComplexObject) and output.to_string is None: obj_lines = output.property_sets for key, value in output.adapted_properties.items(): obj_lines.append(u"%s: %s" % (key, value)) for key, value in output.extended_properties.items(): obj_lines.append(u"%s: %s" % (key, value)) output_msg = u"\n".join(obj_lines) else: output_msg = to_text(output, nonstring='simplerepr') stdout_list.append(output_msg) if len(self.host.ui.stdout) > 0: stdout_list += self.host.ui.stdout stdout = u"\r\n".join(stdout_list) stderr_list = [] for error in pipeline.streams.error: # the error record is not as fully fleshed out like we usually get # in PS, we will manually create it here command_name = "%s : " % error.command_name if error.command_name else '' position = "%s\r\n" % error.invocation_position_message if error.invocation_position_message else '' error_msg = "%s%s\r\n%s" \ " + CategoryInfo : %s\r\n" \ " + FullyQualifiedErrorId : %s" \ % (command_name, str(error), position, error.message, error.fq_error) stacktrace = error.script_stacktrace if self._play_context.verbosity >= 3 and stacktrace is not None: error_msg += "\r\nStackTrace:\r\n%s" % stacktrace stderr_list.append(error_msg) if len(self.host.ui.stderr) > 0: stderr_list += self.host.ui.stderr stderr = u"\r\n".join([to_text(o) for o in stderr_list]) display.vvvvv("PSRP RC: %d" % rc, host=self._psrp_host) display.vvvvv("PSRP STDOUT: %s" % stdout, host=self._psrp_host) display.vvvvv("PSRP STDERR: %s" % stderr, host=self._psrp_host) # reset the host back output back to defaults, needed if running # multiple pipelines on the same RunspacePool self.host.rc = 0 self.host.ui.stdout = [] self.host.ui.stderr = [] return rc, to_bytes(stdout, encoding='utf-8'), to_bytes(stderr, encoding='utf-8')
def set_module_args(args): """prepare arguments so that they will be picked up during module creation""" args = json.dumps({'ANSIBLE_MODULE_ARGS': args}) basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
def _get_file_contents(self, path): path = to_text(path) if path in self._file_mapping: return (to_bytes(self._file_mapping[path]), False) else: raise AnsibleParserError("file not found: %s" % path)
def put_file(self, in_path, out_path): super(Connection, self).put_file(in_path, out_path) display.vvv("PUT %s TO %s" % (in_path, out_path), host=self._psrp_host) out_path = self._shell._unquote(out_path) script = u'''begin { $ErrorActionPreference = "Stop" $path = '%s' $fd = [System.IO.File]::Create($path) $algo = [System.Security.Cryptography.SHA1CryptoServiceProvider]::Create() $bytes = @() } process { $bytes = [System.Convert]::FromBase64String($input) $algo.TransformBlock($bytes, 0, $bytes.Length, $bytes, 0) > $null $fd.Write($bytes, 0, $bytes.Length) } end { $fd.Close() $algo.TransformFinalBlock($bytes, 0, 0) > $null $hash = [System.BitConverter]::ToString($algo.Hash) $hash = $hash.Replace("-", "").ToLowerInvariant() Write-Output -InputObject "{`"sha1`":`"$hash`"}" }''' % self._shell._escape(out_path) cmd_parts = self._shell._encode_script(script, as_list=True, strict_mode=False, preserve_rc=False) b_in_path = to_bytes(in_path, errors='surrogate_or_strict') if not os.path.exists(b_in_path): raise AnsibleFileNotFound('file or module does not exist: "%s"' % to_native(in_path)) in_size = os.path.getsize(b_in_path) buffer_size = int(self.runspace.connection.max_payload_size / 4 * 3) # copying files is faster when using the raw WinRM shell and not PSRP # we will create a WinRS shell just for this process # TODO: speed this up as there is overhead creating a shell for this with WinRS(self.runspace.connection, codepage=65001) as shell: process = Process(shell, cmd_parts[0], cmd_parts[1:]) process.begin_invoke() offset = 0 with open(b_in_path, 'rb') as src_file: for data in iter((lambda: src_file.read(buffer_size)), b""): offset += len(data) display.vvvvv("PSRP PUT %s to %s (offset=%d, size=%d" % (in_path, out_path, offset, len(data)), host=self._psrp_host) b64_data = base64.b64encode(data) + b"\r\n" process.send(b64_data, end=(src_file.tell() == in_size)) # the file was empty, return empty buffer if offset == 0: process.send(b"", end=True) process.end_invoke() process.signal(SignalCode.CTRL_C) if process.rc != 0: raise AnsibleError(to_native(process.stderr)) put_output = json.loads(process.stdout) remote_sha1 = put_output.get("sha1") if not remote_sha1: raise AnsibleError("Remote sha1 was not returned, stdout: '%s', " "stderr: '%s'" % (to_native(process.stdout), to_native(process.stderr))) local_sha1 = secure_hash(in_path) if not remote_sha1 == local_sha1: raise AnsibleError("Remote sha1 hash %s does not match local hash " "%s" % (to_native(remote_sha1), to_native(local_sha1)))
def get_user_data(self): user_data = self.module.params.get('user_data') if user_data is not None: user_data = to_text(base64.b64encode(to_bytes(user_data))) return user_data
def edit_config(self, command): for cmd in chain(['configure terminal'], to_list(command), ['end']): self.send_command(to_bytes(cmd))
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) del tmp # tmp no longer has any effect # Options type validation # stings for s_type in ('src', 'dest', 'state', 'newline_sequence', 'variable_start_string', 'variable_end_string', 'block_start_string', 'block_end_string'): if s_type in self._task.args: value = ensure_type(self._task.args[s_type], 'string') if value is not None and not isinstance(value, string_types): raise AnsibleActionFail( "%s is expected to be a string, but got %s instead" % (s_type, type(value))) self._task.args[s_type] = value # booleans try: follow = boolean(self._task.args.get('follow', False), strict=False) trim_blocks = boolean(self._task.args.get('trim_blocks', True), strict=False) lstrip_blocks = boolean(self._task.args.get( 'lstrip_blocks', False), strict=False) except TypeError as e: raise AnsibleActionFail(to_native(e)) # assign to local vars for ease of use source = self._task.args.get('src', None) dest = self._task.args.get('dest', None) 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) output_encoding = self._task.args.get('output_encoding', 'utf-8') or 'utf-8' # Option `lstrip_blocks' was added in Jinja2 version 2.7. if lstrip_blocks: try: import jinja2.defaults except ImportError: raise AnsibleError( 'Unable to import Jinja2 defaults for determining Jinja2 features.' ) try: jinja2.defaults.LSTRIP_BLOCKS except AttributeError: raise AnsibleError( "Option `lstrip_blocks' is only available in Jinja2 versions >=2.7" ) 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)] try: # logical validation if state is not None: raise AnsibleActionFail( "'state' cannot be specified on a template") elif source is None or dest is None: raise AnsibleActionFail("src and dest are required") elif newline_sequence not in allowed_sequences: raise AnsibleActionFail( "newline_sequence needs to be one of: \n, \r or \r\n") else: try: source = self._find_needle('templates', source) except AnsibleError as e: raise AnsibleActionFail(to_text(e)) mode = self._task.args.get('mode', None) if mode == 'preserve': mode = '0%03o' % stat.S_IMODE(os.stat(source).st_mode) # Get vault decrypted tmp file try: tmp_source = self._loader.get_real_file(source) except AnsibleFileNotFound as e: raise AnsibleActionFail("could not find src=%s, %s" % (source, to_text(e))) b_tmp_source = to_bytes(tmp_source, errors='surrogate_or_strict') # template the source data locally & get ready to transfer try: with open(b_tmp_source, 'rb') as f: try: template_data = to_text(f.read(), errors='surrogate_or_strict') except UnicodeError: raise AnsibleActionFail( "Template source files must be utf-8 encoded") # 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 # add ansible 'template' vars temp_vars = task_vars.copy() temp_vars.update(generate_ansible_template_vars(source, dest)) with self._templar.set_temporary_context( searchpath=searchpath, newline_sequence=newline_sequence, block_start_string=block_start_string, block_end_string=block_end_string, variable_start_string=variable_start_string, variable_end_string=variable_end_string, trim_blocks=trim_blocks, lstrip_blocks=lstrip_blocks, available_variables=temp_vars): resultant = self._templar.do_template( template_data, preserve_trailing_newlines=True, escape_backslashes=False) except AnsibleAction: raise except Exception as e: raise AnsibleActionFail("%s: %s" % (type(e).__name__, to_text(e))) finally: self._loader.cleanup_tmp_file(b_tmp_source) new_task = self._task.copy() # mode is either the mode from task.args or the mode of the source file if the task.args # mode == 'preserve' new_task.args['mode'] = mode # remove 'template only' options: for remove in ('newline_sequence', 'block_start_string', 'block_end_string', 'variable_start_string', 'variable_end_string', 'trim_blocks', 'lstrip_blocks', 'output_encoding'): new_task.args.pop(remove, None) local_tempdir = tempfile.mkdtemp(dir=C.DEFAULT_LOCAL_TMP) try: result_file = os.path.join(local_tempdir, os.path.basename(source)) with open(to_bytes(result_file, errors='surrogate_or_strict'), 'wb') as f: f.write( to_bytes(resultant, encoding=output_encoding, errors='surrogate_or_strict')) new_task.args.update( dict( src=result_file, dest=dest, follow=follow, ), ) copy_action = self._shared_loader_obj.action_loader.get( 'copy', task=new_task, connection=self._connection, play_context=self._play_context, loader=self._loader, templar=self._templar, shared_loader_obj=self._shared_loader_obj) result.update(copy_action.run(task_vars=task_vars)) finally: shutil.rmtree( to_bytes(local_tempdir, errors='surrogate_or_strict')) 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=None): socket_path = None network_os = self._get_network_os(task_vars).split('.')[-1] persistent_connection = self._play_context.connection.split('.')[-1] result = super(ActionModule, self).run(task_vars=task_vars) if persistent_connection != 'network_cli': # It is supported only with network_cli result['failed'] = True result['msg'] = ( 'connection type %s is not valid for net_put module,' ' please use fully qualified name of network_cli connection type' % self._play_context.connection) return result try: src = self._task.args['src'] except KeyError as exc: return { 'failed': True, 'msg': 'missing required argument: %s' % exc } src_file_path_name = src # Get destination file if specified dest = self._task.args.get('dest') # Get proto proto = self._task.args.get('protocol') if proto is None: proto = 'scp' # Get mode if set mode = self._task.args.get('mode') if mode is None: mode = 'binary' if mode == 'text': try: self._handle_template(convert_data=False) except ValueError as exc: return dict(failed=True, msg=to_text(exc)) # Now src has resolved file write to disk in current diectory for scp src = self._task.args.get('src') filename = str(uuid.uuid4()) cwd = self._loader.get_basedir() output_file = os.path.join(cwd, filename) try: with open(output_file, 'wb') as f: f.write(to_bytes(src, encoding='utf-8')) except Exception: os.remove(output_file) raise else: try: output_file = self._get_binary_src_file(src) except ValueError as exc: return dict(failed=True, msg=to_text(exc)) if socket_path is None: socket_path = self._connection.socket_path conn = Connection(socket_path) sock_timeout = conn.get_option('persistent_command_timeout') if dest is None: dest = src_file_path_name try: changed = self._handle_existing_file(conn, output_file, dest, proto, sock_timeout) if changed is False: result['changed'] = changed result['destination'] = dest return result except Exception as exc: result['msg'] = ( 'Warning: %s idempotency check failed. Check dest' % exc) try: conn.copy_file(source=output_file, destination=dest, proto=proto, timeout=sock_timeout) except Exception as exc: if to_text(exc) == "No response from server": if network_os == 'iosxr': # IOSXR sometimes closes socket prematurely after completion # of file transfer result[ 'msg'] = 'Warning: iosxr scp server pre close issue. Please check dest' else: result['failed'] = True result['msg'] = 'Exception received: %s' % exc if mode == 'text': # Cleanup tmp file expanded wih ansible vars os.remove(output_file) result['changed'] = changed result['destination'] = dest return result
def main(): module = AnsibleModule( argument_spec=dict( path=dict(type='path', required=True, aliases=['dest', 'name']), follow=dict(type='bool', default=False), get_md5=dict(type='bool', default=False), get_checksum=dict(type='bool', default=True), get_mime=dict(type='bool', default=True, aliases=['mime', 'mime_type', 'mime-type']), get_attributes=dict(type='bool', default=True, aliases=['attr', 'attributes']), checksum_algorithm=dict(type='str', default='sha1', choices=['md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512'], aliases=['checksum', 'checksum_algo']), ), supports_check_mode=True, ) path = module.params.get('path') b_path = to_bytes(path, errors='surrogate_or_strict') follow = module.params.get('follow') get_mime = module.params.get('get_mime') get_attr = module.params.get('get_attributes') get_checksum = module.params.get('get_checksum') checksum_algorithm = module.params.get('checksum_algorithm') # NOTE: undocumented option since 2.9 to be removed at a later date if possible (3.0+) # no real reason for keeping other than fear we may break older content. get_md5 = module.params.get('get_md5') # main stat data try: if follow: st = os.stat(b_path) else: st = os.lstat(b_path) except OSError as e: if e.errno == errno.ENOENT: output = {'exists': False} module.exit_json(changed=False, stat=output) module.fail_json(msg=e.strerror) # process base results output = format_output(module, path, st) # resolved permissions for perm in [('readable', os.R_OK), ('writeable', os.W_OK), ('executable', os.X_OK)]: output[perm[0]] = os.access(b_path, perm[1]) # symlink info if output.get('islnk'): output['lnk_source'] = os.path.realpath(b_path) output['lnk_target'] = os.readlink(b_path) try: # user data pw = pwd.getpwuid(st.st_uid) output['pw_name'] = pw.pw_name except (TypeError, KeyError): pass try: # group data grp_info = grp.getgrgid(st.st_gid) output['gr_name'] = grp_info.gr_name except (KeyError, ValueError, OverflowError): pass # checksums if output.get('isreg') and output.get('readable'): # NOTE: see above about get_md5 if get_md5: # Will fail on FIPS-140 compliant systems try: output['md5'] = module.md5(b_path) except ValueError: output['md5'] = None if get_checksum: output['checksum'] = module.digest_from_file(b_path, checksum_algorithm) # try to get mime data if requested if get_mime: output['mimetype'] = output['charset'] = 'unknown' mimecmd = module.get_bin_path('file') if mimecmd: mimecmd = [mimecmd, '-i', b_path] try: rc, out, err = module.run_command(mimecmd) if rc == 0: mimetype, charset = out.split(':')[1].split(';') output['mimetype'] = mimetype.strip() output['charset'] = charset.split('=')[1].strip() except Exception: pass # try to get attr data if get_attr: output['version'] = None output['attributes'] = [] output['attr_flags'] = '' out = module.get_file_attributes(b_path) for x in ('version', 'attributes', 'attr_flags'): if x in out: output[x] = out[x] module.exit_json(changed=False, stat=output)
def run(self, tmp=None, task_vars=None): ''' handler for file transfer 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 source = self._task.args.get('src', None) content = self._task.args.get('content', None) dest = self._task.args.get('dest', None) remote_src = boolean(self._task.args.get('remote_src', False), strict=False) local_follow = boolean(self._task.args.get('local_follow', True), strict=False) result['failed'] = True if not source and content is None: result['msg'] = 'src (or content) is required' elif not dest: result['msg'] = 'dest is required' elif source and content is not None: result['msg'] = 'src and content are mutually exclusive' elif content is not None and dest is not None and dest.endswith("/"): result['msg'] = "can not use content with a dir as dest" else: del result['failed'] if result.get('failed'): return self._ensure_invocation(result) # Define content_tempfile in case we set it after finding content populated. content_tempfile = None # If content is defined make a tmp file and write the content into it. if content is not None: try: # If content comes to us as a dict it should be decoded json. # We need to encode it back into a string to write it out. if isinstance(content, dict) or isinstance(content, list): content_tempfile = self._create_content_tempfile( json.dumps(content)) else: content_tempfile = self._create_content_tempfile(content) source = content_tempfile except Exception as err: result['failed'] = True result[ 'msg'] = "could not write content temp file: %s" % to_native( err) return self._ensure_invocation(result) # if we have first_available_file in our vars # look up the files and use the first one we find as src elif remote_src: result.update( self._execute_module(module_name='copy', task_vars=task_vars)) return self._ensure_invocation(result) else: # find_needle returns a path that may not have a trailing slash on # a directory so we need to determine that now (we use it just # like rsync does to figure out whether to include the directory # or only the files inside the directory trailing_slash = source.endswith(os.path.sep) try: # find in expected paths source = self._find_needle('files', source) except AnsibleError as e: result['failed'] = True result['msg'] = to_text(e) result['exception'] = traceback.format_exc() return self._ensure_invocation(result) if trailing_slash != source.endswith(os.path.sep): if source[-1] == os.path.sep: source = source[:-1] else: source = source + os.path.sep # A list of source file tuples (full_path, relative_path) which will try to copy to the destination source_files = {'files': [], 'directories': [], 'symlinks': []} # If source is a directory populate our list else source is a file and translate it to a tuple. if os.path.isdir(to_bytes(source, errors='surrogate_or_strict')): # Get a list of the files we want to replicate on the remote side source_files = _walk_dirs(source, local_follow=local_follow, trailing_slash_detector=self._connection. _shell.path_has_trailing_slash) # If it's recursive copy, destination is always a dir, # explicitly mark it so (note - copy module relies on this). if not self._connection._shell.path_has_trailing_slash(dest): dest = self._connection._shell.join_path(dest, '') # FIXME: Can we optimize cases where there's only one file, no # symlinks and any number of directories? In the original code, # empty directories are not copied.... else: source_files['files'] = [(source, os.path.basename(source))] changed = False module_return = dict(changed=False) # A register for if we executed a module. # Used to cut down on command calls when not recursive. module_executed = False # expand any user home dir specifier dest = self._remote_expand_user(dest) implicit_directories = set() for source_full, source_rel in source_files['files']: # copy files over. This happens first as directories that have # a file do not need to be created later # We only follow symlinks for files in the non-recursive case if source_files['directories']: follow = False else: follow = boolean(self._task.args.get('follow', False), strict=False) module_return = self._copy_file(source_full, source_rel, content, content_tempfile, dest, task_vars, follow) if module_return is None: continue if module_return.get('failed'): result.update(module_return) return self._ensure_invocation(result) paths = os.path.split(source_rel) dir_path = '' for dir_component in paths: os.path.join(dir_path, dir_component) implicit_directories.add(dir_path) if 'diff' in result and not result['diff']: del result['diff'] module_executed = True changed = changed or module_return.get('changed', False) for src, dest_path in source_files['directories']: # Find directories that are leaves as they might not have been # created yet. if dest_path in implicit_directories: continue # Use file module to create these new_module_args = _create_remote_file_args(self._task.args) new_module_args['path'] = os.path.join(dest, dest_path) new_module_args['state'] = 'directory' new_module_args['mode'] = self._task.args.get( 'directory_mode', None) new_module_args['recurse'] = False del new_module_args['src'] module_return = self._execute_module(module_name='file', module_args=new_module_args, task_vars=task_vars) if module_return.get('failed'): result.update(module_return) return self._ensure_invocation(result) module_executed = True changed = changed or module_return.get('changed', False) for target_path, dest_path in source_files['symlinks']: # Copy symlinks over new_module_args = _create_remote_file_args(self._task.args) new_module_args['path'] = os.path.join(dest, dest_path) new_module_args['src'] = target_path new_module_args['state'] = 'link' new_module_args['force'] = True # Only follow remote symlinks in the non-recursive case if source_files['directories']: new_module_args['follow'] = False module_return = self._execute_module(module_name='file', module_args=new_module_args, task_vars=task_vars) module_executed = True if module_return.get('failed'): result.update(module_return) return self._ensure_invocation(result) changed = changed or module_return.get('changed', False) if module_executed and len(source_files['files']) == 1: result.update(module_return) # the file module returns the file path as 'path', but # the copy module uses 'dest', so add it if it's not there if 'path' in result and 'dest' not in result: result['dest'] = result['path'] else: result.update(dict(dest=dest, src=source, changed=changed)) # Delete tmp path self._remove_tmp_path(self._connection._shell.tmpdir) return self._ensure_invocation(result)
def ensure_hardlink(path, src, follow, force, timestamps): b_path = to_bytes(path, errors='surrogate_or_strict') b_src = to_bytes(src, errors='surrogate_or_strict') prev_state = get_state(b_path) file_args = module.load_file_common_arguments(module.params) mtime = get_timestamp_for_time(timestamps['modification_time'], timestamps['modification_time_format']) atime = get_timestamp_for_time(timestamps['access_time'], timestamps['access_time_format']) # src is the source of a hardlink. We require it if we are creating a new hardlink. # We require path in the argument_spec so we know it is present at this point. if src is None: raise AnsibleModuleError( results={'msg': 'src is required for creating new hardlinks'}) if not os.path.exists(b_src): raise AnsibleModuleError(results={ 'msg': 'src does not exist', 'dest': path, 'src': src }) diff = initial_diff(path, 'hard', prev_state) changed = False if prev_state == 'absent': changed = True elif prev_state == 'link': b_old_src = os.readlink(b_path) if b_old_src != b_src: diff['before']['src'] = to_native(b_old_src, errors='strict') diff['after']['src'] = src changed = True elif prev_state == 'hard': if not os.stat(b_path).st_ino == os.stat(b_src).st_ino: changed = True if not force: raise AnsibleModuleError( results={ 'msg': 'Cannot link, different hard link exists at destination', 'dest': path, 'src': src }) elif prev_state == 'file': changed = True if not force: raise AnsibleModuleError( results={ 'msg': 'Cannot link, %s exists at destination' % prev_state, 'dest': path, 'src': src }) elif prev_state == 'directory': changed = True if os.path.exists(b_path): if os.stat(b_path).st_ino == os.stat(b_src).st_ino: return {'path': path, 'changed': False} elif not force: raise AnsibleModuleError( results={ 'msg': 'Cannot link: different hard link exists at destination', 'dest': path, 'src': src }) else: raise AnsibleModuleError(results={ 'msg': 'unexpected position reached', 'dest': path, 'src': src }) if changed and not module.check_mode: if prev_state != 'absent': # try to replace atomically b_tmppath = to_bytes(os.path.sep).join([ os.path.dirname(b_path), to_bytes(".%s.%s.tmp" % (os.getpid(), time.time())) ]) try: if prev_state == 'directory': if os.path.exists(b_path): try: os.unlink(b_path) except OSError as e: if e.errno != errno.ENOENT: # It may already have been removed raise os.link(b_src, b_tmppath) os.rename(b_tmppath, b_path) except OSError as e: if os.path.exists(b_tmppath): os.unlink(b_tmppath) raise AnsibleModuleError( results={ 'msg': 'Error while replacing: %s' % to_native(e, nonstring='simplerepr'), 'path': path }) else: try: os.link(b_src, b_path) except OSError as e: raise AnsibleModuleError( results={ 'msg': 'Error while linking: %s' % to_native(e, nonstring='simplerepr'), 'path': path }) if module.check_mode and not os.path.exists(b_path): return {'dest': path, 'src': src, 'changed': changed, 'diff': diff} changed = module.set_fs_attributes_if_different(file_args, changed, diff, expand=False) changed |= update_timestamp_for_file(file_args['path'], mtime, atime, diff) return {'dest': path, 'src': src, 'changed': changed, 'diff': diff}
def ensure_symlink(path, src, follow, force, timestamps): b_path = to_bytes(path, errors='surrogate_or_strict') b_src = to_bytes(src, errors='surrogate_or_strict') prev_state = get_state(b_path) mtime = get_timestamp_for_time(timestamps['modification_time'], timestamps['modification_time_format']) atime = get_timestamp_for_time(timestamps['access_time'], timestamps['access_time_format']) # source is both the source of a symlink or an informational passing of the src for a template module # or copy module, even if this module never uses it, it is needed to key off some things if src is None: if follow: # use the current target of the link as the source src = to_native(os.path.realpath(b_path), errors='strict') b_src = to_bytes(src, errors='surrogate_or_strict') if not os.path.islink(b_path) and os.path.isdir(b_path): relpath = path else: b_relpath = os.path.dirname(b_path) relpath = to_native(b_relpath, errors='strict') absrc = os.path.join(relpath, src) b_absrc = to_bytes(absrc, errors='surrogate_or_strict') if not force and not os.path.exists(b_absrc): raise AnsibleModuleError( results={ 'msg': 'src file does not exist, use "force=yes" if you' ' really want to create the link: %s' % absrc, 'path': path, 'src': src }) if prev_state == 'directory': if not force: raise AnsibleModuleError( results={ 'msg': 'refusing to convert from %s to symlink for %s' % (prev_state, path), 'path': path }) elif os.listdir(b_path): # refuse to replace a directory that has files in it raise AnsibleModuleError( results={ 'msg': 'the directory %s is not empty, refusing to' ' convert it' % path, 'path': path }) elif prev_state in ('file', 'hard') and not force: raise AnsibleModuleError( results={ 'msg': 'refusing to convert from %s to symlink for %s' % (prev_state, path), 'path': path }) diff = initial_diff(path, 'link', prev_state) changed = False if prev_state == 'absent': changed = True elif prev_state == 'link': b_old_src = os.readlink(b_path) if b_old_src != b_src: diff['before']['src'] = to_native(b_old_src, errors='strict') diff['after']['src'] = src changed = True elif prev_state == 'hard': changed = True if not force: raise AnsibleModuleError( results={ 'msg': 'Cannot link because a hard link exists at destination', 'dest': path, 'src': src }) elif prev_state == 'file': changed = True if not force: raise AnsibleModuleError( results={ 'msg': 'Cannot link because a file exists at destination', 'dest': path, 'src': src }) elif prev_state == 'directory': changed = True if os.path.exists(b_path): if not force: raise AnsibleModuleError( results={ 'msg': 'Cannot link because a file exists at destination', 'dest': path, 'src': src }) else: raise AnsibleModuleError(results={ 'msg': 'unexpected position reached', 'dest': path, 'src': src }) if changed and not module.check_mode: if prev_state != 'absent': # try to replace atomically b_tmppath = to_bytes(os.path.sep).join([ os.path.dirname(b_path), to_bytes(".%s.%s.tmp" % (os.getpid(), time.time())) ]) try: if prev_state == 'directory': os.rmdir(b_path) os.symlink(b_src, b_tmppath) os.rename(b_tmppath, b_path) except OSError as e: if os.path.exists(b_tmppath): os.unlink(b_tmppath) raise AnsibleModuleError( results={ 'msg': 'Error while replacing: %s' % to_native(e, nonstring='simplerepr'), 'path': path }) else: try: os.symlink(b_src, b_path) except OSError as e: raise AnsibleModuleError( results={ 'msg': 'Error while linking: %s' % to_native(e, nonstring='simplerepr'), 'path': path }) if module.check_mode and not os.path.exists(b_path): return {'dest': path, 'src': src, 'changed': changed, 'diff': diff} # Now that we might have created the symlink, get the arguments. # We need to do it now so we can properly follow the symlink if needed # because load_file_common_arguments sets 'path' according # the value of follow and the symlink existence. file_args = module.load_file_common_arguments(module.params) # Whenever we create a link to a nonexistent target we know that the nonexistent target # cannot have any permissions set on it. Skip setting those and emit a warning (the user # can set follow=False to remove the warning) if follow and os.path.islink(b_path) and not os.path.exists( file_args['path']): module.warn( 'Cannot set fs attributes on a non-existent symlink target. follow should be' ' set to False to avoid this.') else: changed = module.set_fs_attributes_if_different(file_args, changed, diff, expand=False) changed |= update_timestamp_for_file(file_args['path'], mtime, atime, diff) return {'dest': path, 'src': src, 'changed': changed, 'diff': diff}
def ensure_directory(path, follow, recurse, timestamps): b_path = to_bytes(path, errors='surrogate_or_strict') prev_state = get_state(b_path) file_args = module.load_file_common_arguments(module.params) mtime = get_timestamp_for_time(timestamps['modification_time'], timestamps['modification_time_format']) atime = get_timestamp_for_time(timestamps['access_time'], timestamps['access_time_format']) # For followed symlinks, we need to operate on the target of the link if follow and prev_state == 'link': b_path = os.path.realpath(b_path) path = to_native(b_path, errors='strict') file_args['path'] = path prev_state = get_state(b_path) changed = False diff = initial_diff(path, 'directory', prev_state) if prev_state == 'absent': # Create directory and assign permissions to it if module.check_mode: return {'changed': True, 'diff': diff} curpath = '' try: # Split the path so we can apply filesystem attributes recursively # from the root (/) directory for absolute paths or the base path # of a relative path. We can then walk the appropriate directory # path to apply attributes. # Something like mkdir -p with mode applied to all of the newly created directories for dirname in path.strip('/').split('/'): curpath = '/'.join([curpath, dirname]) # Remove leading slash if we're creating a relative path if not os.path.isabs(path): curpath = curpath.lstrip('/') b_curpath = to_bytes(curpath, errors='surrogate_or_strict') if not os.path.exists(b_curpath): try: os.mkdir(b_curpath) changed = True except OSError as ex: # Possibly something else created the dir since the os.path.exists # check above. As long as it's a dir, we don't need to error out. if not (ex.errno == errno.EEXIST and os.path.isdir(b_curpath)): raise tmp_file_args = file_args.copy() tmp_file_args['path'] = curpath changed = module.set_fs_attributes_if_different( tmp_file_args, changed, diff, expand=False) changed |= update_timestamp_for_file( file_args['path'], mtime, atime, diff) except Exception as e: raise AnsibleModuleError( results={ 'msg': 'There was an issue creating %s as requested:' ' %s' % (curpath, to_native(e)), 'path': path }) return {'path': path, 'changed': changed, 'diff': diff} elif prev_state != 'directory': # We already know prev_state is not 'absent', therefore it exists in some form. raise AnsibleModuleError( results={ 'msg': '%s already exists as a %s' % (path, prev_state), 'path': path }) # # previous state == directory # changed = module.set_fs_attributes_if_different(file_args, changed, diff, expand=False) changed |= update_timestamp_for_file(file_args['path'], mtime, atime, diff) if recurse: changed |= recursive_set_attributes(b_path, follow, file_args, mtime, atime) return {'path': path, 'changed': changed, 'diff': diff}
def main(): module = AnsibleModule( argument_spec=dict( group_id=dict(required=True), artifact_id=dict(required=True), version=dict(default=None), version_by_spec=dict(default=None), classifier=dict(default=''), extension=dict(default='jar'), repository_url=dict(default='https://repo1.maven.org/maven2'), username=dict(default=None, aliases=['aws_secret_key']), password=dict(default=None, no_log=True, aliases=['aws_secret_access_key']), headers=dict(type='dict'), force_basic_auth=dict(default=False, type='bool'), state=dict(default="present", choices=["present", "absent"]), # TODO - Implement a "latest" state timeout=dict(default=10, type='int'), dest=dict(type="path", required=True), validate_certs=dict(required=False, default=True, type='bool'), keep_name=dict(required=False, default=False, type='bool'), verify_checksum=dict(required=False, default='download', choices=['never', 'download', 'change', 'always']) ), add_file_common_args=True, mutually_exclusive=([('version', 'version_by_spec')]) ) if not HAS_LXML_ETREE: module.fail_json(msg=missing_required_lib('lxml'), exception=LXML_ETREE_IMP_ERR) if module.params['version_by_spec'] and not HAS_SEMANTIC_VERSION: module.fail_json(msg=missing_required_lib('semantic_version'), exception=SEMANTIC_VERSION_IMP_ERR) repository_url = module.params["repository_url"] if not repository_url: repository_url = "https://repo1.maven.org/maven2" try: parsed_url = urlparse(repository_url) except AttributeError as e: module.fail_json(msg='url parsing went wrong %s' % e) local = parsed_url.scheme == "file" if parsed_url.scheme == 's3' and not HAS_BOTO: module.fail_json(msg=missing_required_lib('boto3', reason='when using s3:// repository URLs'), exception=BOTO_IMP_ERR) group_id = module.params["group_id"] artifact_id = module.params["artifact_id"] version = module.params["version"] version_by_spec = module.params["version_by_spec"] classifier = module.params["classifier"] extension = module.params["extension"] headers = module.params['headers'] state = module.params["state"] dest = module.params["dest"] b_dest = to_bytes(dest, errors='surrogate_or_strict') keep_name = module.params["keep_name"] verify_checksum = module.params["verify_checksum"] verify_download = verify_checksum in ['download', 'always'] verify_change = verify_checksum in ['change', 'always'] downloader = MavenDownloader(module, repository_url, local, headers) if not version_by_spec and not version: version = "latest" try: artifact = Artifact(group_id, artifact_id, version, version_by_spec, classifier, extension) except ValueError as e: module.fail_json(msg=e.args[0]) changed = False prev_state = "absent" if dest.endswith(os.sep): b_dest = to_bytes(dest, errors='surrogate_or_strict') if not os.path.exists(b_dest): (pre_existing_dir, new_directory_list) = split_pre_existing_dir(dest) os.makedirs(b_dest) directory_args = module.load_file_common_arguments(module.params) directory_mode = module.params["directory_mode"] if directory_mode is not None: directory_args['mode'] = directory_mode else: directory_args['mode'] = None changed = adjust_recursive_directory_permissions(pre_existing_dir, new_directory_list, module, directory_args, changed) if os.path.isdir(b_dest): version_part = version if version == 'latest': version_part = downloader.find_latest_version_available(artifact) elif version_by_spec: version_part = downloader.find_version_by_spec(artifact) filename = "{artifact_id}{version_part}{classifier}.{extension}".format( artifact_id=artifact_id, version_part="-{0}".format(version_part) if keep_name else "", classifier="-{0}".format(classifier) if classifier else "", extension=extension ) dest = posixpath.join(dest, filename) b_dest = to_bytes(dest, errors='surrogate_or_strict') if os.path.lexists(b_dest) and ((not verify_change) or not downloader.is_invalid_md5(dest, downloader.find_uri_for_artifact(artifact))): prev_state = "present" if prev_state == "absent": try: download_error = downloader.download(module.tmpdir, artifact, verify_download, b_dest) if download_error is None: changed = True else: module.fail_json(msg="Cannot retrieve the artifact to destination: " + download_error) except ValueError as e: module.fail_json(msg=e.args[0]) module.params['dest'] = dest file_args = module.load_file_common_arguments(module.params) changed = module.set_fs_attributes_if_different(file_args, changed) if changed: module.exit_json(state=state, dest=dest, group_id=group_id, artifact_id=artifact_id, version=version, classifier=classifier, extension=extension, repository_url=repository_url, changed=changed) else: module.exit_json(state=state, dest=dest, changed=changed)
def is_unarchived(self): # BSD unzip doesn't support zipinfo listings with timestamp. cmd = [self.zipinfocmd_path, '-T', '-s', self.src] if self.excludes: cmd.extend(['-x', ] + self.excludes) rc, out, err = self.module.run_command(cmd) old_out = out diff = '' out = '' if rc == 0: unarchived = True else: unarchived = False # Get some information related to user/group ownership umask = os.umask(0) os.umask(umask) systemtype = platform.system() # Get current user and group information groups = os.getgroups() run_uid = os.getuid() run_gid = os.getgid() try: run_owner = pwd.getpwuid(run_uid).pw_name except (TypeError, KeyError): run_owner = run_uid try: run_group = grp.getgrgid(run_gid).gr_name except (KeyError, ValueError, OverflowError): run_group = run_gid # Get future user ownership fut_owner = fut_uid = None if self.file_args['owner']: try: tpw = pwd.getpwnam(self.file_args['owner']) except KeyError: try: tpw = pwd.getpwuid(self.file_args['owner']) except (TypeError, KeyError): tpw = pwd.getpwuid(run_uid) fut_owner = tpw.pw_name fut_uid = tpw.pw_uid else: try: fut_owner = run_owner except Exception: pass fut_uid = run_uid # Get future group ownership fut_group = fut_gid = None if self.file_args['group']: try: tgr = grp.getgrnam(self.file_args['group']) except (ValueError, KeyError): try: tgr = grp.getgrgid(self.file_args['group']) except (KeyError, ValueError, OverflowError): tgr = grp.getgrgid(run_gid) fut_group = tgr.gr_name fut_gid = tgr.gr_gid else: try: fut_group = run_group except Exception: pass fut_gid = run_gid for line in old_out.splitlines(): change = False pcs = line.split(None, 7) if len(pcs) != 8: # Too few fields... probably a piece of the header or footer continue # Check first and seventh field in order to skip header/footer if len(pcs[0]) != 7 and len(pcs[0]) != 10: continue if len(pcs[6]) != 15: continue # Possible entries: # -rw-rws--- 1.9 unx 2802 t- defX 11-Aug-91 13:48 perms.2660 # -rw-a-- 1.0 hpf 5358 Tl i4:3 4-Dec-91 11:33 longfilename.hpfs # -r--ahs 1.1 fat 4096 b- i4:2 14-Jul-91 12:58 EA DATA. SF # --w------- 1.0 mac 17357 bx i8:2 4-May-92 04:02 unzip.macr if pcs[0][0] not in 'dl-?' or not frozenset(pcs[0][1:]).issubset('rwxstah-'): continue ztype = pcs[0][0] permstr = pcs[0][1:] version = pcs[1] ostype = pcs[2] size = int(pcs[3]) path = to_text(pcs[7], errors='surrogate_or_strict') # Skip excluded files if path in self.excludes: out += 'Path %s is excluded on request\n' % path continue # Itemized change requires L for symlink if path[-1] == '/': if ztype != 'd': err += 'Path %s incorrectly tagged as "%s", but is a directory.\n' % (path, ztype) ftype = 'd' elif ztype == 'l': ftype = 'L' elif ztype == '-': ftype = 'f' elif ztype == '?': ftype = 'f' # Some files may be storing FAT permissions, not Unix permissions # For FAT permissions, we will use a base permissions set of 777 if the item is a directory or has the execute bit set. Otherwise, 666. # This permission will then be modified by the system UMask. # BSD always applies the Umask, even to Unix permissions. # For Unix style permissions on Linux or Mac, we want to use them directly. # So we set the UMask for this file to zero. That permission set will then be unchanged when calling _permstr_to_octal if len(permstr) == 6: if path[-1] == '/': permstr = 'rwxrwxrwx' elif permstr == 'rwx---': permstr = 'rwxrwxrwx' else: permstr = 'rw-rw-rw-' file_umask = umask elif 'bsd' in systemtype.lower(): file_umask = umask else: file_umask = 0 # Test string conformity if len(permstr) != 9 or not ZIP_FILE_MODE_RE.match(permstr): raise UnarchiveError('ZIP info perm format incorrect, %s' % permstr) # DEBUG # err += "%s%s %10d %s\n" % (ztype, permstr, size, path) b_dest = os.path.join(self.b_dest, to_bytes(path, errors='surrogate_or_strict')) try: st = os.lstat(b_dest) except Exception: change = True self.includes.append(path) err += 'Path %s is missing\n' % path diff += '>%s++++++.?? %s\n' % (ftype, path) continue # Compare file types if ftype == 'd' and not stat.S_ISDIR(st.st_mode): change = True self.includes.append(path) err += 'File %s already exists, but not as a directory\n' % path diff += 'c%s++++++.?? %s\n' % (ftype, path) continue if ftype == 'f' and not stat.S_ISREG(st.st_mode): change = True unarchived = False self.includes.append(path) err += 'Directory %s already exists, but not as a regular file\n' % path diff += 'c%s++++++.?? %s\n' % (ftype, path) continue if ftype == 'L' and not stat.S_ISLNK(st.st_mode): change = True self.includes.append(path) err += 'Directory %s already exists, but not as a symlink\n' % path diff += 'c%s++++++.?? %s\n' % (ftype, path) continue itemized = list('.%s.......??' % ftype) # Note: this timestamp calculation has a rounding error # somewhere... unzip and this timestamp can be one second off # When that happens, we report a change and re-unzip the file dt_object = datetime.datetime(*(time.strptime(pcs[6], '%Y%m%d.%H%M%S')[0:6])) timestamp = time.mktime(dt_object.timetuple()) # Compare file timestamps if stat.S_ISREG(st.st_mode): if self.module.params['keep_newer']: if timestamp > st.st_mtime: change = True self.includes.append(path) err += 'File %s is older, replacing file\n' % path itemized[4] = 't' elif stat.S_ISREG(st.st_mode) and timestamp < st.st_mtime: # Add to excluded files, ignore other changes out += 'File %s is newer, excluding file\n' % path self.excludes.append(path) continue else: if timestamp != st.st_mtime: change = True self.includes.append(path) err += 'File %s differs in mtime (%f vs %f)\n' % (path, timestamp, st.st_mtime) itemized[4] = 't' # Compare file sizes if stat.S_ISREG(st.st_mode) and size != st.st_size: change = True err += 'File %s differs in size (%d vs %d)\n' % (path, size, st.st_size) itemized[3] = 's' # Compare file checksums if stat.S_ISREG(st.st_mode): crc = crc32(b_dest) if crc != self._crc32(path): change = True err += 'File %s differs in CRC32 checksum (0x%08x vs 0x%08x)\n' % (path, self._crc32(path), crc) itemized[2] = 'c' # Compare file permissions # Do not handle permissions of symlinks if ftype != 'L': # Use the new mode provided with the action, if there is one if self.file_args['mode']: if isinstance(self.file_args['mode'], int): mode = self.file_args['mode'] else: try: mode = int(self.file_args['mode'], 8) except Exception as e: try: mode = AnsibleModule._symbolic_mode_to_octal(st, self.file_args['mode']) except ValueError as e: self.module.fail_json(path=path, msg="%s" % to_native(e), exception=traceback.format_exc()) # Only special files require no umask-handling elif ztype == '?': mode = self._permstr_to_octal(permstr, 0) else: mode = self._permstr_to_octal(permstr, file_umask) if mode != stat.S_IMODE(st.st_mode): change = True itemized[5] = 'p' err += 'Path %s differs in permissions (%o vs %o)\n' % (path, mode, stat.S_IMODE(st.st_mode)) # Compare file user ownership owner = uid = None try: owner = pwd.getpwuid(st.st_uid).pw_name except (TypeError, KeyError): uid = st.st_uid # If we are not root and requested owner is not our user, fail if run_uid != 0 and (fut_owner != run_owner or fut_uid != run_uid): raise UnarchiveError('Cannot change ownership of %s to %s, as user %s' % (path, fut_owner, run_owner)) if owner and owner != fut_owner: change = True err += 'Path %s is owned by user %s, not by user %s as expected\n' % (path, owner, fut_owner) itemized[6] = 'o' elif uid and uid != fut_uid: change = True err += 'Path %s is owned by uid %s, not by uid %s as expected\n' % (path, uid, fut_uid) itemized[6] = 'o' # Compare file group ownership group = gid = None try: group = grp.getgrgid(st.st_gid).gr_name except (KeyError, ValueError, OverflowError): gid = st.st_gid if run_uid != 0 and fut_gid not in groups: raise UnarchiveError('Cannot change group ownership of %s to %s, as user %s' % (path, fut_group, run_owner)) if group and group != fut_group: change = True err += 'Path %s is owned by group %s, not by group %s as expected\n' % (path, group, fut_group) itemized[6] = 'g' elif gid and gid != fut_gid: change = True err += 'Path %s is owned by gid %s, not by gid %s as expected\n' % (path, gid, fut_gid) itemized[6] = 'g' # Register changed files and finalize diff output if change: if path not in self.includes: self.includes.append(path) diff += '%s %s\n' % (''.join(itemized), path) if self.includes: unarchived = False # DEBUG # out = old_out + out return dict(unarchived=unarchived, rc=rc, out=out, err=err, cmd=cmd, diff=diff)
def main(): # the command module is the one ansible module that does not take key=value args # hence don't copy this one if you are looking to build others! module = AnsibleModule( argument_spec=dict( _raw_params=dict(), _uses_shell=dict(type='bool', default=False), argv=dict(type='list'), chdir=dict(type='path'), executable=dict(), creates=dict(type='path'), removes=dict(type='path'), # The default for this really comes from the action plugin warn=dict(type='bool', default=True), stdin=dict(required=False), stdin_add_newline=dict(type='bool', default=True), strip_empty_ends=dict(type='bool', default=True), ), supports_check_mode=True, ) shell = module.params['_uses_shell'] chdir = module.params['chdir'] executable = module.params['executable'] args = module.params['_raw_params'] argv = module.params['argv'] creates = module.params['creates'] removes = module.params['removes'] warn = module.params['warn'] stdin = module.params['stdin'] stdin_add_newline = module.params['stdin_add_newline'] strip = module.params['strip_empty_ends'] if not shell and executable: module.warn( "As of Ansible 2.4, the parameter 'executable' is no longer supported with the 'command' module. Not using '%s'." % executable) executable = None if (not args or args.strip() == '') and not argv: module.fail_json(rc=256, msg="no command given") if args and argv: module.fail_json(rc=256, msg="only command or argv can be given, not both") if not shell and args: args = shlex.split(args) args = args or argv # All args must be strings if is_iterable(args, include_strings=False): args = [ to_native(arg, errors='surrogate_or_strict', nonstring='simplerepr') for arg in args ] if chdir: try: chdir = to_bytes(os.path.abspath(chdir), errors='surrogate_or_strict') except ValueError as e: module.fail_json(msg='Unable to use supplied chdir: %s' % to_text(e)) try: os.chdir(chdir) except (IOError, OSError) as e: module.fail_json( msg='Unable to change directory before execution: %s' % to_text(e)) if creates: # do not run the command if the line contains creates=filename # and the filename already exists. This allows idempotence # of command executions. if glob.glob(creates): module.exit_json(cmd=args, stdout="skipped, since %s exists" % creates, changed=False, rc=0) if removes: # do not run the command if the line contains removes=filename # and the filename does not exist. This allows idempotence # of command executions. if not glob.glob(removes): module.exit_json(cmd=args, stdout="skipped, since %s does not exist" % removes, changed=False, rc=0) if warn: check_command(module, args) startd = datetime.datetime.now() if not module.check_mode: rc, out, err = module.run_command(args, executable=executable, use_unsafe_shell=shell, encoding=None, data=stdin, binary_data=(not stdin_add_newline)) elif creates or removes: rc = 0 out = err = b'Command would have run if not in check mode' else: module.exit_json(msg="skipped, running in check mode", skipped=True) endd = datetime.datetime.now() delta = endd - startd if strip: out = out.rstrip(b"\r\n") err = err.rstrip(b"\r\n") result = dict( cmd=args, stdout=out, stderr=err, rc=rc, start=str(startd), end=str(endd), delta=str(delta), changed=True, ) if rc != 0: module.fail_json(msg='non-zero return code', **result) module.exit_json(**result)
def main(): module = AnsibleModule( # not checking because of daisy chain to file module argument_spec=dict( src=dict(type='path', required=True), dest=dict(type='path', required=True), remote_src=dict(type='bool', default=False), creates=dict(type='path'), list_files=dict(type='bool', default=False), keep_newer=dict(type='bool', default=False), exclude=dict(type='list', default=[]), extra_opts=dict(type='list', default=[]), validate_certs=dict(type='bool', default=True), ), add_file_common_args=True, # check-mode only works for zip files, we cover that later supports_check_mode=True, ) src = module.params['src'] dest = module.params['dest'] b_dest = to_bytes(dest, errors='surrogate_or_strict') remote_src = module.params['remote_src'] file_args = module.load_file_common_arguments(module.params) # did tar file arrive? if not os.path.exists(src): if not remote_src: module.fail_json(msg="Source '%s' failed to transfer" % src) # If remote_src=true, and src= contains ://, try and download the file to a temp directory. elif '://' in src: src = fetch_file(module, src) else: module.fail_json(msg="Source '%s' does not exist" % src) if not os.access(src, os.R_OK): module.fail_json(msg="Source '%s' not readable" % src) # skip working with 0 size archives try: if os.path.getsize(src) == 0: module.fail_json(msg="Invalid archive '%s', the file is 0 bytes" % src) except Exception as e: module.fail_json(msg="Source '%s' not readable, %s" % (src, to_native(e))) # is dest OK to receive tar file? if not os.path.isdir(b_dest): module.fail_json(msg="Destination '%s' is not a directory" % dest) handler = pick_handler(src, b_dest, file_args, module) res_args = dict(handler=handler.__class__.__name__, dest=dest, src=src) # do we need to do unpack? check_results = handler.is_unarchived() # DEBUG # res_args['check_results'] = check_results if module.check_mode: res_args['changed'] = not check_results['unarchived'] elif check_results['unarchived']: res_args['changed'] = False else: # do the unpack try: res_args['extract_results'] = handler.unarchive() if res_args['extract_results']['rc'] != 0: module.fail_json(msg="failed to unpack %s to %s" % (src, dest), **res_args) except IOError: module.fail_json(msg="failed to unpack %s to %s" % (src, dest), **res_args) else: res_args['changed'] = True # Get diff if required if check_results.get('diff', False): res_args['diff'] = {'prepared': check_results['diff']} # Run only if we found differences (idempotence) or diff was missing if res_args.get('diff', True) and not module.check_mode: # do we need to change perms? for filename in handler.files_in_archive: file_args['path'] = os.path.join(b_dest, to_bytes(filename, errors='surrogate_or_strict')) try: res_args['changed'] = module.set_fs_attributes_if_different(file_args, res_args['changed'], expand=False) except (IOError, OSError) as e: module.fail_json(msg="Unexpected error when accessing exploded file: %s" % to_native(e), **res_args) if module.params['list_files']: res_args['files'] = handler.files_in_archive module.exit_json(**res_args)
def main(): module = AnsibleModule( argument_spec=dict(name=dict(type='str', required=True), state=dict(type='str', required=True, choices=[ 'absent', 'opts_absent', 'opts_present', 'present' ]), backing_device=dict(type='str'), password=dict(type='path'), opts=dict(type='str'), path=dict(type='path', default='/etc/crypttab')), supports_check_mode=True, ) backing_device = module.params['backing_device'] password = module.params['password'] opts = module.params['opts'] state = module.params['state'] path = module.params['path'] name = module.params['name'] if name.startswith('/dev/mapper/'): name = name[len('/dev/mapper/'):] if state != 'absent' and backing_device is None and password is None and opts is None: module.fail_json( msg= "expected one or more of 'backing_device', 'password' or 'opts'", **module.params) if 'opts' in state and (backing_device is not None or password is not None): module.fail_json( msg="cannot update 'backing_device' or 'password' when state=%s" % state, **module.params) for arg_name, arg in (('name', name), ('backing_device', backing_device), ('password', password), ('opts', opts)): if (arg is not None and (' ' in arg or '\t' in arg or arg == '')): module.fail_json( msg="invalid '%s': contains white space or is empty" % arg_name, **module.params) try: crypttab = Crypttab(path) existing_line = crypttab.match(name) except Exception as e: module.fail_json(msg="failed to open and parse crypttab file: %s" % to_native(e), exception=traceback.format_exc(), **module.params) if 'present' in state and existing_line is None and backing_device is None: module.fail_json(msg="'backing_device' required to add a new entry", **module.params) changed, reason = False, '?' if state == 'absent': if existing_line is not None: changed, reason = existing_line.remove() elif state == 'present': if existing_line is not None: changed, reason = existing_line.set(backing_device, password, opts) else: changed, reason = crypttab.add( Line(None, name, backing_device, password, opts)) elif state == 'opts_present': if existing_line is not None: changed, reason = existing_line.opts.add(opts) else: changed, reason = crypttab.add( Line(None, name, backing_device, password, opts)) elif state == 'opts_absent': if existing_line is not None: changed, reason = existing_line.opts.remove(opts) if changed and not module.check_mode: try: f = open(path, 'wb') f.write(to_bytes(crypttab, errors='surrogate_or_strict')) finally: f.close() module.exit_json(changed=changed, msg=reason, **module.params)
def test_to_bytes(in_string, encoding, expected): """test happy path of encoding to bytes""" assert to_bytes(in_string, encoding) == expected
def b64decode(string, encoding='utf-8'): return to_text(base64.b64decode(to_bytes(string, errors='surrogate_or_strict')), encoding=encoding)
def test_to_bytes_unsafe(): assert isinstance(to_bytes(AnsibleUnsafeText(u'foo')), AnsibleUnsafeBytes) assert to_bytes(AnsibleUnsafeText(u'foo')) == AnsibleUnsafeBytes(b'foo')
# from __future__ import (absolute_import, division, print_function) __metaclass__ = type from os import path import json from mock import MagicMock from ansible_collections.notstdlib.moveitallout.tests.unit.compat import unittest from ansible_collections.notstdlib.moveitallout.plugins.cliconf import ios from ansible_collections.notstdlib.moveitallout.plugins.module_utils._text import to_bytes b_FIXTURE_DIR = b'%s/fixtures/ios' % ( to_bytes(path.dirname(path.abspath(__file__)), errors='surrogate_or_strict') ) def _connection_side_effect(*args, **kwargs): try: if args: value = args[0] else: value = kwargs.get('command') fixture_path = path.abspath( b'%s/%s' % (b_FIXTURE_DIR, b'_'.join(value.split(b' '))) ) with open(fixture_path, 'rb') as file_desc: return file_desc.read()
def _connect(self): if not HAS_NCCLIENT: raise AnsibleError("%s: %s" % (missing_required_lib("ncclient"), to_native(NCCLIENT_IMP_ERR))) self.queue_message('log', 'ssh connection done, starting ncclient') allow_agent = True if self._play_context.password is not None: allow_agent = False setattr(self._play_context, 'allow_agent', allow_agent) self.key_filename = self._play_context.private_key_file or self.get_option('private_key_file') if self.key_filename: self.key_filename = str(os.path.expanduser(self.key_filename)) self._ssh_config = self.get_option('netconf_ssh_config') if self._ssh_config in BOOLEANS_TRUE: self._ssh_config = True elif self._ssh_config in BOOLEANS_FALSE: self._ssh_config = None # Try to guess the network_os if the network_os is set to auto if self._network_os == 'auto': for cls in netconf_loader.all(class_only=True): network_os = cls.guess_network_os(self) if network_os: self.queue_message('vvv', 'discovered network_os %s' % network_os) self._network_os = network_os # If we have tried to detect the network_os but were unable to i.e. network_os is still 'auto' # then use default as the network_os if self._network_os == 'auto': # Network os not discovered. Set it to default self.queue_message('vvv', 'Unable to discover network_os. Falling back to default.') self._network_os = 'default' try: ncclient_device_handler = self.netconf.get_option('ncclient_device_handler') except KeyError: ncclient_device_handler = 'default' self.queue_message('vvv', 'identified ncclient device handler: %s.' % ncclient_device_handler) device_params = {'name': ncclient_device_handler} try: port = self._play_context.port or 830 self.queue_message('vvv', "ESTABLISH NETCONF SSH CONNECTION FOR USER: %s on PORT %s TO %s WITH SSH_CONFIG = %s" % (self._play_context.remote_user, port, self._play_context.remote_addr, self._ssh_config)) self._manager = manager.connect( host=self._play_context.remote_addr, port=port, username=self._play_context.remote_user, password=self._play_context.password, key_filename=self.key_filename, hostkey_verify=self.get_option('host_key_checking'), look_for_keys=self.get_option('look_for_keys'), device_params=device_params, allow_agent=self._play_context.allow_agent, timeout=self.get_option('persistent_connect_timeout'), ssh_config=self._ssh_config ) self._manager._timeout = self.get_option('persistent_command_timeout') except SSHUnknownHostError as exc: raise AnsibleConnectionFailure(to_native(exc)) except ImportError: raise AnsibleError("connection=netconf is not supported on {0}".format(self._network_os)) if not self._manager.connected: return 1, b'', b'not connected' self.queue_message('log', 'ncclient manager object created successfully') self._connected = True super(Connection, self)._connect() return 0, to_bytes(self._manager.session_id, errors='surrogate_or_strict'), b''
def fetch_file(self, in_path, out_path): super(Connection, self).fetch_file(in_path, out_path) display.vvv("FETCH %s TO %s" % (in_path, out_path), host=self._psrp_host) in_path = self._shell._unquote(in_path) out_path = out_path.replace('\\', '/') # because we are dealing with base64 data we need to get the max size # of the bytes that the base64 size would equal max_b64_size = int(self.runspace.connection.max_payload_size - (self.runspace.connection.max_payload_size / 4 * 3)) buffer_size = max_b64_size - (max_b64_size % 1024) # setup the file stream with read only mode setup_script = '''$ErrorActionPreference = "Stop" $path = '%s' if (Test-Path -Path $path -PathType Leaf) { $fs = New-Object -TypeName System.IO.FileStream -ArgumentList @( $path, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::Read ) $buffer_size = %d } elseif (Test-Path -Path $path -PathType Container) { Write-Output -InputObject "[DIR]" } else { Write-Error -Message "$path does not exist" $host.SetShouldExit(1) }''' % (self._shell._escape(in_path), buffer_size) # read the file stream at the offset and return the b64 string read_script = '''$ErrorActionPreference = "Stop" $fs.Seek(%d, [System.IO.SeekOrigin]::Begin) > $null $buffer = New-Object -TypeName byte[] -ArgumentList $buffer_size $bytes_read = $fs.Read($buffer, 0, $buffer_size) if ($bytes_read -gt 0) { $bytes = $buffer[0..($bytes_read - 1)] Write-Output -InputObject ([System.Convert]::ToBase64String($bytes)) }''' # need to run the setup script outside of the local scope so the # file stream stays active between fetch operations rc, stdout, stderr = self._exec_psrp_script(setup_script, use_local_scope=False, force_stop=True) if rc != 0: raise AnsibleError("failed to setup file stream for fetch '%s': %s" % (out_path, to_native(stderr))) elif stdout.strip() == '[DIR]': # to be consistent with other connection plugins, we assume the caller has created the target dir return b_out_path = to_bytes(out_path, errors='surrogate_or_strict') # to be consistent with other connection plugins, we assume the caller has created the target dir offset = 0 with open(b_out_path, 'wb') as out_file: while True: display.vvvvv("PSRP FETCH %s to %s (offset=%d" % (in_path, out_path, offset), host=self._psrp_host) rc, stdout, stderr = self._exec_psrp_script(read_script % offset, force_stop=True) if rc != 0: raise AnsibleError("failed to transfer file to '%s': %s" % (out_path, to_native(stderr))) data = base64.b64decode(stdout.strip()) out_file.write(data) if len(data) < buffer_size: break offset += len(data) rc, stdout, stderr = self._exec_psrp_script("$fs.Close()", force_stop=True) if rc != 0: display.warning("failed to close remote file stream of file " "'%s': %s" % (in_path, to_native(stderr)))
def copy_left_only(src, dest, module): changed = False owner = module.params['owner'] group = module.params['group'] local_follow = module.params['local_follow'] left_only = filecmp.dircmp(src, dest).left_only if len(left_only): changed = True if not module.check_mode: for item in left_only: src_item_path = os.path.join(src, item) dest_item_path = os.path.join(dest, item) b_src_item_path = to_bytes(src_item_path, errors='surrogate_or_strict') b_dest_item_path = to_bytes(dest_item_path, errors='surrogate_or_strict') if os.path.islink(b_src_item_path) and os.path.isdir( b_src_item_path) and local_follow is True: shutil.copytree(b_src_item_path, b_dest_item_path, symlinks=not (local_follow)) chown_recursive(b_dest_item_path, module) if os.path.islink(b_src_item_path) and os.path.isdir( b_src_item_path) and local_follow is False: linkto = os.readlink(b_src_item_path) os.symlink(linkto, b_dest_item_path) if os.path.islink(b_src_item_path) and os.path.isfile( b_src_item_path) and local_follow is True: shutil.copyfile(b_src_item_path, b_dest_item_path) if owner is not None: module.set_owner_if_different(b_dest_item_path, owner, False) if group is not None: module.set_group_if_different(b_dest_item_path, group, False) if os.path.islink(b_src_item_path) and os.path.isfile( b_src_item_path) and local_follow is False: linkto = os.readlink(b_src_item_path) os.symlink(linkto, b_dest_item_path) if not os.path.islink(b_src_item_path) and os.path.isfile( b_src_item_path): shutil.copyfile(b_src_item_path, b_dest_item_path) if owner is not None: module.set_owner_if_different(b_dest_item_path, owner, False) if group is not None: module.set_group_if_different(b_dest_item_path, group, False) if not os.path.islink(b_src_item_path) and os.path.isdir( b_src_item_path): shutil.copytree(b_src_item_path, b_dest_item_path, symlinks=not (local_follow)) chown_recursive(b_dest_item_path, module) changed = True return changed
def daemonize(module, cmd): ''' Execute a command while detaching as a daemon, returns rc, stdout, and stderr. :arg module: is an AnsibleModule object, used for it's utility methods :arg cmd: is a list or string representing the command and options to run This is complex because daemonization is hard for people. What we do is daemonize a part of this module, the daemon runs the command, picks up the return code and output, and returns it to the main process. ''' # init some vars chunk = 4096 # FIXME: pass in as arg? errors = 'surrogate_or_strict' # start it! try: pipe = os.pipe() pid = fork_process() except OSError: module.fail_json(msg="Error while attempting to fork: %s", exception=traceback.format_exc()) except Exception as exc: module.fail_json(msg=to_text(exc), exception=traceback.format_exc()) # we don't do any locking as this should be a unique module/process if pid == 0: os.close(pipe[0]) # if command is string deal with py2 vs py3 conversions for shlex if not isinstance(cmd, list): if PY2: cmd = shlex.split(to_bytes(cmd, errors=errors)) else: cmd = shlex.split(to_text(cmd, errors=errors)) # make sure we always use byte strings run_cmd = [] for c in cmd: run_cmd.append(to_bytes(c, errors=errors)) # execute the command in forked process p = subprocess.Popen(run_cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE, preexec_fn=lambda: os.close(pipe[1])) fds = [p.stdout, p.stderr] # loop reading output till its done output = {p.stdout: b(""), p.stderr: b("")} while fds: rfd, wfd, efd = select.select(fds, [], fds, 1) if (rfd + wfd + efd) or p.poll(): for out in fds: if out in rfd: data = os.read(out.fileno(), chunk) if not data: fds.remove(out) output[out] += b(data) # even after fds close, we might want to wait for pid to die p.wait() # Return a pickled data of parent return_data = pickle.dumps([ p.returncode, to_text(output[p.stdout]), to_text(output[p.stderr]) ], protocol=pickle.HIGHEST_PROTOCOL) os.write(pipe[1], to_bytes(return_data, errors=errors)) # clean up os.close(pipe[1]) os._exit(0) elif pid == -1: module.fail_json( msg= "Unable to fork, no exception thrown, probably due to lack of resources, check logs." ) else: # in parent os.close(pipe[1]) os.waitpid(pid, 0) # Grab response data after child finishes return_data = b("") while True: rfd, wfd, efd = select.select([pipe[0]], [], [pipe[0]]) if pipe[0] in rfd: data = os.read(pipe[0], chunk) if not data: break return_data += b(data) # Note: no need to specify encoding on py3 as this module sends the # pickle to itself (thus same python interpreter so we aren't mixing # py2 and py3) return pickle.loads(to_bytes(return_data, errors=errors))
def main(): global module module = AnsibleModule( # not checking because of daisy chain to file module argument_spec=dict( src=dict(type='path'), _original_basename=dict( type='str' ), # used to handle 'dest is a directory' via template, a slight hack content=dict(type='str', no_log=True), dest=dict(type='path', required=True), backup=dict(type='bool', default=False), force=dict(type='bool', default=True, aliases=['thirsty']), validate=dict(type='str'), directory_mode=dict(type='raw'), remote_src=dict(type='bool'), local_follow=dict(type='bool'), checksum=dict(type='str'), ), add_file_common_args=True, supports_check_mode=True, ) if module.params.get('thirsty'): module.deprecate( 'The alias "thirsty" has been deprecated and will be removed, use "force" instead', version='2.13') src = module.params['src'] b_src = to_bytes(src, errors='surrogate_or_strict') dest = module.params['dest'] # Make sure we always have a directory component for later processing if os.path.sep not in dest: dest = '.{0}{1}'.format(os.path.sep, dest) b_dest = to_bytes(dest, errors='surrogate_or_strict') backup = module.params['backup'] force = module.params['force'] _original_basename = module.params.get('_original_basename', None) validate = module.params.get('validate', None) follow = module.params['follow'] local_follow = module.params['local_follow'] mode = module.params['mode'] owner = module.params['owner'] group = module.params['group'] remote_src = module.params['remote_src'] checksum = module.params['checksum'] if not os.path.exists(b_src): module.fail_json(msg="Source %s not found" % (src)) if not os.access(b_src, os.R_OK): module.fail_json(msg="Source %s not readable" % (src)) # Preserve is usually handled in the action plugin but mode + remote_src has to be done on the # remote host if module.params['mode'] == 'preserve': module.params['mode'] = '0%03o' % stat.S_IMODE(os.stat(b_src).st_mode) mode = module.params['mode'] checksum_dest = None if os.path.isfile(src): checksum_src = module.sha1(src) else: checksum_src = None # Backwards compat only. This will be None in FIPS mode try: if os.path.isfile(src): md5sum_src = module.md5(src) else: md5sum_src = None except ValueError: md5sum_src = None changed = False if checksum and checksum_src != checksum: module.fail_json( msg= 'Copied file does not match the expected checksum. Transfer failed.', checksum=checksum_src, expected_checksum=checksum) # Special handling for recursive copy - create intermediate dirs if _original_basename and dest.endswith(os.sep): dest = os.path.join(dest, _original_basename) b_dest = to_bytes(dest, errors='surrogate_or_strict') dirname = os.path.dirname(dest) b_dirname = to_bytes(dirname, errors='surrogate_or_strict') if not os.path.exists(b_dirname): try: (pre_existing_dir, new_directory_list) = split_pre_existing_dir(dirname) except AnsibleModuleError as e: e.result['msg'] += ' Could not copy to {0}'.format(dest) module.fail_json(**e.results) os.makedirs(b_dirname) directory_args = module.load_file_common_arguments(module.params) directory_mode = module.params["directory_mode"] if directory_mode is not None: directory_args['mode'] = directory_mode else: directory_args['mode'] = None adjust_recursive_directory_permissions(pre_existing_dir, new_directory_list, module, directory_args, changed) if os.path.isdir(b_dest): basename = os.path.basename(src) if _original_basename: basename = _original_basename dest = os.path.join(dest, basename) b_dest = to_bytes(dest, errors='surrogate_or_strict') if os.path.exists(b_dest): if os.path.islink(b_dest) and follow: b_dest = os.path.realpath(b_dest) dest = to_native(b_dest, errors='surrogate_or_strict') if not force: module.exit_json(msg="file already exists", src=src, dest=dest, changed=False) if os.access(b_dest, os.R_OK) and os.path.isfile(b_dest): checksum_dest = module.sha1(dest) else: if not os.path.exists(os.path.dirname(b_dest)): try: # os.path.exists() can return false in some # circumstances where the directory does not have # the execute bit for the current user set, in # which case the stat() call will raise an OSError os.stat(os.path.dirname(b_dest)) except OSError as e: if "permission denied" in to_native(e).lower(): module.fail_json( msg="Destination directory %s is not accessible" % (os.path.dirname(dest))) module.fail_json(msg="Destination directory %s does not exist" % (os.path.dirname(dest))) if not os.access(os.path.dirname(b_dest), os.W_OK) and not module.params['unsafe_writes']: module.fail_json(msg="Destination %s not writable" % (os.path.dirname(dest))) backup_file = None if checksum_src != checksum_dest or os.path.islink(b_dest): if not module.check_mode: try: if backup: if os.path.exists(b_dest): backup_file = module.backup_local(dest) # allow for conversion from symlink. if os.path.islink(b_dest): os.unlink(b_dest) open(b_dest, 'w').close() if validate: # if we have a mode, make sure we set it on the temporary # file source as some validations may require it if mode is not None: module.set_mode_if_different(src, mode, False) if owner is not None: module.set_owner_if_different(src, owner, False) if group is not None: module.set_group_if_different(src, group, False) if "%s" not in validate: module.fail_json(msg="validate must contain %%s: %s" % (validate)) (rc, out, err) = module.run_command(validate % src) if rc != 0: module.fail_json(msg="failed to validate", exit_status=rc, stdout=out, stderr=err) b_mysrc = b_src if remote_src and os.path.isfile(b_src): _, b_mysrc = tempfile.mkstemp(dir=os.path.dirname(b_dest)) shutil.copyfile(b_src, b_mysrc) try: shutil.copystat(b_src, b_mysrc) except OSError as err: if err.errno == errno.ENOSYS and mode == "preserve": module.warn("Unable to copy stats {0}".format( to_native(b_src))) else: raise # might be needed below if PY3 and hasattr(os, 'listxattr'): try: src_has_acls = 'system.posix_acl_access' in os.listxattr( src) except Exception as e: # assume unwanted ACLs by default src_has_acls = True module.atomic_move( b_mysrc, dest, unsafe_writes=module.params['unsafe_writes']) if PY3 and hasattr(os, 'listxattr') and platform.system( ) == 'Linux' and not remote_src: # atomic_move used above to copy src into dest might, in some cases, # use shutil.copy2 which in turn uses shutil.copystat. # Since Python 3.3, shutil.copystat copies file extended attributes: # https://docs.python.org/3/library/shutil.html#shutil.copystat # os.listxattr (along with others) was added to handle the operation. # This means that on Python 3 we are copying the extended attributes which includes # the ACLs on some systems - further limited to Linux as the documentation above claims # that the extended attributes are copied only on Linux. Also, os.listxattr is only # available on Linux. # If not remote_src, then the file was copied from the controller. In that # case, any filesystem ACLs are artifacts of the copy rather than preservation # of existing attributes. Get rid of them: if src_has_acls: # FIXME If dest has any default ACLs, there are not applied to src now because # they were overridden by copystat. Should/can we do anything about this? # 'system.posix_acl_default' in os.listxattr(os.path.dirname(b_dest)) try: clear_facls(dest) except ValueError as e: if 'setfacl' in to_native(e): # No setfacl so we're okay. The controller couldn't have set a facl # without the setfacl command pass else: raise except RuntimeError as e: # setfacl failed. if 'Operation not supported' in to_native(e): # The file system does not support ACLs. pass else: raise except (IOError, OSError): module.fail_json(msg="failed to copy: %s to %s" % (src, dest), traceback=traceback.format_exc()) changed = True else: changed = False if checksum_src is None and checksum_dest is None: if remote_src and os.path.isdir(module.params['src']): b_src = to_bytes(module.params['src'], errors='surrogate_or_strict') b_dest = to_bytes(module.params['dest'], errors='surrogate_or_strict') if src.endswith(os.path.sep) and os.path.isdir( module.params['dest']): diff_files_changed = copy_diff_files(b_src, b_dest, module) left_only_changed = copy_left_only(b_src, b_dest, module) common_dirs_changed = copy_common_dirs(b_src, b_dest, module) owner_group_changed = chown_recursive(b_dest, module) if diff_files_changed or left_only_changed or common_dirs_changed or owner_group_changed: changed = True if src.endswith( os.path.sep) and not os.path.exists(module.params['dest']): b_basename = to_bytes(os.path.basename(src), errors='surrogate_or_strict') b_dest = to_bytes(os.path.join(b_dest, b_basename), errors='surrogate_or_strict') b_src = to_bytes(os.path.join(module.params['src'], ""), errors='surrogate_or_strict') if not module.check_mode: shutil.copytree(b_src, b_dest, symlinks=not (local_follow)) chown_recursive(dest, module) changed = True if not src.endswith(os.path.sep) and os.path.isdir( module.params['dest']): b_basename = to_bytes(os.path.basename(src), errors='surrogate_or_strict') b_dest = to_bytes(os.path.join(b_dest, b_basename), errors='surrogate_or_strict') b_src = to_bytes(os.path.join(module.params['src'], ""), errors='surrogate_or_strict') if not module.check_mode and not os.path.exists(b_dest): shutil.copytree(b_src, b_dest, symlinks=not (local_follow)) changed = True chown_recursive(dest, module) if module.check_mode and not os.path.exists(b_dest): changed = True if os.path.exists(b_dest): diff_files_changed = copy_diff_files(b_src, b_dest, module) left_only_changed = copy_left_only(b_src, b_dest, module) common_dirs_changed = copy_common_dirs( b_src, b_dest, module) owner_group_changed = chown_recursive(b_dest, module) if diff_files_changed or left_only_changed or common_dirs_changed or owner_group_changed: changed = True if not src.endswith(os.path.sep) and not os.path.exists( module.params['dest']): b_basename = to_bytes(os.path.basename(module.params['src']), errors='surrogate_or_strict') b_dest = to_bytes(os.path.join(b_dest, b_basename), errors='surrogate_or_strict') if not module.check_mode and not os.path.exists(b_dest): os.makedirs(b_dest) b_src = to_bytes(os.path.join(module.params['src'], ""), errors='surrogate_or_strict') diff_files_changed = copy_diff_files(b_src, b_dest, module) left_only_changed = copy_left_only(b_src, b_dest, module) common_dirs_changed = copy_common_dirs( b_src, b_dest, module) owner_group_changed = chown_recursive(b_dest, module) if diff_files_changed or left_only_changed or common_dirs_changed or owner_group_changed: changed = True if module.check_mode and not os.path.exists(b_dest): changed = True res_args = dict(dest=dest, src=src, md5sum=md5sum_src, checksum=checksum_src, changed=changed) if backup_file: res_args['backup_file'] = backup_file module.params['dest'] = dest if not module.check_mode: file_args = module.load_file_common_arguments(module.params) res_args['changed'] = module.set_fs_attributes_if_different( file_args, res_args['changed']) module.exit_json(**res_args)
def send_msg(msg, server='localhost', port='6667', channel=None, nick_to=None, key=None, topic=None, nick="ansible", color='none', passwd=False, timeout=30, use_ssl=False, part=True, style=None): '''send message to IRC''' nick_to = [] if nick_to is None else nick_to colornumbers = { 'white': "00", 'black': "01", 'blue': "02", 'green': "03", 'red': "04", 'brown': "05", 'purple': "06", 'orange': "07", 'yellow': "08", 'light_green': "09", 'teal': "10", 'light_cyan': "11", 'light_blue': "12", 'pink': "13", 'gray': "14", 'light_gray': "15", } stylechoices = { 'bold': "\x02", 'underline': "\x1F", 'reverse': "\x16", 'italic': "\x1D", } try: styletext = stylechoices[style] except Exception: styletext = "" try: colornumber = colornumbers[color] colortext = "\x03" + colornumber except Exception: colortext = "" message = styletext + colortext + msg irc = socket.socket(socket.AF_INET, socket.SOCK_STREAM) if use_ssl: irc = ssl.wrap_socket(irc) irc.connect((server, int(port))) if passwd: irc.send(to_bytes('PASS %s\r\n' % passwd)) irc.send(to_bytes('NICK %s\r\n' % nick)) irc.send(to_bytes('USER %s %s %s :ansible IRC\r\n' % (nick, nick, nick))) motd = '' start = time.time() while 1: motd += to_native(irc.recv(1024)) # The server might send back a shorter nick than we specified (due to NICKLEN), # so grab that and use it from now on (assuming we find the 00[1-4] response). match = re.search(r'^:\S+ 00[1-4] (?P<nick>\S+) :', motd, flags=re.M) if match: nick = match.group('nick') break elif time.time() - start > timeout: raise Exception('Timeout waiting for IRC server welcome response') time.sleep(0.5) if key: irc.send(to_bytes('JOIN %s %s\r\n' % (channel, key))) else: irc.send(to_bytes('JOIN %s\r\n' % channel)) join = '' start = time.time() while 1: join += to_native(irc.recv(1024)) if re.search(r'^:\S+ 366 %s %s :' % (nick, channel), join, flags=re.M | re.I): break elif time.time() - start > timeout: raise Exception('Timeout waiting for IRC JOIN response') time.sleep(0.5) if topic is not None: irc.send(to_bytes('TOPIC %s :%s\r\n' % (channel, topic))) time.sleep(1) if nick_to: for nick in nick_to: irc.send(to_bytes('PRIVMSG %s :%s\r\n' % (nick, message))) if channel: irc.send(to_bytes('PRIVMSG %s :%s\r\n' % (channel, message))) time.sleep(1) if part: irc.send(to_bytes('PART %s\r\n' % channel)) irc.send(to_bytes('QUIT\r\n')) time.sleep(1) irc.close()
def main(): global module module = AnsibleModule( argument_spec=dict( state=dict(type='str', choices=[ 'absent', 'directory', 'file', 'hard', 'link', 'touch' ]), path=dict(type='path', required=True, aliases=['dest', 'name']), _original_basename=dict( type='str'), # Internal use only, for recursive ops recurse=dict(type='bool', default=False), force=dict(type='bool', default=False ), # Note: Should not be in file_common_args in future follow=dict( type='bool', default=True), # Note: Different default than file_common_args _diff_peek=dict( type='bool' ), # Internal use only, for internal checks in the action plugins src=dict(type='path' ), # Note: Should not be in file_common_args in future modification_time=dict(type='str'), modification_time_format=dict(type='str', default='%Y%m%d%H%M.%S'), access_time=dict(type='str'), access_time_format=dict(type='str', default='%Y%m%d%H%M.%S'), ), add_file_common_args=True, supports_check_mode=True, ) # When we rewrite basic.py, we will do something similar to this on instantiating an AnsibleModule sys.excepthook = _ansible_excepthook additional_parameter_handling(module.params) params = module.params state = params['state'] recurse = params['recurse'] force = params['force'] follow = params['follow'] path = params['path'] src = params['src'] timestamps = {} timestamps[ 'modification_time'] = keep_backward_compatibility_on_timestamps( params['modification_time'], state) timestamps['modification_time_format'] = params['modification_time_format'] timestamps['access_time'] = keep_backward_compatibility_on_timestamps( params['access_time'], state) timestamps['access_time_format'] = params['access_time_format'] # short-circuit for diff_peek if params['_diff_peek'] is not None: appears_binary = execute_diff_peek( to_bytes(path, errors='surrogate_or_strict')) module.exit_json(path=path, changed=False, appears_binary=appears_binary) if state == 'file': result = ensure_file_attributes(path, follow, timestamps) elif state == 'directory': result = ensure_directory(path, follow, recurse, timestamps) elif state == 'link': result = ensure_symlink(path, src, follow, force, timestamps) elif state == 'hard': result = ensure_hardlink(path, src, follow, force, timestamps) elif state == 'touch': result = execute_touch(path, follow, timestamps) elif state == 'absent': result = ensure_absent(path) module.exit_json(**result)