ansible-core 2.19.3rc1__py3-none-any.whl → 2.20.0b2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of ansible-core might be problematic. Click here for more details.

Files changed (201) hide show
  1. ansible/_internal/__init__.py +1 -4
  2. ansible/_internal/_ansiballz/_builder.py +1 -3
  3. ansible/_internal/_collection_proxy.py +7 -9
  4. ansible/_internal/_json/__init__.py +3 -4
  5. ansible/_internal/_templating/_engine.py +1 -1
  6. ansible/_internal/_templating/_jinja_plugins.py +1 -2
  7. ansible/_internal/_wrapt.py +105 -301
  8. ansible/cli/__init__.py +11 -10
  9. ansible/cli/adhoc.py +1 -2
  10. ansible/cli/arguments/option_helpers.py +1 -1
  11. ansible/cli/config.py +5 -6
  12. ansible/cli/doc.py +70 -68
  13. ansible/cli/galaxy.py +15 -24
  14. ansible/cli/inventory.py +0 -1
  15. ansible/cli/playbook.py +0 -1
  16. ansible/cli/pull.py +0 -1
  17. ansible/cli/scripts/ansible_connection_cli_stub.py +1 -1
  18. ansible/collections/list.py +4 -2
  19. ansible/config/base.yml +1 -25
  20. ansible/config/manager.py +0 -2
  21. ansible/executor/play_iterator.py +42 -20
  22. ansible/executor/playbook_executor.py +0 -9
  23. ansible/executor/task_executor.py +26 -18
  24. ansible/executor/task_queue_manager.py +1 -3
  25. ansible/galaxy/api.py +33 -80
  26. ansible/galaxy/collection/__init__.py +4 -17
  27. ansible/galaxy/dependency_resolution/dataclasses.py +0 -10
  28. ansible/galaxy/dependency_resolution/providers.py +24 -118
  29. ansible/galaxy/role.py +1 -33
  30. ansible/inventory/manager.py +2 -3
  31. ansible/keyword_desc.yml +0 -3
  32. ansible/module_utils/_internal/_datatag/__init__.py +2 -10
  33. ansible/module_utils/_internal/_no_six.py +86 -0
  34. ansible/module_utils/_text.py +28 -8
  35. ansible/module_utils/ansible_release.py +2 -2
  36. ansible/module_utils/basic.py +26 -23
  37. ansible/module_utils/common/_collections_compat.py +11 -2
  38. ansible/module_utils/common/collections.py +8 -3
  39. ansible/module_utils/common/dict_transformations.py +1 -2
  40. ansible/module_utils/common/network.py +4 -2
  41. ansible/module_utils/common/parameters.py +32 -41
  42. ansible/module_utils/common/text/converters.py +109 -23
  43. ansible/module_utils/common/text/formatters.py +6 -2
  44. ansible/module_utils/common/validation.py +11 -9
  45. ansible/module_utils/connection.py +8 -3
  46. ansible/module_utils/facts/hardware/linux.py +23 -7
  47. ansible/module_utils/facts/hardware/netbsd.py +1 -1
  48. ansible/module_utils/facts/hardware/sunos.py +2 -1
  49. ansible/module_utils/facts/packages.py +6 -2
  50. ansible/module_utils/facts/system/distribution.py +2 -1
  51. ansible/module_utils/facts/system/env.py +6 -3
  52. ansible/module_utils/facts/system/local.py +3 -1
  53. ansible/module_utils/parsing/convert_bool.py +6 -2
  54. ansible/module_utils/service.py +2 -3
  55. ansible/module_utils/six/__init__.py +19 -6
  56. ansible/module_utils/yumdnf.py +0 -5
  57. ansible/modules/apt.py +18 -13
  58. ansible/modules/apt_repository.py +1 -1
  59. ansible/modules/assemble.py +5 -9
  60. ansible/modules/blockinfile.py +39 -23
  61. ansible/modules/cron.py +26 -35
  62. ansible/modules/deb822_repository.py +83 -12
  63. ansible/modules/dnf.py +3 -7
  64. ansible/modules/dnf5.py +4 -6
  65. ansible/modules/expect.py +0 -3
  66. ansible/modules/find.py +1 -2
  67. ansible/modules/get_url.py +1 -1
  68. ansible/modules/git.py +4 -5
  69. ansible/modules/include_vars.py +1 -1
  70. ansible/modules/known_hosts.py +7 -1
  71. ansible/modules/lineinfile.py +71 -63
  72. ansible/modules/package_facts.py +1 -1
  73. ansible/modules/pip.py +8 -2
  74. ansible/modules/replace.py +6 -6
  75. ansible/modules/service.py +3 -4
  76. ansible/modules/stat.py +20 -0
  77. ansible/modules/uri.py +9 -10
  78. ansible/modules/user.py +1 -2
  79. ansible/modules/wait_for.py +2 -2
  80. ansible/modules/wait_for_connection.py +2 -1
  81. ansible/modules/yum_repository.py +1 -16
  82. ansible/parsing/dataloader.py +24 -31
  83. ansible/parsing/mod_args.py +3 -0
  84. ansible/parsing/vault/__init__.py +1 -2
  85. ansible/playbook/base.py +8 -56
  86. ansible/playbook/block.py +1 -63
  87. ansible/playbook/collectionsearch.py +1 -2
  88. ansible/playbook/handler.py +1 -7
  89. ansible/playbook/helpers.py +15 -20
  90. ansible/playbook/included_file.py +1 -1
  91. ansible/playbook/play.py +105 -49
  92. ansible/playbook/play_context.py +4 -0
  93. ansible/playbook/role/__init__.py +10 -65
  94. ansible/playbook/role/definition.py +3 -4
  95. ansible/playbook/role/include.py +2 -3
  96. ansible/playbook/role/metadata.py +1 -12
  97. ansible/playbook/role/requirement.py +1 -2
  98. ansible/playbook/role_include.py +1 -2
  99. ansible/playbook/taggable.py +16 -5
  100. ansible/playbook/task.py +51 -55
  101. ansible/plugins/action/__init__.py +20 -19
  102. ansible/plugins/action/add_host.py +1 -2
  103. ansible/plugins/action/fetch.py +3 -5
  104. ansible/plugins/action/group_by.py +1 -2
  105. ansible/plugins/action/include_vars.py +20 -22
  106. ansible/plugins/action/script.py +1 -3
  107. ansible/plugins/action/template.py +1 -2
  108. ansible/plugins/action/uri.py +4 -2
  109. ansible/plugins/cache/__init__.py +1 -0
  110. ansible/plugins/callback/__init__.py +13 -6
  111. ansible/plugins/connection/__init__.py +3 -7
  112. ansible/plugins/connection/local.py +2 -3
  113. ansible/plugins/connection/psrp.py +0 -2
  114. ansible/plugins/connection/ssh.py +2 -7
  115. ansible/plugins/connection/winrm.py +0 -2
  116. ansible/plugins/doc_fragments/result_format_callback.py +15 -0
  117. ansible/plugins/filter/core.py +4 -5
  118. ansible/plugins/filter/encryption.py +3 -27
  119. ansible/plugins/filter/mathstuff.py +1 -2
  120. ansible/plugins/filter/to_nice_yaml.yml +31 -3
  121. ansible/plugins/filter/to_yaml.yml +29 -12
  122. ansible/plugins/inventory/__init__.py +1 -2
  123. ansible/plugins/inventory/toml.py +3 -6
  124. ansible/plugins/inventory/yaml.py +1 -2
  125. ansible/plugins/loader.py +3 -4
  126. ansible/plugins/lookup/password.py +1 -2
  127. ansible/plugins/lookup/subelements.py +2 -3
  128. ansible/plugins/lookup/url.py +1 -1
  129. ansible/plugins/lookup/varnames.py +1 -2
  130. ansible/plugins/shell/__init__.py +9 -4
  131. ansible/plugins/shell/powershell.py +8 -24
  132. ansible/plugins/strategy/__init__.py +6 -3
  133. ansible/plugins/test/core.py +4 -1
  134. ansible/plugins/test/falsy.yml +1 -1
  135. ansible/plugins/test/regex.yml +18 -6
  136. ansible/plugins/test/truthy.yml +1 -1
  137. ansible/release.py +2 -2
  138. ansible/template/__init__.py +3 -7
  139. ansible/utils/collection_loader/_collection_config.py +5 -0
  140. ansible/utils/collection_loader/_collection_finder.py +11 -14
  141. ansible/utils/context_objects.py +7 -4
  142. ansible/utils/display.py +7 -6
  143. ansible/utils/encrypt.py +0 -5
  144. ansible/utils/helpers.py +6 -2
  145. ansible/utils/jsonrpc.py +7 -3
  146. ansible/utils/plugin_docs.py +49 -38
  147. ansible/utils/ssh_functions.py +0 -19
  148. ansible/utils/unsafe_proxy.py +7 -7
  149. ansible/vars/clean.py +2 -3
  150. ansible/vars/manager.py +28 -22
  151. ansible/vars/plugins.py +1 -31
  152. {ansible_core-2.19.3rc1.dist-info → ansible_core-2.20.0b2.dist-info}/METADATA +3 -3
  153. {ansible_core-2.19.3rc1.dist-info → ansible_core-2.20.0b2.dist-info}/RECORD +199 -200
  154. ansible_test/_data/completion/docker.txt +7 -7
  155. ansible_test/_data/completion/network.txt +0 -1
  156. ansible_test/_data/completion/remote.txt +4 -4
  157. ansible_test/_data/requirements/ansible-test.txt +1 -1
  158. ansible_test/_data/requirements/sanity.changelog.txt +1 -1
  159. ansible_test/_data/requirements/sanity.pep8.txt +1 -1
  160. ansible_test/_data/requirements/sanity.pylint.txt +4 -4
  161. ansible_test/_internal/cache.py +2 -5
  162. ansible_test/_internal/cli/compat.py +1 -1
  163. ansible_test/_internal/commands/coverage/combine.py +1 -3
  164. ansible_test/_internal/commands/integration/__init__.py +3 -7
  165. ansible_test/_internal/commands/integration/cloud/httptester.py +1 -1
  166. ansible_test/_internal/commands/integration/coverage.py +1 -3
  167. ansible_test/_internal/commands/integration/filters.py +5 -10
  168. ansible_test/_internal/commands/sanity/validate_modules.py +1 -5
  169. ansible_test/_internal/commands/units/__init__.py +1 -13
  170. ansible_test/_internal/completion.py +2 -5
  171. ansible_test/_internal/config.py +2 -7
  172. ansible_test/_internal/coverage_util.py +1 -1
  173. ansible_test/_internal/delegation.py +2 -0
  174. ansible_test/_internal/docker_util.py +1 -1
  175. ansible_test/_internal/host_profiles.py +6 -11
  176. ansible_test/_internal/provider/__init__.py +2 -5
  177. ansible_test/_internal/provisioning.py +2 -5
  178. ansible_test/_internal/pypi_proxy.py +1 -1
  179. ansible_test/_internal/target.py +2 -6
  180. ansible_test/_internal/thread.py +1 -4
  181. ansible_test/_internal/util.py +9 -14
  182. ansible_test/_util/controller/sanity/code-smell/runtime-metadata.py +14 -19
  183. ansible_test/_util/controller/sanity/pylint/plugins/unwanted.py +40 -27
  184. ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py +31 -18
  185. ansible_test/_util/controller/sanity/validate-modules/validate_modules/module_args.py +1 -2
  186. ansible_test/_util/controller/sanity/validate-modules/validate_modules/schema.py +59 -71
  187. ansible_test/_util/controller/sanity/validate-modules/validate_modules/utils.py +1 -2
  188. ansible_test/_util/target/cli/ansible_test_cli_stub.py +4 -2
  189. ansible_test/_util/target/common/constants.py +2 -2
  190. ansible_test/_util/target/setup/bootstrap.sh +0 -6
  191. ansible/utils/py3compat.py +0 -27
  192. ansible_test/_data/pytest/config/legacy.ini +0 -4
  193. {ansible_core-2.19.3rc1.dist-info → ansible_core-2.20.0b2.dist-info}/WHEEL +0 -0
  194. {ansible_core-2.19.3rc1.dist-info → ansible_core-2.20.0b2.dist-info}/entry_points.txt +0 -0
  195. {ansible_core-2.19.3rc1.dist-info → ansible_core-2.20.0b2.dist-info}/licenses/COPYING +0 -0
  196. {ansible_core-2.19.3rc1.dist-info → ansible_core-2.20.0b2.dist-info}/licenses/licenses/Apache-License.txt +0 -0
  197. {ansible_core-2.19.3rc1.dist-info → ansible_core-2.20.0b2.dist-info}/licenses/licenses/BSD-3-Clause.txt +0 -0
  198. {ansible_core-2.19.3rc1.dist-info → ansible_core-2.20.0b2.dist-info}/licenses/licenses/MIT-license.txt +0 -0
  199. {ansible_core-2.19.3rc1.dist-info → ansible_core-2.20.0b2.dist-info}/licenses/licenses/PSF-license.txt +0 -0
  200. {ansible_core-2.19.3rc1.dist-info → ansible_core-2.20.0b2.dist-info}/licenses/licenses/simplified_bsd.txt +0 -0
  201. {ansible_core-2.19.3rc1.dist-info → ansible_core-2.20.0b2.dist-info}/top_level.txt +0 -0
