5from __future__
import absolute_import, division, print_function
27 'Z':
'compresseddirty',
57from collections
import deque
58from itertools
import chain, repeat
67 from systemd
import journal
82from ansible.module_utils.compat
import selectors
84from ._text
import to_native, to_bytes, to_text
87 container_to_bytes
as json_dict_unicode_to_bytes,
88 container_to_text
as json_dict_bytes_to_unicode,
100except ImportError
as e:
101 print(
'\n{{"msg": "Error: ansible requires the stdlib json: {0}", "failed": true}}'.
format(
to_native(e)))
105AVAILABLE_HASH_ALGORITHMS = dict()
110 for attribute
in (
'available_algorithms',
'algorithms'):
111 algorithms = getattr(hashlib, attribute,
None)
114 if algorithms
is None:
116 algorithms = (
'md5',
'sha1',
'sha224',
'sha256',
'sha384',
'sha512')
117 for algorithm
in algorithms:
118 AVAILABLE_HASH_ALGORITHMS[algorithm] = getattr(hashlib, algorithm)
124 AVAILABLE_HASH_ALGORITHMS.pop(
'md5',
None)
127 AVAILABLE_HASH_ALGORITHMS = {
'sha1': sha.sha}
130 AVAILABLE_HASH_ALGORITHMS[
'md5'] = md5.md5
136 Mapping, MutableMapping,
137 Sequence, MutableSequence,
142 _PERM_BITS
as PERM_BITS,
143 _EXEC_PERM_BITS
as EXEC_PERM_BITS,
144 _DEFAULT_PERM
as DEFAULT_PERM,
147 get_flags_from_attributes,
151 get_distribution_version,
152 get_platform_subclass,
173from ansible.module_utils.six.moves
import map, reduce, shlex_quote
175 check_missing_parameters,
176 check_mutually_exclusive,
177 check_required_arguments,
180 check_required_one_of,
181 check_required_together,
201SEQUENCETYPE = frozenset, KeysView, Sequence
203PASSWORD_MATCH = re.compile(
r'^(?:.+[-_\s])?pass(?:[-_\s]?(?:word|phrase|wrd|wd)?)(?:[-_\s].+)?$', re.I)
219 basestring = string_types
221_literal_eval = literal_eval
233 ''' Load value from environment '''
235 if arg
in os.environ:
236 return os.environ[arg]
237 raise AnsibleFallbackNotFound
240FILE_COMMON_ARGUMENTS = dict(
244 mode=dict(type=
'raw'),
251 attributes=dict(aliases=[
'attr']),
255 follow=dict(type=
'bool', default=
False),
256 force=dict(type=
'bool'),
261 content=dict(no_log=
True),
266 directory_mode=dict(),
267 unsafe_writes=dict(type=
'bool', default=
False, fallback=(env_fallback, [
'ANSIBLE_UNSAFE_WRITES'])),
270PASSWD_ARG_RE = re.compile(
r'^[-]{0,2}pass[-]?(word|wd)?')
273MODE_OPERATOR_RE = re.compile(
r'[+=-]')
274USERS_RE = re.compile(
r'[^ugo]')
275PERMS_RE = re.compile(
r'[^rwxXstugo]')
279_PY3_MIN = sys.version_info[:2] >= (3, 5)
280_PY2_MIN = (2, 6) <= sys.version_info[:2] < (3,)
281_PY_MIN = _PY3_MIN
or _PY2_MIN
284 '\n{"failed": true, '
285 '"msg": "Ansible requires a minimum of Python2 version 2.6 or Python3 version 3.5. Current version: %s"}' %
''.join(sys.version.splitlines())
296 **Deprecated** Use :py:func:`platform.system` directly.
298 :returns: Name of the platform the module is running on
in a native string
300 Returns a native string that labels the platform (
"Linux",
"Solaris", etc). Currently, this
is
301 the result of calling :py:func:`platform.system`.
303 return platform.system()
313 """**Deprecated**: Use ansible.module_utils.common.sys_info.get_platform_subclass instead"""
315 return super(cls, platform_cls).__new__(platform_cls)
319 """**Deprecated**: Use ansible.module_utils.common._utils.get_all_subclasses instead"""
320 return list(_get_all_subclasses(cls))
328 Helper function for :meth:`remove_values`.
330 :arg value: The value to check
for strings that need to be stripped
331 :arg no_log_strings: set of strings which must be stripped out of any values
332 :arg deferred_removals: List which holds information about nested
333 containers that have to be iterated
for removals. It
is passed into
334 this function so that more entries can be added to it
if value
is
335 a container type. The format of each entry
is a 2-tuple where the first
336 element
is the ``value`` parameter
and the second value
is a new
337 container to copy the elements of ``value`` into once iterated.
338 :returns:
if ``value``
is a scalar, returns ``value``
with two exceptions:
339 1. :
class:`~datetime.datetime` objects which are changed into a string representation.
340 2. objects which are
in no_log_strings are replaced
with a placeholder
341 so that no sensitive data
is leaked.
342 If ``value``
is a container type, returns a new empty container.
344 ``deferred_removals``
is added to
as a side-effect of this function.
346 .. warning:: It
is up to the caller to make sure the order
in which value
347 is passed
in is correct. For instance, higher level containers need
348 to be passed
in before lower level containers. For example, given
349 ``{
'level1': {
'level2':
'level3': [
True]} }`` first
pass in the
350 dictionary
for ``level1``, then the dict
for ``level2``,
and finally
351 the list
for ``level3``.
353 if isinstance(value, (text_type, binary_type)):
355 native_str_value = value
356 if isinstance(value, text_type):
359 native_str_value =
to_bytes(value, errors=
'surrogate_or_strict')
360 elif isinstance(value, binary_type):
361 value_is_text =
False
363 native_str_value =
to_text(value, errors=
'surrogate_or_strict')
365 if native_str_value
in no_log_strings:
366 return 'VALUE_SPECIFIED_IN_NO_LOG_PARAMETER'
367 for omit_me
in no_log_strings:
368 native_str_value = native_str_value.replace(omit_me,
'*' * 8)
370 if value_is_text
and isinstance(native_str_value, binary_type):
371 value =
to_text(native_str_value, encoding=
'utf-8', errors=
'surrogate_then_replace')
372 elif not value_is_text
and isinstance(native_str_value, text_type):
373 value =
to_bytes(native_str_value, encoding=
'utf-8', errors=
'surrogate_then_replace')
375 value = native_str_value
377 elif isinstance(value, Sequence):
378 if isinstance(value, MutableSequence):
379 new_value = type(value)()
382 deferred_removals.append((value, new_value))
385 elif isinstance(value, Set):
386 if isinstance(value, MutableSet):
387 new_value = type(value)()
390 deferred_removals.append((value, new_value))
393 elif isinstance(value, Mapping):
394 if isinstance(value, MutableMapping):
395 new_value = type(value)()
398 deferred_removals.append((value, new_value))
401 elif isinstance(value, tuple(
chain(integer_types, (float, bool, NoneType)))):
402 stringy_value =
to_native(value, encoding=
'utf-8', errors=
'surrogate_or_strict')
403 if stringy_value
in no_log_strings:
404 return 'VALUE_SPECIFIED_IN_NO_LOG_PARAMETER'
405 for omit_me
in no_log_strings:
406 if omit_me
in stringy_value:
407 return 'VALUE_SPECIFIED_IN_NO_LOG_PARAMETER'
409 elif isinstance(value, datetime.datetime):
410 value = value.isoformat()
412 raise TypeError(
'Value of unknown type: %s, %s' % (type(value), value))
418 """ Remove strings in no_log_strings from value. If value is a container
419 type, then remove a lot more.
421 Use of deferred_removals exists, rather than a pure recursive solution,
422 because of the potential to hit the maximum recursion depth when dealing
with
423 large amounts of data (see issue
426 deferred_removals = deque()
428 no_log_strings = [to_native(s, errors='surrogate_or_strict')
for s
in no_log_strings]
431 while deferred_removals:
432 old_data, new_data = deferred_removals.popleft()
433 if isinstance(new_data, Mapping):
434 for old_key, old_elem
in old_data.items():
436 new_data[old_key] = new_elem
438 for elem
in old_data:
440 if isinstance(new_data, MutableSequence):
441 new_data.append(new_elem)
442 elif isinstance(new_data, MutableSet):
443 new_data.add(new_elem)
445 raise TypeError(
'Unknown container type encountered when removing private values from output')
451 """ Helper method to sanitize_keys() to build deferred_removals and avoid deep recursion. """
452 if isinstance(value, (text_type, binary_type)):
455 if isinstance(value, Sequence):
456 if isinstance(value, MutableSequence):
457 new_value = type(value)()
460 deferred_removals.append((value, new_value))
463 if isinstance(value, Set):
464 if isinstance(value, MutableSet):
465 new_value = type(value)()
468 deferred_removals.append((value, new_value))
471 if isinstance(value, Mapping):
472 if isinstance(value, MutableMapping):
473 new_value = type(value)()
476 deferred_removals.append((value, new_value))
479 if isinstance(value, tuple(
chain(integer_types, (float, bool, NoneType)))):
482 if isinstance(value, (datetime.datetime, datetime.date)):
485 raise TypeError(
'Value of unknown type: %s, %s' % (type(value), value))
489 """ Sanitize the keys in a container object by removing no_log values from key names.
491 This is a companion function to the `
remove_values()` function. Similar to that function,
492 we make use of deferred_removals to avoid hitting maximum recursion depth
in cases of
493 large data structures.
495 :param obj: The container object to sanitize. Non-container objects are returned unmodified.
496 :param no_log_strings: A set of string values we do
not want logged.
497 :param ignore_keys: A set of string values of keys to
not sanitize.
499 :returns: An object
with sanitized keys.
502 deferred_removals = deque()
504 no_log_strings = [to_native(s, errors='surrogate_or_strict')
for s
in no_log_strings]
507 while deferred_removals:
508 old_data, new_data = deferred_removals.popleft()
510 if isinstance(new_data, Mapping):
511 for old_key, old_elem
in old_data.items():
512 if old_key
in ignore_keys
or old_key.startswith(
'_ansible'):
520 for elem
in old_data:
522 if isinstance(new_data, MutableSequence):
523 new_data.append(new_elem)
524 elif isinstance(new_data, MutableSet):
525 new_data.add(new_elem)
527 raise TypeError(
'Unknown container type encountered when removing private values from keys')
533 ''' Remove strings that look like passwords from log messages '''
554 end = data.rindex(
'@', 0, begin)
557 output.insert(0, data[0:begin])
566 begin = data.rindex(
'://', 0, sep_search_end)
573 sep = data.index(
':', begin + 3, end)
579 output.insert(0, data[0:begin])
582 sep_search_end = begin
586 output.insert(0, data[end:prev_begin])
587 output.insert(0,
'********')
588 output.insert(0, data[begin:sep + 1])
591 output =
''.join(output)
598 ''' read the modules parameters and store them globally.
600 This function may be needed for certain very dynamic custom modules which
601 want to process the parameters that are being handed the module. Since
602 this
is so closely tied to the implementation of modules we cannot
603 guarantee API stability
for it (it may change between versions) however we
604 will
try not to
break it gratuitously. It
is certainly more future-proof
605 to call this function
and consume its outputs than to implement the logic
606 inside it
as a copy
in your own code.
609 if _ANSIBLE_ARGS
is not None:
610 buffer = _ANSIBLE_ARGS
616 if len(sys.argv) > 1:
617 if os.path.isfile(sys.argv[1]):
618 fd = open(sys.argv[1],
'rb')
624 buffer = buffer.encode(
'utf-8', errors=
'surrogateescape')
628 buffer = sys.stdin.read()
630 buffer = sys.stdin.buffer.read()
631 _ANSIBLE_ARGS = buffer
634 params = json.loads(buffer.decode(
'utf-8'))
637 print(
'\n{"msg": "Error: Module unable to decode valid JSON on stdin. Unable to figure out what parameters were passed", "failed": true}')
641 params = json_dict_unicode_to_bytes(params)
644 return params[
'ANSIBLE_MODULE_ARGS']
648 print(
'\n{"msg": "Error: Module unable to locate ANSIBLE_MODULE_ARGS in json data from stdin. Unable to figure out what parameters were passed", '
654 hostname = platform.node()
655 msg =
"Failed to import the required Python library (%s) on %s's Python %s." % (library, hostname, sys.executable)
657 msg +=
" This is required %s." % reason
659 msg +=
" See %s for more info." % url
661 msg += (
" Please read module documentation and install in the appropriate location."
662 " If the required library is installed, but Ansible is using the wrong Python interpreter,"
663 " please consult the documentation on ansible_python_interpreter")
671class AnsibleModule(object):
672 def __init__(self, argument_spec, bypass_checks=False, no_log=False,
673 check_invalid_arguments=None, mutually_exclusive=None, required_together=None,
674 required_one_of=None, add_file_common_args=False, supports_check_mode=False,
675 required_if=None, required_by=None):
678 Common code for quickly building an ansible module
in Python
679 (although you can write modules
with anything that can
return JSON).
681 See :ref:`developing_modules_general`
for a general introduction
682 and :ref:`developing_program_flow_modules`
for more detailed explanation.
685 self._name_name = os.path.basename(__file__)
693 if check_invalid_arguments
is None:
694 check_invalid_arguments =
True
695 module_set_check_invalid_arguments =
False
697 module_set_check_invalid_arguments =
True
724 if add_file_common_args:
725 for k, v
in FILE_COMMON_ARGUMENTS.items():
738 except (ValueError, TypeError)
as e:
740 print(
'\n{"failed": true, "msg": "Module alias error: %s"}' %
to_native(e))
752 if not bypass_checks:
771 if not bypass_checks:
795 if module_set_check_invalid_arguments
and self.
_name_name
not in (
'uri',
'zfs'):
796 self.
deprecatedeprecate(
'Setting check_invalid_arguments is deprecated and will be removed.'
797 ' Update the code for this module In the future, AnsibleModule will'
798 ' always check for invalid arguments.', version=
'2.9')
805 if self.
_tmpdir_tmpdir
is None:
808 if self._remote_tmp
is not None:
809 basedir = os.path.expanduser(os.path.expandvars(self._remote_tmp))
811 if basedir
is not None and not os.path.exists(basedir):
813 os.makedirs(basedir, mode=0o700)
814 except (OSError, IOError)
as e:
815 self.
warnwarn(
"Unable to use %s as temporary directory, "
816 "failing back to system: %s" % (basedir,
to_native(e)))
819 self.
warnwarn(
"Module remote_tmp %s did not exist and was "
820 "created with a mode of 0700, this may cause"
821 " issues when running as another user. To "
822 "avoid this, create the remote_tmp dir with "
823 "the correct permissions manually" % basedir)
825 basefile =
"ansible-moduletmp-%s-" % time.time()
827 tmpdir = tempfile.mkdtemp(prefix=basefile, dir=basedir)
828 except (OSError, IOError)
as e:
830 msg=
"Failed to create remote module tmp path at dir %s "
831 "with prefix %s: %s" % (basedir, basefile,
to_native(e))
833 if not self._keep_remote_files:
834 atexit.register(shutil.rmtree, tmpdir)
841 if isinstance(warning, string_types):
843 self.
loglog(
'[WARNING] %s' % warning)
845 raise TypeError(
"warn requires a string not a %s" % type(warning))
847 def deprecate(self, msg, version=None, date=None, collection_name=None):
851 if isinstance(msg, string_types):
856 self.
loglog(
'[DEPRECATION WARNING] %s %s' % (msg, version))
858 raise TypeError(
"deprecate requires a string not a %s" % type(msg))
862 many modules deal with files, this encapsulates common
863 options that the file module accepts such that it
is directly
864 available to all modules
and they can share code.
867 path = params.get('path', params.get(
'dest',
None))
871 path = os.path.expanduser(os.path.expandvars(path))
873 b_path =
to_bytes(path, errors=
'surrogate_or_strict')
876 if params.get(
'follow',
False)
and os.path.islink(b_path):
877 b_path = os.path.realpath(b_path)
880 mode = params.get(
'mode',
None)
881 owner = params.get(
'owner',
None)
882 group = params.get(
'group',
None)
885 seuser = params.get(
'seuser',
None)
886 serole = params.get(
'serole',
None)
887 setype = params.get(
'setype',
None)
888 selevel = params.get(
'selevel',
None)
889 secontext = [seuser, serole, setype]
892 secontext.append(selevel)
895 for i
in range(len(default_secontext)):
896 if i
is not None and secontext[i] ==
'_default':
897 secontext[i] = default_secontext[i]
899 attributes = params.get(
'attributes',
None)
901 path=path, mode=mode, owner=owner, group=group,
902 seuser=seuser, serole=serole, setype=setype,
903 selevel=selevel, secontext=secontext, attributes=attributes,
915 if selinux.is_selinux_mls_enabled() == 1:
922 seenabled = self.
get_bin_pathget_bin_path(
'selinuxenabled')
923 if seenabled
is not None:
924 (rc, out, err) = self.
run_commandrun_command(seenabled)
926 self.
fail_jsonfail_json(msg=
"Aborting, target uses selinux but python bindings (libselinux-python) aren't installed!")
928 if selinux.is_selinux_enabled() == 1:
935 context = [
None,
None,
None]
946 ret = selinux.matchpathcon(
to_native(path, errors=
'surrogate_or_strict'), mode)
953 context = ret[1].
split(
':', 3)
961 ret = selinux.lgetfilecon_raw(
to_native(path, errors=
'surrogate_or_strict'))
963 if e.errno == errno.ENOENT:
964 self.
fail_jsonfail_json(path=path, msg=
'path %s does not exist' % path)
966 self.
fail_jsonfail_json(path=path, msg=
'failed to retrieve selinux context')
971 context = ret[1].
split(
':', 3)
975 b_path =
to_bytes(path, errors=
'surrogate_or_strict')
977 b_path = os.path.expanduser(os.path.expandvars(b_path))
978 st = os.lstat(b_path)
984 path_is_bytes =
False
985 if isinstance(path, binary_type):
988 b_path = os.path.realpath(
to_bytes(os.path.expanduser(os.path.expandvars(path)), errors=
'surrogate_or_strict'))
989 while not os.path.ismount(b_path):
990 b_path = os.path.dirname(b_path)
995 return to_text(b_path, errors=
'surrogate_or_strict')
999 Returns a tuple containing (True, selinux_context)
if the given path
is on a
1000 NFS
or other
'special' fs mount point, otherwise the
return will be (
False,
None).
1003 f = open(
'/proc/mounts',
'r')
1004 mount_data = f.readlines()
1007 return (
False,
None)
1011 for line
in mount_data:
1012 (device, mount_point, fstype, options, rest) = line.split(
' ', 4)
1014 for fs
in self._selinux_special_fs:
1016 special_context = self.
selinux_contextselinux_context(path_mount_point)
1017 return (
True, special_context)
1019 return (
False,
None)
1036 new_context = list(cur_context)
1042 new_context = sp_context
1044 for i
in range(len(cur_context)):
1045 if len(context) > i:
1046 if context[i]
is not None and context[i] != cur_context[i]:
1047 new_context[i] = context[i]
1048 elif context[i]
is None:
1049 new_context[i] = cur_context[i]
1051 if cur_context != new_context:
1052 if diff
is not None:
1053 if 'before' not in diff:
1055 diff[
'before'][
'secontext'] = cur_context
1056 if 'after' not in diff:
1058 diff[
'after'][
'secontext'] = new_context
1063 rc = selinux.lsetfilecon(
to_native(path),
':'.join(new_context))
1064 except OSError
as e:
1066 new_context=new_context, cur_context=cur_context, input_was=context)
1068 self.
fail_jsonfail_json(path=path, msg=
'set selinux context failed')
1077 b_path =
to_bytes(path, errors=
'surrogate_or_strict')
1079 b_path = os.path.expanduser(os.path.expandvars(b_path))
1084 orig_uid, orig_gid = self.
user_and_groupuser_and_group(b_path, expand)
1089 uid = pwd.getpwnam(owner).pw_uid
1092 self.
fail_jsonfail_json(path=path, msg=
'chown failed: failed to look up user %s' % owner)
1095 if diff
is not None:
1096 if 'before' not in diff:
1098 diff[
'before'][
'owner'] = orig_uid
1099 if 'after' not in diff:
1101 diff[
'after'][
'owner'] = uid
1106 os.lchown(b_path, uid, -1)
1107 except (IOError, OSError)
as e:
1118 b_path =
to_bytes(path, errors=
'surrogate_or_strict')
1120 b_path = os.path.expanduser(os.path.expandvars(b_path))
1125 orig_uid, orig_gid = self.
user_and_groupuser_and_group(b_path, expand)
1130 gid = grp.getgrnam(group).gr_gid
1133 self.
fail_jsonfail_json(path=path, msg=
'chgrp failed: failed to look up group %s' % group)
1136 if diff
is not None:
1137 if 'before' not in diff:
1139 diff[
'before'][
'group'] = orig_gid
1140 if 'after' not in diff:
1142 diff[
'after'][
'group'] = gid
1147 os.lchown(b_path, -1, gid)
1150 self.
fail_jsonfail_json(path=path, msg=
'chgrp failed')
1159 b_path =
to_bytes(path, errors=
'surrogate_or_strict')
1161 b_path = os.path.expanduser(os.path.expandvars(b_path))
1162 path_stat = os.lstat(b_path)
1167 if not isinstance(mode, int):
1173 except Exception
as e:
1176 msg=
"mode must be in octal or symbolic form",
1179 if mode != stat.S_IMODE(mode):
1182 self.
fail_jsonfail_json(path=path, msg=
"Invalid mode supplied, only permission info is allowed", details=mode)
1184 prev_mode = stat.S_IMODE(path_stat.st_mode)
1186 if prev_mode != mode:
1188 if diff
is not None:
1189 if 'before' not in diff:
1191 diff[
'before'][
'mode'] =
'0%03o' % prev_mode
1192 if 'after' not in diff:
1194 diff[
'after'][
'mode'] =
'0%03o' % mode
1201 if hasattr(os,
'lchmod'):
1202 os.lchmod(b_path, mode)
1204 if not os.path.islink(b_path):
1205 os.chmod(b_path, mode)
1210 underlying_stat = os.stat(b_path)
1211 os.chmod(b_path, mode)
1212 new_underlying_stat = os.stat(b_path)
1213 if underlying_stat.st_mode != new_underlying_stat.st_mode:
1214 os.chmod(b_path, stat.S_IMODE(underlying_stat.st_mode))
1215 except OSError
as e:
1216 if os.path.islink(b_path)
and e.errno
in (
1222 elif e.errno
in (errno.ENOENT, errno.ELOOP):
1226 except Exception
as e:
1229 exception=traceback.format_exc())
1231 path_stat = os.lstat(b_path)
1232 new_mode = stat.S_IMODE(path_stat.st_mode)
1234 if new_mode != prev_mode:
1240 if attributes
is None:
1243 b_path =
to_bytes(path, errors=
'surrogate_or_strict')
1245 b_path = os.path.expanduser(os.path.expandvars(b_path))
1253 if attributes.startswith((
'-',
'+')):
1254 attr_mod = attributes[0]
1255 attributes = attributes[1:]
1257 if existing.get(
'attr_flags',
'') != attributes
or attr_mod ==
'-':
1260 attrcmd = [attrcmd,
'%s%s' % (attr_mod, attributes), b_path]
1263 if diff
is not None:
1264 if 'before' not in diff:
1266 diff[
'before'][
'attributes'] = existing.get(
'attr_flags')
1267 if 'after' not in diff:
1269 diff[
'after'][
'attributes'] =
'%s%s' % (attr_mod, attributes)
1273 rc, out, err = self.
run_commandrun_command(attrcmd)
1275 raise Exception(
"Error while setting attributes: %s" % (out + err))
1276 except Exception
as e:
1278 details=
to_native(e), exception=traceback.format_exc())
1283 attrcmd = self.
get_bin_pathget_bin_path(
'lsattr',
False)
1285 attrcmd = [attrcmd,
'-vd', path]
1287 rc, out, err = self.
run_commandrun_command(attrcmd)
1290 output[
'attr_flags'] = res[1].
replace(
'-',
'').strip()
1291 output[
'version'] = res[0].strip()
1300 This enables symbolic chmod string parsing as stated
in the chmod man-page
1302 This includes things like:
"u=rw-x+X,g=r-x+X,o=r-x+X"
1305 new_mode = stat.S_IMODE(path_stat.st_mode)
1308 for mode
in symbolic_mode.split(
','):
1311 permlist = MODE_OPERATOR_RE.split(mode)
1314 opers = MODE_OPERATOR_RE.findall(mode)
1319 users = permlist.pop(0)
1320 use_umask = (users ==
'')
1321 if users ==
'a' or users ==
'':
1326 if USERS_RE.match(users):
1327 raise ValueError(
"bad symbolic permission for mode: %s" % mode)
1331 for idx, perms
in enumerate(permlist):
1333 if PERMS_RE.match(perms):
1334 raise ValueError(
"bad symbolic permission for mode: %s" % mode)
1346 mask = stat.S_IRWXU | stat.S_ISUID
1348 mask = stat.S_IRWXG | stat.S_ISGID
1350 mask = stat.S_IRWXO | stat.S_ISVTX
1353 inverse_mask = mask ^ PERM_BITS
1354 new_mode = (current_mode & inverse_mask) | mode_to_apply
1355 elif operator ==
'+':
1356 new_mode = current_mode | mode_to_apply
1357 elif operator ==
'-':
1358 new_mode = current_mode - (current_mode & mode_to_apply)
1363 prev_mode = stat.S_IMODE(path_stat.st_mode)
1365 is_directory = stat.S_ISDIR(path_stat.st_mode)
1366 has_x_permissions = (prev_mode & EXEC_PERM_BITS) > 0
1367 apply_X_permission = is_directory
or has_x_permissions
1374 rev_umask = umask ^ PERM_BITS
1378 if apply_X_permission:
1380 'u': {
'X': stat.S_IXUSR},
1381 'g': {
'X': stat.S_IXGRP},
1382 'o': {
'X': stat.S_IXOTH},
1391 user_perms_to_modes = {
1393 'r': rev_umask & stat.S_IRUSR
if use_umask
else stat.S_IRUSR,
1394 'w': rev_umask & stat.S_IWUSR
if use_umask
else stat.S_IWUSR,
1395 'x': rev_umask & stat.S_IXUSR
if use_umask
else stat.S_IXUSR,
1398 'u': prev_mode & stat.S_IRWXU,
1399 'g': (prev_mode & stat.S_IRWXG) << 3,
1400 'o': (prev_mode & stat.S_IRWXO) << 6},
1402 'r': rev_umask & stat.S_IRGRP
if use_umask
else stat.S_IRGRP,
1403 'w': rev_umask & stat.S_IWGRP
if use_umask
else stat.S_IWGRP,
1404 'x': rev_umask & stat.S_IXGRP
if use_umask
else stat.S_IXGRP,
1407 'u': (prev_mode & stat.S_IRWXU) >> 3,
1408 'g': prev_mode & stat.S_IRWXG,
1409 'o': (prev_mode & stat.S_IRWXO) << 3},
1411 'r': rev_umask & stat.S_IROTH
if use_umask
else stat.S_IROTH,
1412 'w': rev_umask & stat.S_IWOTH
if use_umask
else stat.S_IWOTH,
1413 'x': rev_umask & stat.S_IXOTH
if use_umask
else stat.S_IXOTH,
1416 'u': (prev_mode & stat.S_IRWXU) >> 6,
1417 'g': (prev_mode & stat.S_IRWXG) >> 3,
1418 'o': prev_mode & stat.S_IRWXO},
1422 for key, value
in X_perms.items():
1423 user_perms_to_modes[key].
update(value)
1425 def or_reduce(mode, perm):
1426 return mode | user_perms_to_modes[user][perm]
1428 return reduce(or_reduce, perms, 0)
1433 file_args[
'path'], file_args[
'secontext'], changed, diff
1436 file_args[
'path'], file_args[
'owner'], changed, diff, expand
1439 file_args[
'path'], file_args[
'group'], changed, diff, expand
1442 file_args[
'path'], file_args[
'mode'], changed, diff, expand
1445 file_args[
'path'], file_args[
'attributes'], changed, diff, expand
1450 return self.
check_modecheck_mode
and not os.path.exists(file_path)
1460 for results that are files, supplement the info about the file
1461 in the
return path
with stats about the file path.
1464 path = kwargs.get('path', kwargs.get(
'dest',
None))
1467 b_path =
to_bytes(path, errors=
'surrogate_or_strict')
1468 if os.path.exists(b_path):
1473 user = pwd.getpwuid(uid)[0]
1477 group = grp.getgrgid(gid)[0]
1480 kwargs[
'owner'] = user
1481 kwargs[
'group'] = group
1482 st = os.lstat(b_path)
1483 kwargs[
'mode'] =
'0%03o' % stat.S_IMODE(st[stat.ST_MODE])
1485 if os.path.islink(b_path):
1486 kwargs[
'state'] =
'link'
1487 elif os.path.isdir(b_path):
1488 kwargs[
'state'] =
'directory'
1489 elif os.stat(b_path).st_nlink > 1:
1490 kwargs[
'state'] =
'hard'
1492 kwargs[
'state'] =
'file'
1494 kwargs[
'secontext'] =
':'.join(self.
selinux_contextselinux_context(path))
1495 kwargs[
'size'] = st[stat.ST_SIZE]
1500 Uses the locale module to test the currently set locale
1501 (per the LANG and LC_CTYPE environment settings)
1506 locale.setlocale(locale.LC_ALL,
'')
1507 except locale.Error:
1511 locale.setlocale(locale.LC_ALL,
'C')
1512 os.environ[
'LANG'] =
'C'
1513 os.environ[
'LC_ALL'] =
'C'
1514 os.environ[
'LC_MESSAGES'] =
'C'
1515 except Exception
as e:
1516 self.
fail_jsonfail_json(msg=
"An unknown error was encountered while attempting to validate the locale: %s" %
1517 to_native(e), exception=traceback.format_exc())
1523 param = self.
paramsparams
1528 for option, alias
in alias_warnings:
1529 self.
_warnings_warnings.append(
'Both option %s and its alias %s are set.' % (option_prefix + option, option_prefix + alias))
1531 deprecated_aliases = []
1532 for i
in spec.keys():
1533 if 'deprecated_aliases' in spec[i].keys():
1534 for alias
in spec[i][
'deprecated_aliases']:
1535 deprecated_aliases.append(alias)
1537 for deprecation
in deprecated_aliases:
1538 if deprecation[
'name']
in param.keys():
1540 {
'msg':
"Alias '%s' is deprecated. See the module docs for more information" % deprecation[
'name'],
1541 'version': deprecation.get(
'version')})
1542 return alias_results
1548 param = self.
paramsparams
1552 except TypeError
as te:
1553 self.
fail_jsonfail_json(msg=
"Failure when processing no_log parameters. Module invocation will be hidden. "
1554 "%s" %
to_native(te), invocation={
'module_args':
'HIDDEN DUE TO FAILURE'})
1559 unsupported_parameters = set()
1563 param = self.
paramsparams
1564 if legal_inputs
is None:
1567 for k
in list(param.keys()):
1569 if check_invalid_arguments
and k
not in legal_inputs:
1570 unsupported_parameters.add(k)
1574 param_key =
'_ansible_%s' % k
1575 if param_key
in param:
1577 setattr(self, PASS_VARS[k][0], self.
booleanboolean(param[param_key]))
1579 setattr(self, PASS_VARS[k][0], param[param_key])
1582 if param_key
in self.
paramsparams:
1583 del self.
paramsparams[param_key]
1586 if not hasattr(self, PASS_VARS[k][0]):
1587 setattr(self, PASS_VARS[k][0], PASS_VARS[k][1])
1589 if unsupported_parameters:
1590 msg =
"Unsupported parameters for (%s) module: %s" % (self.
_name_name,
', '.join(sorted(list(unsupported_parameters))))
1592 msg +=
" found in %s." %
" -> ".join(self.
_options_context_options_context)
1593 msg +=
" Supported parameters include: %s" % (
', '.join(sorted(spec.keys())))
1596 self.
exit_jsonexit_json(skipped=
True, msg=
"remote module (%s) does not support check mode" % self.
_name_name)
1600 param = self.
paramsparams
1605 param = self.
paramsparams
1609 except TypeError
as e:
1620 param = self.
paramsparams
1624 except TypeError
as e:
1634 param = self.
paramsparams
1638 except TypeError
as e:
1648 param = self.
paramsparams
1652 except TypeError
as e:
1659 param = self.
paramsparams
1663 except TypeError
as e:
1670 ''' ensure that parameters which conditionally required are present '''
1674 param = self.
paramsparams
1678 except TypeError
as e:
1685 ''' ensure all arguments have the requested values, and there are no stray arguments '''
1689 param = self.
paramsparams
1690 for (k, v)
in spec.items():
1691 choices = v.get(
'choices',
None)
1694 if isinstance(choices, SEQUENCETYPE)
and not isinstance(choices, (binary_type, text_type)):
1697 if isinstance(param[k], list):
1698 diff_list =
", ".join([item
for item
in param[k]
if item
not in choices])
1700 choices_str =
", ".join([
to_native(c)
for c
in choices])
1701 msg =
"value of %s must be one or more of: %s. Got no match for: %s" % (k, choices_str, diff_list)
1705 elif param[k]
not in choices:
1708 lowered_choices =
None
1709 if param[k] ==
'False':
1711 overlap = BOOLEANS_FALSE.intersection(choices)
1712 if len(overlap) == 1:
1714 (param[k],) = overlap
1716 if param[k] ==
'True':
1717 if lowered_choices
is None:
1719 overlap = BOOLEANS_TRUE.intersection(choices)
1720 if len(overlap) == 1:
1721 (param[k],) = overlap
1723 if param[k]
not in choices:
1724 choices_str =
", ".join([
to_native(c)
for c
in choices])
1725 msg =
"value of %s must be one of: %s, got: %s" % (k, choices_str, param[k])
1730 msg =
"internal error: choices for argument %s are not iterable: %s" % (k, choices)
1735 def safe_eval(self, value, locals=None, include_exceptions=False):
1736 return safe_eval(value, locals, include_exceptions)
1750 common_msg =
'quote the entire value to ensure it does not change.'
1752 msg = common_msg.capitalize()
1755 msg = (
'The value {0!r} (type {0.__class__.__name__}) in a string field was converted to {1!r} (type string). '
1756 'If this does not look like what you expect, {2}').
format(value,
to_text(value), common_msg)
1758 return to_native(value, errors=
'surrogate_or_strict')
1791 ''' deal with options to create sub spec '''
1792 if argument_spec
is None:
1795 params = self.
paramsparams
1797 for (k, v)
in argument_spec.items():
1798 wanted = v.get(
'type',
None)
1799 if wanted ==
'dict' or (wanted ==
'list' and v.get(
'elements',
'') ==
'dict'):
1800 spec = v.get(
'options',
None)
1801 if v.get(
'apply_defaults',
False):
1802 if spec
is not None:
1803 if params.get(k)
is None:
1807 elif spec
is None or k
not in params
or params[k]
is None:
1812 if isinstance(params[k], dict):
1813 elements = [params[k]]
1815 elements = params[k]
1817 for idx, param
in enumerate(elements):
1818 if not isinstance(param, dict):
1819 self.
fail_jsonfail_json(msg=
"value of %s must be of type dict or list of dict" % k)
1821 new_prefix = prefix + k
1822 if wanted ==
'list':
1823 new_prefix +=
'[%d]' % idx
1827 options_aliases = self.
_handle_aliases_handle_aliases(spec, param, option_prefix=new_prefix)
1829 options_legal_inputs = list(spec.keys()) + list(options_aliases.keys())
1837 self.
_set_defaults_set_defaults(pre=
True, spec=spec, param=param)
1849 self.
_set_defaults_set_defaults(pre=
False, spec=spec, param=param)
1865 self.
fail_jsonfail_json(msg=
"implementation error: unknown type %s requested for %s" % (wanted, k))
1868 type_checker = wanted
1869 wanted = getattr(wanted,
'__name__',
to_native(type(wanted)))
1871 return type_checker, wanted
1874 type_checker, wanted_name = self.
_get_wanted_type_get_wanted_type(wanted, param)
1875 validated_params = []
1876 for value
in values:
1878 validated_params.append(type_checker(value))
1879 except (TypeError, ValueError)
as e:
1880 msg =
"Elements value for option %s" % param
1882 msg +=
" found in '%s'" %
" -> ".join(self.
_options_context_options_context)
1883 msg +=
" is of type %s and we were unable to convert to %s: %s" % (type(value), wanted_name,
to_native(e))
1885 return validated_params
1888 ''' ensure all arguments have the requested type '''
1893 param = self.
paramsparams
1895 for (k, v)
in spec.items():
1896 wanted = v.get(
'type',
None)
1904 type_checker, wanted_name = self.
_get_wanted_type_get_wanted_type(wanted, k)
1906 param[k] = type_checker(value)
1907 wanted_elements = v.get(
'elements',
None)
1909 if wanted !=
'list' or not isinstance(param[k], list):
1910 msg =
"Invalid type %s for option '%s'" % (wanted_name, param)
1912 msg +=
" found in '%s'." %
" -> ".join(self.
_options_context_options_context)
1913 msg +=
", elements value check is supported only with 'list' type"
1915 param[k] = self.
_handle_elements_handle_elements(wanted_elements, k, param[k])
1917 except (TypeError, ValueError)
as e:
1918 msg =
"argument %s is of type %s" % (k, type(value))
1920 msg +=
" found in '%s'." %
" -> ".join(self.
_options_context_options_context)
1921 msg +=
" and we were unable to convert to %s: %s" % (wanted_name,
to_native(e))
1928 param = self.
paramsparams
1929 for (k, v)
in spec.items():
1930 default = v.get(
'default',
None)
1934 if k
not in param
and (default
is not None or not pre):
1936 if v.get(
'no_log',
False)
and default:
1945 param = self.
paramsparams
1947 for (k, v)
in spec.items():
1948 fallback = v.get(
'fallback', (
None,))
1949 fallback_strategy = fallback[0]
1951 fallback_kwargs = {}
1952 if k
not in param
and fallback_strategy
is not None:
1953 for item
in fallback[1:]:
1954 if isinstance(item, dict):
1955 fallback_kwargs = item
1957 fallback_args = item
1959 fallback_value = fallback_strategy(*fallback_args, **fallback_kwargs)
1960 except AnsibleFallbackNotFound:
1963 if v.get(
'no_log',
False)
and fallback_value:
1965 param[k] = fallback_value
1968 ''' read the input and set the params attribute.
1970 This method is for backwards compatibility. The guts of the function
1971 were moved out
in 2.1 so that custom modules could read the parameters.
1978 module =
'ansible-%s' % self.
_name_name
1979 facility = getattr(syslog, self.
_syslog_facility_syslog_facility, syslog.LOG_USER)
1980 syslog.openlog(str(module), 0, facility)
1981 syslog.syslog(syslog.LOG_INFO, msg)
1985 self.
loglog(
'[debug] %s' % msg)
1987 def log(self, msg, log_args=None):
1989 if not self.
no_logno_log:
1991 if log_args
is None:
1994 module =
'ansible-%s' % self.
_name_name
1995 if isinstance(module, binary_type):
1996 module = module.decode(
'utf-8',
'replace')
1999 if not isinstance(msg, (binary_type, text_type)):
2000 raise TypeError(
"msg should be a string (got %s)" % type(msg))
2004 if isinstance(msg, binary_type):
2011 syslog_msg = journal_msg
2013 syslog_msg = journal_msg.encode(
'utf-8',
'replace')
2016 journal_args = [(
"MODULE", os.path.basename(__file__))]
2017 for arg
in log_args:
2018 journal_args.append((arg.upper(), str(log_args[arg])))
2024 facility = getattr(syslog,
2026 syslog.LOG_USER) >> 3
2027 journal.send(MESSAGE=
u"%s %s" % (module, journal_msg),
2028 SYSLOG_FACILITY=facility,
2029 **dict(journal_args))
2031 journal.send(MESSAGE=
u"%s %s" % (module, journal_msg),
2032 **dict(journal_args))
2040 ''' log that ansible ran the module '''
2045 for param
in self.
paramsparams:
2046 canon = self.
aliasesaliases.
get(param, param)
2048 no_log = arg_opts.get(
'no_log',
None)
2051 if no_log
is None and PASSWORD_MATCH.search(param):
2052 log_args[param] =
'NOT_LOGGING_PASSWORD'
2053 self.
warnwarn(
'Module did not set no_log for %s' % param)
2054 elif self.
booleanboolean(no_log):
2055 log_args[param] =
'NOT_LOGGING_PARAMETER'
2057 param_val = self.
paramsparams[param]
2058 if not isinstance(param_val, (text_type, binary_type)):
2059 param_val = str(param_val)
2060 elif isinstance(param_val, text_type):
2061 param_val = param_val.encode(
'utf-8')
2066 msg =
'Invoked with %s' %
' '.join(msg)
2070 self.
loglog(msg, log_args=log_args)
2075 if not os.access(cwd, os.F_OK | os.R_OK):
2081 for cwd
in [self.
tmpdirtmpdir, os.path.expandvars(
'$HOME'), tempfile.gettempdir()]:
2083 if os.access(cwd, os.F_OK | os.R_OK):
2094 Find system executable in PATH.
2096 :param arg: The executable to find.
2097 :param required:
if executable
is not found
and required
is ``
True``, fail_json
2098 :param opt_dirs: optional list of directories to search
in addition to ``PATH``
2099 :returns:
if found
return full path; otherwise
return None
2105 except ValueError
as e:
2111 '''Convert the argument to a boolean'''
2117 except TypeError
as e:
2123 except UnicodeError
as e:
2127 return json.loads(data)
2141 if 'invocation' not in kwargs:
2142 kwargs[
'invocation'] = {
'module_args': self.
paramsparams}
2144 if 'warnings' in kwargs:
2145 if isinstance(kwargs[
'warnings'], list):
2146 for w
in kwargs[
'warnings']:
2149 self.
warnwarn(kwargs[
'warnings'])
2152 kwargs[
'warnings'] = self.
_warnings_warnings
2154 if 'deprecations' in kwargs:
2155 if isinstance(kwargs[
'deprecations'], list):
2156 for d
in kwargs[
'deprecations']:
2157 if isinstance(d, SEQUENCETYPE)
and len(d) == 2:
2158 self.
deprecatedeprecate(d[0], version=d[1])
2159 elif isinstance(d, Mapping):
2160 self.
deprecatedeprecate(d[
'msg'], version=d.get(
'version',
None))
2164 self.
deprecatedeprecate(kwargs[
'deprecations'])
2170 print(
'\n%s' % self.
jsonifyjsonify(kwargs))
2173 ''' return from the module, without error '''
2180 ''' return from the module, with an error message '''
2182 if 'msg' not in kwargs:
2183 raise AssertionError(
"implementation error -- msg to explain the error is required")
2184 kwargs[
'failed'] =
True
2188 if 'exception' not in kwargs
and sys.exc_info()[2]
and (self.
_debug_debug
or self.
_verbosity_verbosity >= 3):
2191 kwargs[
'exception'] =
'WARNING: The below traceback may *not* be related to the actual failure.\n' +\
2192 ''.join(traceback.format_tb(sys.exc_info()[2]))
2194 kwargs[
'exception'] =
''.join(traceback.format_tb(sys.exc_info()[2]))
2201 if not required_params:
2205 except TypeError
as e:
2209 ''' Return hex digest of local file for a digest_method specified by name, or None if file is not present. '''
2210 b_filename =
to_bytes(filename, errors=
'surrogate_or_strict')
2212 if not os.path.exists(b_filename):
2214 if os.path.isdir(b_filename):
2215 self.
fail_jsonfail_json(msg=
"attempted to take checksum of directory: %s" % filename)
2218 if hasattr(algorithm,
'hexdigest'):
2219 digest_method = algorithm
2222 digest_method = AVAILABLE_HASH_ALGORITHMS[algorithm]()
2224 self.
fail_jsonfail_json(msg=
"Could not hash file '%s' with algorithm '%s'. Available algorithms: %s" %
2225 (filename, algorithm,
', '.join(AVAILABLE_HASH_ALGORITHMS)))
2227 blocksize = 64 * 1024
2228 infile = open(os.path.realpath(b_filename),
'rb')
2229 block = infile.read(blocksize)
2231 digest_method.update(block)
2232 block = infile.read(blocksize)
2234 return digest_method.hexdigest()
2237 ''' Return MD5 hex digest of local file using digest_from_file().
2239 Do not use this function unless you have no other choice
for:
2240 1) Optional backwards compatibility
2241 2) Compatibility
with a third party protocol
2243 This function will
not work on systems complying
with FIPS-140-2.
2245 Most uses of this function can use the module.sha1 function instead.
2247 if 'md5' not in AVAILABLE_HASH_ALGORITHMS:
2248 raise ValueError(
'MD5 not available. Possibly running in FIPS mode')
2252 ''' Return SHA1 hex digest of local file using digest_from_file(). '''
2256 ''' Return SHA-256 hex digest of local file using digest_from_file(). '''
2260 '''make a date-marked backup of the specified file, return True or False on success or failure'''
2263 if os.path.exists(fn):
2265 ext = time.strftime(
"%Y-%m-%d@%H:%M:%S~", time.localtime(time.time()))
2266 backupdest =
'%s.%s.%s' % (fn, os.getpid(), ext)
2270 except (shutil.Error, IOError)
as e:
2271 self.
fail_jsonfail_json(msg=
'Could not make backup of %s to %s: %s' % (fn, backupdest,
to_native(e)))
2276 if os.path.exists(tmpfile):
2279 except OSError
as e:
2280 sys.stderr.write(
"could not cleanup %s: %s" % (tmpfile,
to_native(e)))
2283 """Copy a file with preserved ownership, permissions and context"""
2295 shutil.copy2(src, dest)
2304 dest_stat = os.stat(src)
2305 tmp_stat = os.stat(dest)
2306 if dest_stat
and (tmp_stat.st_uid != dest_stat.st_uid
or tmp_stat.st_gid != dest_stat.st_gid):
2307 os.chown(dest, dest_stat.st_uid, dest_stat.st_gid)
2308 except OSError
as e:
2309 if e.errno != errno.EPERM:
2314 current_attribs = current_attribs.get(
'attr_flags',
'')
2318 '''atomically move src to dest, copying attributes from dest, returns true on success
2319 it uses os.rename to ensure this as it
is an atomic operation, rest of the function
is
2320 to work around limitations, corner cases
and ensure selinux context
is saved
if possible
'''
2323 b_src =
to_bytes(src, errors=
'surrogate_or_strict')
2324 b_dest =
to_bytes(dest, errors=
'surrogate_or_strict')
2325 if os.path.exists(b_dest):
2327 dest_stat = os.stat(b_dest)
2330 os.chmod(b_src, dest_stat.st_mode & PERM_BITS)
2331 os.chown(b_src, dest_stat.st_uid, dest_stat.st_gid)
2334 if hasattr(os,
'chflags')
and hasattr(dest_stat,
'st_flags'):
2336 os.chflags(b_src, dest_stat.st_flags)
2337 except OSError
as e:
2338 for err
in 'EOPNOTSUPP',
'ENOTSUP':
2339 if hasattr(errno, err)
and e.errno == getattr(errno, err):
2343 except OSError
as e:
2344 if e.errno != errno.EPERM:
2352 creating =
not os.path.exists(b_dest)
2356 os.rename(b_src, b_dest)
2357 except (IOError, OSError)
as e:
2358 if e.errno
not in [errno.EPERM, errno.EXDEV, errno.EACCES, errno.ETXTBSY, errno.EBUSY]:
2361 self.
fail_jsonfail_json(msg=
'Could not replace file: %s to %s: %s' % (src, dest,
to_native(e)), exception=traceback.format_exc())
2366 b_dest_dir = os.path.dirname(b_dest)
2367 b_suffix = os.path.basename(b_dest)
2369 tmp_dest_name =
None
2371 tmp_dest_fd, tmp_dest_name = tempfile.mkstemp(prefix=b
'.ansible_tmp', dir=b_dest_dir, suffix=b_suffix)
2372 except (OSError, IOError)
as e:
2373 error_msg =
'The destination directory (%s) is not writable by the current user. Error was: %s' % (os.path.dirname(dest),
to_native(e))
2380 error_msg = (
'Failed creating tmp file for atomic move. This usually happens when using Python3 less than Python3.5. '
2381 'Please use Python2.x or Python3.5 or greater.')
2387 self.
fail_jsonfail_json(msg=error_msg, exception=traceback.format_exc())
2390 b_tmp_dest_name =
to_bytes(tmp_dest_name, errors=
'surrogate_or_strict')
2395 os.close(tmp_dest_fd)
2398 shutil.move(b_src, b_tmp_dest_name)
2402 shutil.copy2(b_src, b_tmp_dest_name)
2406 b_tmp_dest_name, context,
False)
2408 tmp_stat = os.stat(b_tmp_dest_name)
2409 if dest_stat
and (tmp_stat.st_uid != dest_stat.st_uid
or tmp_stat.st_gid != dest_stat.st_gid):
2410 os.chown(b_tmp_dest_name, dest_stat.st_uid, dest_stat.st_gid)
2411 except OSError
as e:
2412 if e.errno != errno.EPERM:
2415 os.rename(b_tmp_dest_name, b_dest)
2416 except (shutil.Error, OSError, IOError)
as e:
2417 if unsafe_writes
and e.errno == errno.EBUSY:
2420 self.
fail_jsonfail_json(msg=
'Unable to make %s into to %s, failed final rename from %s: %s' %
2421 (src, dest, b_tmp_dest_name,
to_native(e)), exception=traceback.format_exc())
2422 except (shutil.Error, OSError, IOError)
as e:
2426 self.
fail_jsonfail_json(msg=
'Failed to replace file: %s to %s: %s' % (src, dest,
to_native(e)), exception=traceback.format_exc())
2428 self.
cleanupcleanup(b_tmp_dest_name)
2435 os.chmod(b_dest, DEFAULT_PERM & ~umask)
2437 os.chown(b_dest, os.geteuid(), os.getegid())
2451 out_dest = in_src =
None
2453 out_dest = open(dest,
'wb')
2454 in_src = open(src,
'rb')
2455 shutil.copyfileobj(in_src, out_dest)
2461 except (shutil.Error, OSError, IOError)
as e:
2462 self.
fail_jsonfail_json(msg=
'Could not write data to file (%s) from (%s): %s' % (dest, src,
to_native(e)),
2463 exception=traceback.format_exc())
2467 if not self.
_clean_clean:
2470 to_clean_args = args
2472 if isinstance(args, text_type):
2475 if isinstance(args, binary_type):
2477 if isinstance(args, (text_type, binary_type)):
2478 to_clean_args = shlex.split(to_clean_args)
2482 for arg
in (
to_native(a)
for a
in to_clean_args):
2485 clean_args.append(
'********')
2487 if PASSWD_ARG_RE.match(arg):
2488 sep_idx = arg.find(
'=')
2490 clean_args.append(
'%s=********' % arg[:sep_idx])
2495 clean_args.append(arg)
2496 self.
_clean_clean =
' '.join(shlex_quote(arg)
for arg
in clean_args)
2502 if PY2
and sys.platform !=
'win32':
2503 signal.signal(signal.SIGPIPE, signal.SIG_DFL)
2505 def run_command(self, args, check_rc=False, close_fds=True, executable=None, data=None, binary_data=False, path_prefix=None, cwd=None,
2506 use_unsafe_shell=False, prompt_regex=None, environ_update=None, umask=None, encoding='utf-8', errors='surrogate_or_strict',
2507 expand_user_and_vars=True, pass_fds=None, before_communicate_callback=None):
2509 Execute a command, returns rc, stdout, and stderr.
2511 :arg args:
is the command to run
2512 * If args
is a list, the command will be run
with shell=
False.
2513 * If args
is a string
and use_unsafe_shell=
False it will split args to a list
and run
with shell=
False
2514 * If args
is a string
and use_unsafe_shell=
True it runs
with shell=
True.
2515 :kw check_rc: Whether to call fail_json
in case of non zero RC.
2517 :kw close_fds: See documentation
for subprocess.Popen(). Default
True
2518 :kw executable: See documentation
for subprocess.Popen(). Default
None
2519 :kw data: If given, information to write to the stdin of the command
2520 :kw binary_data: If
False, append a newline to the data. Default
False
2521 :kw path_prefix: If given, additional path to find the command
in.
2522 This adds to the PATH environment variable so helper commands
in
2523 the same directory can also be found
2524 :kw cwd: If given, working directory to run the command inside
2525 :kw use_unsafe_shell: See `args` parameter. Default
False
2526 :kw prompt_regex: Regex string (
not a compiled regex) which can be
2527 used to detect prompts
in the stdout which would otherwise cause
2528 the execution to hang (especially
if no input data
is specified)
2529 :kw environ_update: dictionary to *update* os.environ
with
2530 :kw umask: Umask to be used when running the command. Default
None
2531 :kw encoding: Since we
return native strings, on python3 we need to
2532 know the encoding to use to transform
from bytes to text. If you
2533 want to always get bytes back, use encoding=
None. The default
is
2534 "utf-8". This does
not affect transformation of strings given
as
2536 :kw errors: Since we
return native strings, on python3 we need to
2537 transform stdout
and stderr
from bytes to text. If the bytes are
2538 undecodable
in the ``encoding`` specified, then use this error
2539 handler to deal
with them. The default
is ``surrogate_or_strict``
2540 which means that the bytes will be decoded using the
2541 surrogateescape error handler
if available (available on all
2542 python3 versions we support) otherwise a UnicodeError traceback
2543 will be raised. This does
not affect transformations of strings
2545 :kw expand_user_and_vars: When ``use_unsafe_shell=
False`` this argument
2546 dictates whether ``~``
is expanded
in paths
and environment variables
2547 are expanded before running the command. When ``
True`` a string such
as
2548 ``$SHELL`` will be expanded regardless of escaping. When ``
False``
and
2549 ``use_unsafe_shell=
False`` no path
or variable expansion will be done.
2550 :kw pass_fds: When running on Python 3 this argument
2551 dictates which file descriptors should be passed
2552 to an underlying ``Popen`` constructor. On Python 2, this will
2553 set ``close_fds`` to
False.
2554 :kw before_communicate_callback: This function will be called
2555 after ``Popen`` object will be created
2556 but before communicating to the process.
2557 (``Popen`` object will be passed to callback
as a first argument)
2558 :returns: A 3-tuple of
return code (integer), stdout (native string),
2559 and stderr (native string). On python2, stdout
and stderr are both
2560 byte strings. On python3, stdout
and stderr are text strings converted
2561 according to the encoding
and errors parameters. If you want byte
2562 strings on python3, use encoding=
None to turn decoding to text off.
2567 if not isinstance(args, (list, binary_type, text_type)):
2568 msg =
"Argument 'args' to run_command must be list or string"
2569 self.
fail_jsonfail_json(rc=257, cmd=args, msg=msg)
2572 if use_unsafe_shell:
2575 if isinstance(args, list):
2576 args = b
" ".join([
to_bytes(shlex_quote(x), errors=
'surrogate_or_strict')
for x
in args])
2578 args =
to_bytes(args, errors=
'surrogate_or_strict')
2582 executable =
to_bytes(executable, errors=
'surrogate_or_strict')
2583 args = [executable, b
'-c', args]
2584 elif self.
_shell_shell
not in (
None,
'/bin/sh'):
2585 args = [
to_bytes(self.
_shell_shell, errors=
'surrogate_or_strict'), b
'-c', args]
2590 if isinstance(args, (binary_type, text_type)):
2594 args =
to_bytes(args, errors=
'surrogate_or_strict')
2596 args =
to_text(args, errors=
'surrogateescape')
2597 args = shlex.split(args)
2600 if expand_user_and_vars:
2601 args = [
to_bytes(os.path.expanduser(os.path.expandvars(x)), errors=
'surrogate_or_strict')
for x
in args
if x
is not None]
2603 args = [
to_bytes(x, errors=
'surrogate_or_strict')
for x
in args
if x
is not None]
2607 if isinstance(prompt_regex, text_type):
2609 prompt_regex =
to_bytes(prompt_regex, errors=
'surrogateescape')
2611 prompt_regex =
to_bytes(prompt_regex, errors=
'surrogate_or_strict')
2613 prompt_re = re.compile(prompt_regex, re.MULTILINE)
2615 self.
fail_jsonfail_json(msg=
"invalid prompt regular expression given to run_command")
2625 old_env_vals[key] = os.environ.get(key,
None)
2626 os.environ[key] = val
2628 for key, val
in environ_update.items():
2629 old_env_vals[key] = os.environ.get(key,
None)
2630 os.environ[key] = val
2632 old_env_vals[
'PATH'] = os.environ[
'PATH']
2633 os.environ[
'PATH'] =
"%s:%s" % (path_prefix, os.environ[
'PATH'])
2641 if 'PYTHONPATH' in os.environ:
2642 pypaths = os.environ[
'PYTHONPATH'].
split(
':')
2643 pypaths = [x
for x
in pypaths
2644 if not x.endswith(
'/ansible_modlib.zip')
and
2645 not x.endswith(
'/debug_dir')]
2646 os.environ[
'PYTHONPATH'] =
':'.join(pypaths)
2647 if not os.environ[
'PYTHONPATH']:
2648 del os.environ[
'PYTHONPATH']
2651 st_in = subprocess.PIPE
2654 executable=executable,
2656 close_fds=close_fds,
2658 stdout=subprocess.PIPE,
2659 stderr=subprocess.PIPE,
2662 if PY3
and pass_fds:
2663 kwargs[
"pass_fds"] = pass_fds
2664 elif PY2
and pass_fds:
2665 kwargs[
'close_fds'] =
False
2668 prev_dir = os.getcwd()
2671 if cwd
and os.path.isdir(cwd):
2672 cwd =
to_bytes(os.path.abspath(os.path.expanduser(cwd)), errors=
'surrogate_or_strict')
2676 except (OSError, IOError)
as e:
2677 self.
fail_jsonfail_json(rc=e.errno, msg=
"Could not open %s, %s" % (cwd,
to_native(e)),
2678 exception=traceback.format_exc())
2682 old_umask = os.umask(umask)
2687 cmd = subprocess.Popen(args, **kwargs)
2688 if before_communicate_callback:
2689 before_communicate_callback(cmd)
2697 selector = selectors.DefaultSelector()
2701 selector = selectors.PollSelector()
2703 selector.register(cmd.stdout, selectors.EVENT_READ)
2704 selector.register(cmd.stderr, selectors.EVENT_READ)
2705 if os.name ==
'posix':
2706 fcntl.fcntl(cmd.stdout.fileno(), fcntl.F_SETFL, fcntl.fcntl(cmd.stdout.fileno(), fcntl.F_GETFL) | os.O_NONBLOCK)
2707 fcntl.fcntl(cmd.stderr.fileno(), fcntl.F_SETFL, fcntl.fcntl(cmd.stderr.fileno(), fcntl.F_GETFL) | os.O_NONBLOCK)
2712 if isinstance(data, text_type):
2714 cmd.stdin.write(data)
2718 events = selector.select(1)
2719 for key, event
in events:
2720 b_chunk = key.fileobj.read()
2721 if b_chunk ==
b(
''):
2722 selector.unregister(key.fileobj)
2723 if key.fileobj == cmd.stdout:
2725 elif key.fileobj == cmd.stderr:
2729 if prompt_re.search(stdout)
and not data:
2731 stdout =
to_native(stdout, encoding=encoding, errors=errors)
2732 return (257, stdout,
"A prompt was encountered while running a command, but no input data was specified")
2736 if (
not events
or not selector.get_map())
and cmd.poll()
is not None:
2741 elif not selector.get_map()
and cmd.poll()
is None:
2752 except (OSError, IOError)
as e:
2755 except Exception
as e:
2756 self.
loglog(
"Error Executing CMD:%s Exception:%s" % (self.
_clean_args_clean_args(args),
to_native(traceback.format_exc())))
2760 for key, val
in old_env_vals.items():
2764 os.environ[key] = val
2769 if rc != 0
and check_rc:
2771 self.
fail_jsonfail_json(cmd=self.
_clean_args_clean_args(args), rc=rc, stdout=stdout, stderr=stderr, msg=msg)
2776 if encoding
is not None:
2777 return (rc,
to_native(stdout, encoding=encoding, errors=errors),
2778 to_native(stderr, encoding=encoding, errors=errors))
2780 return (rc, stdout, stderr)
2783 filename = os.path.expandvars(os.path.expanduser(filename))
2784 fh = open(filename,
'a')
2792 pretty_bytes = bytes_to_human
2802 is_executable = is_executable
2808 buffer_size = fcntl.fcntl(fd, 1032)
2812 buffer_size = select.PIPE_BUF
2820 return os.path.dirname(os.path.realpath(__file__))
def _count_terms(self, check, param=None)
def selinux_default_context(self, path, mode=0)
def get_file_attributes(self, path)
def set_mode_if_different(self, path, mode, changed, diff=None, expand=True)
def _check_type_bool(self, value)
def _set_defaults(self, pre=True, spec=None, param=None)
def set_owner_if_different(self, path, owner, changed, diff=None, expand=True)
def set_directory_attributes_if_different(self, file_args, changed, diff=None, expand=True)
def bytes_to_human(self, size)
def _set_fallbacks(self, spec=None, param=None)
_CHECK_ARGUMENT_TYPES_DISPATCHER
def selinux_mls_enabled(self)
def _symbolic_mode_to_octal(cls, path_stat, symbolic_mode)
def cleanup(self, tmpfile)
def _clean_args(self, args)
def _handle_no_log_values(self, spec=None, param=None)
def _return_formatted(self, kwargs)
def fail_on_missing_params(self, required_params=None)
def _check_required_together(self, spec, param=None)
def selinux_enabled(self)
def is_special_selinux_path(self, path)
def preserved_copy(self, src, dest)
def _log_invocation(self)
def _check_type_path(self, value)
def backup_local(self, fn)
def set_fs_attributes_if_different(self, file_args, changed, diff=None, expand=True)
def exit_json(self, **kwargs)
def _check_type_dict(self, value)
def _check_type_int(self, value)
def _check_arguments(self, check_invalid_arguments, spec=None, param=None, legal_inputs=None)
def _restore_signal_handlers(self)
def set_context_if_different(self, path, context, changed, diff=None)
def _get_wanted_type(self, wanted, k)
def do_cleanup_files(self)
def atomic_move(self, src, dest, unsafe_writes=False)
def deprecate(self, msg, version=None, date=None, collection_name=None)
def _check_type_jsonarg(self, value)
def __init__(self, argument_spec, bypass_checks=False, no_log=False, check_invalid_arguments=None, mutually_exclusive=None, required_together=None, required_one_of=None, add_file_common_args=False, supports_check_mode=False, required_if=None, required_by=None)
def load_file_common_arguments(self, params)
def from_json(self, data)
def _get_octal_mode_from_symbolic_perms(path_stat, user, perms, use_umask)
def _check_type_list(self, value)
def _unsafe_writes(self, src, dest)
def _check_required_by(self, spec, param=None)
def get_bin_path(self, arg, required=False, opt_dirs=None)
def set_default_selinux_context(self, path, changed)
def _check_argument_values(self, spec=None, param=None)
def _check_type_str(self, value)
def _check_required_if(self, spec, param=None)
def log(self, msg, log_args=None)
def add_path_info(self, kwargs)
def add_cleanup_file(self, path)
def human_to_bytes(self, number, isbits=False)
def set_attributes_if_different(self, path, attributes, changed, diff=None, expand=True)
_string_conversion_action
def check_file_absent_if_check_mode(self, file_path)
def _check_argument_types(self, spec=None, param=None)
def _handle_elements(self, wanted, param, values)
def set_group_if_different(self, path, group, changed, diff=None, expand=True)
def append_to_file(self, filename, str)
def _check_type_bytes(self, value)
def selinux_initial_context(self)
def _apply_operation_to_mode(user, operator, mode_to_apply, current_mode)
def run_command(self, args, check_rc=False, close_fds=True, executable=None, data=None, binary_data=False, path_prefix=None, cwd=None, use_unsafe_shell=False, prompt_regex=None, environ_update=None, umask=None, encoding='utf-8', errors='surrogate_or_strict', expand_user_and_vars=True, pass_fds=None, before_communicate_callback=None)
def _log_to_syslog(self, msg)
def _check_type_float(self, value)
run_command_environ_update
def _check_required_one_of(self, spec, param=None)
def find_mount_point(self, path)
def set_file_attributes_if_different(self, file_args, changed, diff=None, expand=True)
def _check_type_raw(self, value)
def user_and_group(self, path, expand=True)
def safe_eval(self, value, locals=None, include_exceptions=False)
def _check_type_bits(self, value)
def _check_mutually_exclusive(self, spec, param=None)
def fail_json(self, **kwargs)
def selinux_context(self, path)
def digest_from_file(self, filename, algorithm)
def _handle_options(self, argument_spec=None, params=None, prefix='')
def _check_required_arguments(self, spec=None, param=None)
def sha256(self, filename)
def _handle_aliases(self, spec=None, param=None, option_prefix='')
def to_bytes(obj, encoding='utf-8', errors=None, nonstring='simplerepr')
def to_text(obj, encoding='utf-8', errors=None, nonstring='simplerepr')
def _remove_values_conditions(value, no_log_strings, deferred_removals)
def missing_required_lib(library, reason=None, url=None)
def load_platform_subclass(cls, *args, **kwargs)
def get_all_subclasses(cls)
def sanitize_keys(obj, no_log_strings, ignore_keys=frozenset())
def _sanitize_keys_conditions(value, no_log_strings, ignore_keys, deferred_removals)
def remove_values(value, no_log_strings)
def heuristic_log_sanitize(data, no_log_values=None)
def env_fallback(*args, **kwargs)
def format_attributes(attributes)
def list_no_log_values(argument_spec, params)
def list_deprecations(argument_spec, params, prefix='')
def handle_aliases(argument_spec, params, alias_warnings=None)
def get_platform_subclass(cls)
def lenient_lowercase(lst)
def check_type_int(value)
def check_type_bits(value)
def check_required_one_of(terms, module_parameters)
def check_mutually_exclusive(terms, module_parameters)
def check_type_bool(value)
def check_required_by(requirements, module_parameters)
def check_type_float(value)
def check_type_raw(value)
def check_required_if(requirements, module_parameters)
def check_type_jsonarg(value)
def check_required_together(terms, module_parameters)
def check_type_str(value, allow_conversion=True)
def check_type_path(value)
def check_missing_parameters(module_parameters, required_parameters=None)
def check_required_arguments(argument_spec, module_parameters)
def check_type_list(value)
def count_terms(terms, module_parameters)
def check_type_dict(value)
def check_type_bytes(value)
def get(network_os, module_name, connection_type)
def update(client, current_stream, stream_name, number_of_shards=1, retention_period=None, tags=None, wait=False, wait_timeout=300, check_mode=False)