@@ -183,14 +183,14 @@ import os
183
183
  import re
184
184
  import tempfile
185
185
 
186
- from ansible.module_utils.common.text.converters import to_text, to_bytes
186
+ from ansible.module_utils.common.text.converters import to_text
187
187
  from ansible.module_utils.basic import AnsibleModule
188
188
 
189
189
 
190
- def write_changes(module, contents, path):
190
+ def write_changes(module, contents, path, encoding='utf-8'):
191
191
 
192
192
  tmpfd, tmpfile = tempfile.mkstemp(dir=module.tmpdir)
193
- with os.fdopen(tmpfd, 'wb') as f:
193
+ with os.fdopen(tmpfd, 'w', encoding=encoding) as f:
194
194
  f.write(contents)
195
195
 
196
196
  validate = module.params.get('validate', None)
@@ -254,8 +254,8 @@ def main():
254
254
  module.fail_json(rc=257, msg='Path %s does not exist !' % path)
255
255
  else:
256
256
  try:
257
- with open(path, 'rb') as f:
258
- contents = to_text(f.read(), errors='surrogate_or_strict', encoding=encoding)
257
+ with open(path, 'r', encoding=encoding) as f:
258
+ contents = f.read()
259
259
  except OSError as ex:
260
260
  raise Exception(f"Unable to read the contents of {path!r}.") from ex
261
261
 
@@ -307,7 +307,7 @@ def main():
307
307
  res_args['backup_file'] = module.backup_local(path)
308
308
  # We should always follow symlinks so that we change the real file
309
309
  path = os.path.realpath(path)
310
- write_changes(module, to_bytes(result[0], encoding=encoding), path)
310
+ write_changes(module, result[0], path, encoding=encoding)
311
311
 
312
312
  res_args['msg'], res_args['changed'] = check_file_attrs(module, changed, msg)
313
313
  module.exit_json(**res_args)
@@ -180,7 +180,6 @@ from ansible.module_utils.basic import AnsibleModule
180
180
  from ansible.module_utils.common.locale import get_best_parsable_locale
181
181
  from ansible.module_utils.common.sys_info import get_platform_subclass
182
182
  from ansible.module_utils.service import fail_if_missing, is_systemd_managed
183
- from ansible.module_utils.six import b
184
183
 
185
184
 
186
185
  class Service(object):
@@ -292,8 +291,8 @@ class Service(object):
292
291
  # chkconfig localizes messages and we're screen scraping so make
293
292
  # sure we use the C locale
294
293
  p = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=lang_env, preexec_fn=lambda: os.close(pipe[1]))
295
- stdout = b("")
296
- stderr = b("")
294
+ stdout = b""
295
+ stderr = b""
297
296
  fds = [p.stdout, p.stderr]
298
297
  # Wait for all output, or until the main process is dead and its output is done.
299
298
  while fds:
@@ -322,7 +321,7 @@ class Service(object):
322
321
  os.close(pipe[1])
323
322
  os.waitpid(pid, 0)
324
323
  # Wait for data from daemon process and process it.
325
- data = b("")
324
+ data = b""
326
325
  while True:
327
326
  rfd, wfd, efd = select.select([pipe[0]], [], [pipe[0]])
328
327
  if pipe[0] in rfd:
ansible/modules/stat.py CHANGED
@@ -44,6 +44,14 @@ options:
44
44
  version_added: "2.3"
45
45
  get_checksum:
46
46
  version_added: "1.8"
47
+ get_selinux_context:
48
+ description:
49
+ - Get file SELinux context in a list V([user, role, type, range]),
50
+ and will get V([None, None, None, None]) if it is not possible to retrieve the context,
51
+ either because it does not exist or some other issue.
52
+ type: bool
53
+ default: no
54
+ version_added: '2.20'
47
55
  extends_documentation_fragment:
48
56
  - action_common_attributes
49
57
  - checksum_common
@@ -346,6 +354,12 @@ stat:
346
354
  type: list
347
355
  sample: [ immutable, extent ]
348
356
  version_added: 2.3
357
+ selinux_context:
358
+ description: The SELinux context of a path
359
+ returned: success, path exists and user can execute the path
360
+ type: list
361
+ sample: [ user, role, type, range ]
362
+ version_added: '2.20'
349
363
  version:
350
364
  description: The version/generation attribute of a file according to the filesystem
351
365
  returned: success, path exists, user can execute the path, lsattr is available and filesystem supports
@@ -434,6 +448,7 @@ def main():
434
448
  get_checksum=dict(type='bool', default=True),
435
449
  get_mime=dict(type='bool', default=True, aliases=['mime', 'mime_type', 'mime-type']),
436
450
  get_attributes=dict(type='bool', default=True, aliases=['attr', 'attributes']),
451
+ get_selinux_context=dict(type='bool', default=False),
437
452
  checksum_algorithm=dict(type='str', default='sha1',
438
453
  choices=['md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512'],
439
454
  aliases=['checksum', 'checksum_algo']),
@@ -448,6 +463,7 @@ def main():
448
463
  get_attr = module.params.get('get_attributes')
449
464
  get_checksum = module.params.get('get_checksum')
450
465
  checksum_algorithm = module.params.get('checksum_algorithm')
466
+ get_selinux_context = module.params.get('get_selinux_context')
451
467
 
452
468
  # main stat data
453
469
  try:
@@ -515,6 +531,10 @@ def main():
515
531
  if x in out:
516
532
  output[x] = out[x]
517
533
 
534
+ # try to get SELinux context
535
+ if get_selinux_context:
536
+ output['selinux_context'] = module.selinux_context(b_path)
537
+
518
538
  module.exit_json(changed=False, stat=output)
519
539
 
520
540
 
ansible/modules/uri.py CHANGED
@@ -438,13 +438,12 @@ import os
438
438
  import re
439
439
  import shutil
440
440
  import tempfile
441
+ from collections.abc import Mapping, Sequence
441
442
  from datetime import datetime, timezone
443
+ from urllib.parse import urlencode, urljoin
442
444
 
443
445
  from ansible.module_utils.basic import AnsibleModule, sanitize_keys
444
- from ansible.module_utils.six import binary_type, iteritems, string_types
445
- from ansible.module_utils.six.moves.urllib.parse import urlencode, urljoin
446
446
  from ansible.module_utils.common.text.converters import to_native, to_text
447
- from ansible.module_utils.six.moves.collections_abc import Mapping, Sequence
448
447
  from ansible.module_utils.urls import (
449
448
  fetch_url,
450
449
  get_response_filename,
@@ -479,7 +478,7 @@ def write_file(module, dest, content, resp):
479
478
  try:
480
479
  fd, tmpsrc = tempfile.mkstemp(dir=module.tmpdir)
481
480
  with os.fdopen(fd, 'wb') as f:
482
- if isinstance(content, binary_type):
481
+ if isinstance(content, bytes):
483
482
  f.write(content)
484
483
  else:
485
484
  shutil.copyfileobj(content, f)
@@ -521,14 +520,14 @@ def kv_list(data):
521
520
 
522
521
  def form_urlencoded(body):
523
522
  """ Convert data into a form-urlencoded string """
524
- if isinstance(body, string_types):
523
+ if isinstance(body, str):
525
524
  return body
526
525
 
527
526
  if isinstance(body, (Mapping, Sequence)):
528
527
  result = []
529
528
  # Turn a list of lists into a list of tuples that urlencode accepts
530
529
  for key, values in kv_list(body):
531
- if isinstance(values, string_types) or not isinstance(values, (Mapping, Sequence)):
530
+ if isinstance(values, str) or not isinstance(values, (Mapping, Sequence)):
532
531
  values = [values]
533
532
  for value in values:
534
533
  if value is not None:
@@ -641,12 +640,12 @@ def main():
641
640
 
642
641
  if body_format == 'json':
643
642
  # Encode the body unless its a string, then assume it is pre-formatted JSON
644
- if not isinstance(body, string_types):
643
+ if not isinstance(body, str):
645
644
  body = json.dumps(body)
646
645
  if 'content-type' not in [header.lower() for header in dict_headers]:
647
646
  dict_headers['Content-Type'] = 'application/json'
648
647
  elif body_format == 'form-urlencoded':
649
- if not isinstance(body, string_types):
648
+ if not isinstance(body, str):
650
649
  try:
651
650
  body = form_urlencoded(body)
652
651
  except ValueError as e:
@@ -747,7 +746,7 @@ def main():
747
746
  # In python3, the headers are title cased. Lowercase them to be
748
747
  # compatible with the python2 behaviour.
749
748
  uresp = {}
750
- for key, value in iteritems(resp):
749
+ for key, value in resp.items():
751
750
  ukey = key.replace("-", "_").lower()
752
751
  uresp[ukey] = value
753
752
 
@@ -755,7 +754,7 @@ def main():
755
754
  uresp['location'] = urljoin(url, uresp['location'])
756
755
 
757
756
  # Default content_encoding to try
758
- if isinstance(content, binary_type):
757
+ if isinstance(content, bytes):
759
758
  u_content = to_text(content, encoding=content_encoding)
760
759
  if maybe_json:
761
760
  try:
ansible/modules/user.py CHANGED
@@ -2504,11 +2504,10 @@ class DarwinUser(User):
2504
2504
  Please note that password must be cleartext.
2505
2505
  """
2506
2506
  # some documentation on how is stored passwords on OSX:
2507
- # http://blog.lostpassword.com/2012/07/cracking-mac-os-x-lion-accounts-passwords/
2508
2507
  # http://null-byte.wonderhowto.com/how-to/hack-mac-os-x-lion-passwords-0130036/
2509
2508
  # http://pastebin.com/RYqxi7Ca
2510
2509
  # on OSX 10.8+ hash is SALTED-SHA512-PBKDF2
2511
- # https://pythonhosted.org/passlib/lib/passlib.hash.pbkdf2_digest.html
2510
+ # https://passlib.readthedocs.io/en/stable/lib/passlib.hash.pbkdf2_digest.html
2512
2511
  # https://gist.github.com/nueh/8252572
2513
2512
  cmd = self._get_dscl()
2514
2513
  if self.password:
@@ -216,13 +216,13 @@ elapsed:
216
216
  type: int
217
217
  sample: 23
218
218
  match_groups:
219
- description: Tuple containing all the subgroups of the match as returned by U(https://docs.python.org/3/library/re.html#re.MatchObject.groups)
219
+ description: Tuple containing all the subgroups of the match as returned by U(https://docs.python.org/3/library/re.html#re.Match.groups)
220
220
  returned: always
221
221
  type: list
222
222
  sample: ['match 1', 'match 2']
223
223
  match_groupdict:
224
224
  description: Dictionary containing all the named subgroups of the match, keyed by the subgroup name,
225
- as returned by U(https://docs.python.org/3/library/re.html#re.MatchObject.groupdict)
225
+ as returned by U(https://docs.python.org/3/library/re.html#re.Match.groupdict)
226
226
  returned: always
227
227
  type: dict
228
228
  sample:
@@ -104,9 +104,10 @@ EXAMPLES = r"""
104
104
  - cmd.exe /c winrm.cmd quickconfig -quiet -force
105
105
  delegate_to: localhost
106
106
 
107
- - name: Wait for system to become reachable over WinRM
107
+ - name: Wait for system to become reachable over WinRM, polling every 10 seconds
108
108
  ansible.builtin.wait_for_connection:
109
109
  timeout: 900
110
+ sleep: 10
110
111
 
111
112
  - name: Gather facts for first time
112
113
  ansible.builtin.setup:
@@ -183,14 +183,6 @@ options:
183
183
  - This parameter is deprecated as it has no effect with dnf as an underlying package manager
184
184
  and will be removed in ansible-core 2.22.
185
185
  type: bool
186
- keepcache:
187
- description:
188
- - Either V(1) or V(0). Determines whether or not yum keeps the cache of
189
- headers and packages after successful installation.
190
- - This parameter is deprecated as it is only valid in the main configuration
191
- and will be removed in ansible-core 2.20.
192
- choices: ['0', '1']
193
- type: str
194
186
  metadata_expire:
195
187
  description:
196
188
  - Time (in seconds) after which the metadata will expire.
@@ -466,13 +458,7 @@ class YumRepo:
466
458
  for key, value in sorted(self.params.items()):
467
459
  if value is None:
468
460
  continue
469
- if key == 'keepcache':
470
- self.module.deprecate(
471
- "'keepcache' parameter is deprecated as it is only valid in "
472
- "the main configuration.",
473
- version='2.20'
474
- )
475
- elif key == 'async':
461
+ if key == 'async':
476
462
  self.module.deprecate(
477
463
  "'async' parameter is deprecated as it has been removed on systems supported by ansible-core",
478
464
  version='2.22',
@@ -557,7 +543,6 @@ def main():
557
543
  includepkgs=dict(type='list', elements='str'),
558
544
  ip_resolve=dict(choices=['4', '6', 'IPv4', 'IPv6', 'whatever']),
559
545
  keepalive=dict(type='bool'),
560
- keepcache=dict(choices=['0', '1']),
561
546
  metadata_expire=dict(),
562
547
  metadata_expire_filter=dict(
563
548
  choices=[
@@ -2,7 +2,6 @@
2
2
  # Copyright: (c) 2017, Ansible Project
3
3
  # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
4
4
 
5
- from __future__ import annotations
6
5
  from __future__ import annotations
7
6
 
8
7
  import copy
@@ -19,7 +18,6 @@ from ansible._internal._errors import _error_utils
19
18
  from ansible.module_utils.basic import is_executable
20
19
  from ansible._internal._datatag._tags import Origin, TrustedAsTemplate, SourceWasEncrypted
21
20
  from ansible.module_utils._internal._datatag import AnsibleTagHelper
22
- from ansible.module_utils.six import binary_type, text_type
23
21
  from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
24
22
  from ansible.parsing.quoting import unquote
25
23
  from ansible.parsing.utils.yaml import from_yaml
@@ -32,7 +30,7 @@ display = Display()
32
30
 
33
31
  # Tries to determine if a path is inside a role, last dir must be 'tasks'
34
32
  # this is not perfect but people should really avoid 'tasks' dirs outside roles when using Ansible.
35
- RE_TASKS = re.compile(u'(?:^|%s)+tasks%s?$' % (os.path.sep, os.path.sep))
33
+ RE_TASKS = re.compile('(?:^|%s)+tasks%s?$' % (os.path.sep, os.path.sep))
36
34
 
37
35
 
38
36
  class DataLoader:
@@ -49,28 +47,27 @@ class DataLoader:
49
47
  Usage:
50
48
 
51
49
  dl = DataLoader()
52
- # optionally: dl.set_vault_secrets([('default', ansible.parsing.vault.PrompVaultSecret(...),)])
50
+ # optionally: dl.set_vault_secrets([('default', ansible.parsing.vault.PromptVaultSecret(...),)])
53
51
  ds = dl.load('...')
54
52
  ds = dl.load_from_file('/path/to/file')
55
53
  """
56
54
 
57
- def __init__(self):
55
+ def __init__(self) -> None:
58
56
 
59
- self._basedir = '.'
57
+ self._basedir: str = os.path.abspath('.')
60
58
 
61
59
  # NOTE: not effective with forks as the main copy does not get updated.
62
60
  # avoids rereading files
63
- self._FILE_CACHE = dict()
61
+ self._FILE_CACHE: dict[str, object] = {}
64
62
 
65
63
  # NOTE: not thread safe, also issues with forks not returning data to main proc
66
64
  # so they need to be cleaned independently. See WorkerProcess for example.
67
65
  # used to keep track of temp files for cleaning
68
- self._tempfiles = set()
66
+ self._tempfiles: set[str] = set()
69
67
 
70
68
  # initialize the vault stuff with an empty password
71
69
  # TODO: replace with a ref to something that can get the password
72
70
  # a creds/auth provider
73
- self._vaults = {}
74
71
  self._vault = VaultLib()
75
72
  self.set_vault_secrets(None)
76
73
 
@@ -230,23 +227,19 @@ class DataLoader:
230
227
 
231
228
  def set_basedir(self, basedir: str) -> None:
232
229
  """ sets the base directory, used to find files when a relative path is given """
233
-
234
- if basedir is not None:
235
- self._basedir = to_text(basedir)
230
+ self._basedir = os.path.abspath(basedir)
236
231
 
237
232
  def path_dwim(self, given: str) -> str:
238
233
  """
239
234
  make relative paths work like folks expect.
240
235
  """
241
236
 
242
- given = to_text(given, errors='surrogate_or_strict')
243
237
  given = unquote(given)
244
238
 
245
- if given.startswith(to_text(os.path.sep)) or given.startswith(u'~'):
239
+ if given.startswith(os.path.sep) or given.startswith('~'):
246
240
  path = given
247
241
  else:
248
- basedir = to_text(self._basedir, errors='surrogate_or_strict')
249
- path = os.path.join(basedir, given)
242
+ path = os.path.join(self._basedir, given)
250
243
 
251
244
  return unfrackpath(path, follow=False)
252
245
 
@@ -294,10 +287,9 @@ class DataLoader:
294
287
  """
295
288
 
296
289
  search = []
297
- source = to_text(source, errors='surrogate_or_strict')
298
290
 
299
291
  # I have full path, nothing else needs to be looked at
300
- if source.startswith(to_text(os.path.sep)) or source.startswith(u'~'):
292
+ if source.startswith(os.path.sep) or source.startswith('~'):
301
293
  search.append(unfrackpath(source, follow=False))
302
294
  else:
303
295
  # base role/play path + templates/files/vars + relative filename
@@ -364,7 +356,7 @@ class DataLoader:
364
356
  if os.path.exists(to_bytes(test_path, errors='surrogate_or_strict')):
365
357
  result = test_path
366
358
  else:
367
- display.debug(u'evaluation_path:\n\t%s' % '\n\t'.join(paths))
359
+ display.debug('evaluation_path:\n\t%s' % '\n\t'.join(paths))
368
360
  for path in paths:
369
361
  upath = unfrackpath(path, follow=False)
370
362
  b_upath = to_bytes(upath, errors='surrogate_or_strict')
@@ -385,9 +377,9 @@ class DataLoader:
385
377
  search.append(os.path.join(to_bytes(self.get_basedir(), errors='surrogate_or_strict'), b_dirname, b_source))
386
378
  search.append(os.path.join(to_bytes(self.get_basedir(), errors='surrogate_or_strict'), b_source))
387
379
 
388
- display.debug(u'search_path:\n\t%s' % to_text(b'\n\t'.join(search)))
380
+ display.debug('search_path:\n\t%s' % to_text(b'\n\t'.join(search)))
389
381
  for b_candidate in search:
390
- display.vvvvv(u'looking for "%s" at "%s"' % (source, to_text(b_candidate)))
382
+ display.vvvvv('looking for "%s" at "%s"' % (source, to_text(b_candidate)))
391
383
  if os.path.exists(b_candidate):
392
384
  result = to_text(b_candidate)
393
385
  break
@@ -418,11 +410,10 @@ class DataLoader:
418
410
  Temporary files are cleanup in the destructor
419
411
  """
420
412
 
421
- if not file_path or not isinstance(file_path, (binary_type, text_type)):
413
+ if not file_path or not isinstance(file_path, (bytes, str)):
422
414
  raise AnsibleParserError("Invalid filename: '%s'" % to_native(file_path))
423
415
 
424
- b_file_path = to_bytes(file_path, errors='surrogate_or_strict')
425
- if not self.path_exists(b_file_path) or not self.is_file(b_file_path):
416
+ if not self.path_exists(file_path) or not self.is_file(file_path):
426
417
  raise AnsibleFileNotFound(file_name=file_path)
427
418
 
428
419
  real_path = self.path_dwim(file_path)
@@ -480,7 +471,7 @@ class DataLoader:
480
471
  """
481
472
 
482
473
  b_path = to_bytes(os.path.join(path, name))
483
- found = []
474
+ found: list[str] = []
484
475
 
485
476
  if extensions is None:
486
477
  # Look for file with no extension first to find dir before file
@@ -489,27 +480,29 @@ class DataLoader:
489
480
  for ext in extensions:
490
481
 
491
482
  if '.' in ext:
492
- full_path = b_path + to_bytes(ext)
483
+ b_full_path = b_path + to_bytes(ext)
493
484
  elif ext:
494
- full_path = b'.'.join([b_path, to_bytes(ext)])
485
+ b_full_path = b'.'.join([b_path, to_bytes(ext)])
495
486
  else:
496
- full_path = b_path
487
+ b_full_path = b_path
488
+
489
+ full_path = to_text(b_full_path)
497
490
 
498
491
  if self.path_exists(full_path):
499
492
  if self.is_directory(full_path):
500
493
  if allow_dir:
501
- found.extend(self._get_dir_vars_files(to_text(full_path), extensions))
494
+ found.extend(self._get_dir_vars_files(full_path, extensions))
502
495
  else:
503
496
  continue
504
497
  else:
505
- found.append(to_text(full_path))
498
+ found.append(full_path)
506
499
  break
507
500
  return found
508
501
 
509
502
  def _get_dir_vars_files(self, path: str, extensions: list[str]) -> list[str]:
510
503
  found = []
511
504
  for spath in sorted(self.list_directory(path)):
512
- if not spath.startswith(u'.') and not spath.endswith(u'~'): # skip hidden and backups
505
+ if not spath.startswith('.') and not spath.endswith('~'): # skip hidden and backups
513
506
 
514
507
  ext = os.path.splitext(spath)[-1]
515
508
  full_spath = os.path.join(path, spath)
@@ -130,6 +130,7 @@ class ModuleArgsParser:
130
130
  # HACK: why are these not FieldAttributes on task with a post-validate to check usage?
131
131
  self._task_attrs.update(['local_action', 'static'])
132
132
  self._task_attrs = frozenset(self._task_attrs)
133
+ self._resolved_action = None
133
134
 
134
135
  def _split_module_string(self, module_string: str) -> tuple[str, str]:
135
136
  """
@@ -344,6 +345,8 @@ class ModuleArgsParser:
344
345
  raise e
345
346
 
346
347
  is_action_candidate = context.resolved and bool(context.redirect_list)
348
+ if is_action_candidate:
349
+ self._resolved_action = context.resolved_fqcn
347
350
 
348
351
  if is_action_candidate:
349
352
  # finding more than one module name is a problem
@@ -59,7 +59,6 @@ except ImportError:
59
59
 
60
60
  from ansible.errors import AnsibleError, AnsibleAssertionError
61
61
  from ansible import constants as C
62
- from ansible.module_utils.six import binary_type
63
62
  from ansible.module_utils.common.text.converters import to_bytes, to_text, to_native
64
63
  from ansible.utils.display import Display
65
64
  from ansible.utils.path import makedirs_safe, unfrackpath
@@ -1237,7 +1236,7 @@ class VaultAES256:
1237
1236
 
1238
1237
  It would be nice if there were a library for this but hey.
1239
1238
  """
1240
- if not (isinstance(b_a, binary_type) and isinstance(b_b, binary_type)):
1239
+ if not (isinstance(b_a, bytes) and isinstance(b_b, bytes)):
1241
1240
  raise TypeError('_is_equal can only be used to compare two byte strings')
1242
1241
 
1243
1242
  # http://codahale.com/a-lesson-in-timing-attacks/
ansible/playbook/base.py CHANGED
@@ -19,7 +19,6 @@ from ansible import context
19
19
  from ansible.errors import AnsibleError, AnsibleParserError, AnsibleAssertionError, AnsibleValueOmittedError, AnsibleFieldAttributeError
20
20
  from ansible.module_utils.datatag import native_type_name
21
21
  from ansible._internal._datatag._tags import Origin
22
- from ansible.module_utils.six import string_types
23
22
  from ansible.module_utils.parsing.convert_bool import boolean
24
23
  from ansible.module_utils.common.sentinel import Sentinel
25
24
  from ansible.module_utils.common.text.converters import to_text
@@ -37,7 +36,7 @@ display = Display()
37
36
  def _validate_action_group_metadata(action, found_group_metadata, fq_group_name):
38
37
  valid_metadata = {
39
38
  'extend_group': {
40
- 'types': (list, string_types,),
39
+ 'types': (list, str,),
41
40
  'errortype': 'list',
42
41
  },
43
42
  }
@@ -204,7 +203,7 @@ class FieldAttributeBase:
204
203
  value = self.set_to_context(attr.name)
205
204
 
206
205
  valid_values = frozenset(('always', 'on_failed', 'on_unreachable', 'on_skipped', 'never'))
207
- if value and isinstance(value, string_types) and value not in valid_values:
206
+ if value and isinstance(value, str) and value not in valid_values:
208
207
  raise AnsibleParserError("'%s' is not a valid value for debugger. Must be one of %s" % (value, ', '.join(valid_values)), obj=self.get_ds())
209
208
  return value
210
209
 
@@ -350,14 +349,14 @@ class FieldAttributeBase:
350
349
  found_group_metadata = False
351
350
  for action in action_group:
352
351
  # Everything should be a string except the metadata entry
353
- if not isinstance(action, string_types):
352
+ if not isinstance(action, str):
354
353
  _validate_action_group_metadata(action, found_group_metadata, fq_group_name)
355
354
 
356
355
  if isinstance(action['metadata'], dict):
357
356
  found_group_metadata = True
358
357
 
359
358
  include_groups = action['metadata'].get('extend_group', [])
360
- if isinstance(include_groups, string_types):
359
+ if isinstance(include_groups, str):
361
360
  include_groups = [include_groups]
362
361
  if not isinstance(include_groups, list):
363
362
  # Bad entries may be a warning above, but prevent tracebacks by setting it back to the acceptable type.
@@ -472,7 +471,7 @@ class FieldAttributeBase:
472
471
  elif attribute.isa == 'percent':
473
472
  # special value, which may be an integer or float
474
473
  # with an optional '%' at the end
475
- if isinstance(value, string_types) and '%' in value:
474
+ if isinstance(value, str) and '%' in value:
476
475
  value = value.replace('%', '')
477
476
  value = float(value)
478
477
  elif attribute.isa == 'list':
@@ -660,8 +659,8 @@ class FieldAttributeBase:
660
659
  attrs = {}
661
660
  for (name, attribute) in self.fattributes.items():
662
661
  attr = getattr(self, name)
663
- if attribute.isa == 'class' and hasattr(attr, 'serialize'):
664
- attrs[name] = attr.serialize()
662
+ if attribute.isa == 'class':
663
+ attrs[name] = attr.dump_attrs()
665
664
  else:
666
665
  attrs[name] = attr
667
666
  return attrs
@@ -675,60 +674,13 @@ class FieldAttributeBase:
675
674
  attribute = self.fattributes[attr]
676
675
  if attribute.isa == 'class' and isinstance(value, dict):
677
676
  obj = attribute.class_type()
678
- obj.deserialize(value)
677
+ obj.from_attrs(value)
679
678
  setattr(self, attr, obj)
680
679
  else:
681
680
  setattr(self, attr, value)
682
681
  else:
683
682
  setattr(self, attr, value) # overridden dump_attrs in derived types may dump attributes which are not field attributes
684
683
 
685
- # from_attrs is only used to create a finalized task
686
- # from attrs from the Worker/TaskExecutor
687
- # Those attrs are finalized and squashed in the TE
688
- # and controller side use needs to reflect that
689
- self._finalized = True
690
- self._squashed = True
691
-
692
- def serialize(self):
693
- """
694
- Serializes the object derived from the base object into
695
- a dictionary of values. This only serializes the field
696
- attributes for the object, so this may need to be overridden
697
- for any classes which wish to add additional items not stored
698
- as field attributes.
699
- """
700
-
701
- repr = self.dump_attrs()
702
-
703
- # serialize the uuid field
704
- repr['uuid'] = self._uuid
705
- repr['finalized'] = self._finalized
706
- repr['squashed'] = self._squashed
707
-
708
- return repr
709
-
710
- def deserialize(self, data):
711
- """
712
- Given a dictionary of values, load up the field attributes for
713
- this object. As with serialize(), if there are any non-field
714
- attribute data members, this method will need to be overridden
715
- and extended.
716
- """
717
-
718
- if not isinstance(data, dict):
719
- raise AnsibleAssertionError('data (%s) should be a dict but is a %s' % (data, type(data)))
720
-
721
- for (name, attribute) in self.fattributes.items():
722
- if name in data:
723
- setattr(self, name, data[name])
724
- else:
725
- self.set_to_context(name)
726
-
727
- # restore the UUID field
728
- setattr(self, '_uuid', data.get('uuid'))
729
- self._finalized = data.get('finalized', False)
730
- self._squashed = data.get('squashed', False)
731
-
732
684
 
733
685
  class Base(FieldAttributeBase):
734
686