ansible-core 2.19.0b3__py3-none-any.whl → 2.19.0b5__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.
Files changed (174) hide show
  1. ansible/_internal/__init__.py +2 -2
  2. ansible/_internal/_collection_proxy.py +1 -1
  3. ansible/_internal/_errors/_alarm_timeout.py +66 -0
  4. ansible/_internal/_errors/_captured.py +25 -30
  5. ansible/_internal/_errors/_error_factory.py +89 -0
  6. ansible/_internal/_errors/_error_utils.py +240 -0
  7. ansible/_internal/_errors/_task_timeout.py +28 -0
  8. ansible/_internal/_event_formatting.py +127 -0
  9. ansible/_internal/_json/__init__.py +6 -6
  10. ansible/_internal/_json/_profiles/_cache_persistence.py +2 -0
  11. ansible/_internal/_json/_profiles/_inventory_legacy.py +1 -1
  12. ansible/_internal/_json/_profiles/_legacy.py +3 -11
  13. ansible/_internal/_ssh/__init__.py +0 -0
  14. ansible/_internal/_ssh/_agent_launch.py +91 -0
  15. ansible/{utils → _internal/_ssh}/_ssh_agent.py +55 -93
  16. ansible/_internal/_templating/__init__.py +5 -3
  17. ansible/_internal/_templating/_datatag.py +2 -1
  18. ansible/_internal/_templating/_engine.py +3 -4
  19. ansible/_internal/_templating/_jinja_bits.py +21 -16
  20. ansible/_internal/_templating/_jinja_common.py +18 -27
  21. ansible/_internal/_templating/_jinja_plugins.py +31 -3
  22. ansible/_internal/_templating/_lazy_containers.py +5 -5
  23. ansible/_internal/_templating/_transform.py +20 -19
  24. ansible/_internal/_templating/_utils.py +1 -1
  25. ansible/_internal/_testing.py +26 -0
  26. ansible/_internal/_yaml/_dumper.py +1 -1
  27. ansible/_internal/_yaml/_errors.py +7 -7
  28. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/true_type.py +1 -1
  29. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/unmask.py +1 -1
  30. ansible/cli/__init__.py +5 -82
  31. ansible/cli/arguments/option_helpers.py +8 -5
  32. ansible/cli/doc.py +84 -28
  33. ansible/cli/inventory.py +1 -1
  34. ansible/compat/importlib_resources.py +9 -12
  35. ansible/config/base.yml +27 -23
  36. ansible/config/manager.py +142 -101
  37. ansible/constants.py +1 -1
  38. ansible/errors/__init__.py +96 -49
  39. ansible/executor/module_common.py +8 -10
  40. ansible/executor/powershell/async_watchdog.ps1 +2 -2
  41. ansible/executor/powershell/async_wrapper.ps1 +3 -3
  42. ansible/executor/powershell/become_wrapper.ps1 +20 -2
  43. ansible/executor/powershell/bootstrap_wrapper.ps1 +28 -6
  44. ansible/executor/powershell/coverage_wrapper.ps1 +15 -6
  45. ansible/executor/powershell/exec_wrapper.ps1 +219 -6
  46. ansible/executor/powershell/module_manifest.py +52 -0
  47. ansible/executor/powershell/module_wrapper.ps1 +47 -21
  48. ansible/executor/powershell/powershell_expand_user.ps1 +20 -0
  49. ansible/executor/powershell/powershell_mkdtemp.ps1 +17 -0
  50. ansible/executor/process/worker.py +38 -113
  51. ansible/executor/task_executor.py +26 -61
  52. ansible/executor/task_result.py +2 -4
  53. ansible/galaxy/collection/__init__.py +1 -4
  54. ansible/inventory/manager.py +1 -0
  55. ansible/module_utils/_internal/__init__.py +0 -3
  56. ansible/module_utils/_internal/_ambient_context.py +3 -3
  57. ansible/module_utils/_internal/_ansiballz.py +4 -2
  58. ansible/module_utils/_internal/_datatag/__init__.py +20 -14
  59. ansible/module_utils/_internal/_datatag/_tags.py +2 -2
  60. ansible/module_utils/_internal/_deprecator.py +66 -48
  61. ansible/module_utils/_internal/_errors.py +88 -17
  62. ansible/module_utils/_internal/_event_utils.py +61 -0
  63. ansible/module_utils/_internal/_json/_profiles/__init__.py +21 -4
  64. ansible/module_utils/_internal/_json/_profiles/_module_legacy_c2m.py +2 -0
  65. ansible/module_utils/_internal/_json/_profiles/_module_legacy_m2c.py +2 -0
  66. ansible/module_utils/_internal/_json/_profiles/_tagless.py +3 -1
  67. ansible/module_utils/{common/messages.py → _internal/_messages.py} +28 -47
  68. ansible/module_utils/_internal/_patches/_dataclass_annotation_patch.py +1 -3
  69. ansible/module_utils/_internal/_plugin_info.py +1 -1
  70. ansible/module_utils/_internal/_stack.py +22 -0
  71. ansible/module_utils/_internal/_text_utils.py +6 -0
  72. ansible/module_utils/_internal/_traceback.py +11 -8
  73. ansible/module_utils/ansible_release.py +1 -1
  74. ansible/module_utils/basic.py +49 -15
  75. ansible/module_utils/common/arg_spec.py +2 -2
  76. ansible/module_utils/common/collections.py +6 -0
  77. ansible/module_utils/common/json.py +2 -2
  78. ansible/module_utils/common/text/converters.py +3 -3
  79. ansible/module_utils/common/validation.py +1 -1
  80. ansible/module_utils/common/warnings.py +80 -23
  81. ansible/module_utils/common/yaml.py +1 -1
  82. ansible/module_utils/datatag.py +5 -2
  83. ansible/module_utils/facts/system/distribution.py +16 -3
  84. ansible/module_utils/facts/virtual/linux.py +2 -2
  85. ansible/module_utils/parsing/convert_bool.py +6 -0
  86. ansible/module_utils/service.py +2 -9
  87. ansible/modules/apt_repository.py +7 -29
  88. ansible/modules/assemble.py +4 -4
  89. ansible/modules/async_status.py +13 -11
  90. ansible/modules/async_wrapper.py +5 -5
  91. ansible/modules/cron.py +3 -5
  92. ansible/modules/dnf5.py +15 -22
  93. ansible/modules/git.py +1 -6
  94. ansible/modules/hostname.py +0 -1
  95. ansible/modules/pip.py +2 -4
  96. ansible/modules/service.py +3 -9
  97. ansible/modules/sysvinit.py +3 -3
  98. ansible/parsing/ajson.py +3 -5
  99. ansible/parsing/dataloader.py +4 -4
  100. ansible/parsing/mod_args.py +1 -1
  101. ansible/parsing/plugin_docs.py +2 -2
  102. ansible/parsing/utils/yaml.py +3 -3
  103. ansible/parsing/vault/__init__.py +4 -4
  104. ansible/playbook/playbook_include.py +1 -1
  105. ansible/playbook/taggable.py +0 -3
  106. ansible/plugins/__init__.py +0 -25
  107. ansible/plugins/action/__init__.py +9 -32
  108. ansible/plugins/action/add_host.py +1 -1
  109. ansible/plugins/action/assemble.py +8 -16
  110. ansible/plugins/action/async_status.py +7 -2
  111. ansible/plugins/action/copy.py +8 -7
  112. ansible/plugins/action/gather_facts.py +8 -8
  113. ansible/plugins/action/package.py +5 -8
  114. ansible/plugins/action/script.py +8 -15
  115. ansible/plugins/action/service.py +3 -7
  116. ansible/plugins/action/template.py +6 -8
  117. ansible/plugins/action/unarchive.py +5 -15
  118. ansible/plugins/action/uri.py +9 -20
  119. ansible/plugins/callback/__init__.py +4 -6
  120. ansible/plugins/callback/junit.py +4 -2
  121. ansible/plugins/connection/local.py +2 -2
  122. ansible/plugins/connection/ssh.py +17 -9
  123. ansible/plugins/connection/winrm.py +5 -2
  124. ansible/plugins/doc_fragments/constructed.py +2 -2
  125. ansible/plugins/filter/core.py +13 -6
  126. ansible/plugins/filter/encryption.py +4 -4
  127. ansible/plugins/inventory/__init__.py +11 -10
  128. ansible/plugins/inventory/script.py +1 -1
  129. ansible/plugins/list.py +69 -16
  130. ansible/plugins/loader.py +10 -9
  131. ansible/plugins/lookup/csvfile.py +16 -71
  132. ansible/plugins/lookup/first_found.py +2 -1
  133. ansible/plugins/shell/__init__.py +56 -2
  134. ansible/plugins/shell/powershell.py +66 -9
  135. ansible/plugins/shell/sh.py +9 -5
  136. ansible/plugins/test/core.py +21 -15
  137. ansible/plugins/test/finished.yml +1 -1
  138. ansible/plugins/test/uri.py +2 -5
  139. ansible/release.py +1 -1
  140. ansible/template/__init__.py +30 -2
  141. ansible/utils/collection_loader/__init__.py +2 -0
  142. ansible/utils/display.py +107 -128
  143. ansible/utils/hashing.py +0 -1
  144. ansible/utils/listify.py +6 -4
  145. ansible/utils/plugin_docs.py +2 -1
  146. ansible/utils/unsafe_proxy.py +1 -1
  147. ansible/vars/hostvars.py +1 -1
  148. {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.dist-info}/METADATA +3 -2
  149. {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.dist-info}/RECORD +173 -161
  150. {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.dist-info}/WHEEL +1 -1
  151. ansible_test/_data/completion/docker.txt +3 -3
  152. ansible_test/_data/completion/remote.txt +1 -0
  153. ansible_test/_data/requirements/sanity.ansible-doc.txt +1 -1
  154. ansible_test/_data/requirements/sanity.changelog.txt +2 -2
  155. ansible_test/_data/requirements/sanity.pep8.txt +1 -1
  156. ansible_test/_data/requirements/sanity.pylint.txt +4 -4
  157. ansible_test/_data/requirements/sanity.yamllint.txt +1 -1
  158. ansible_test/_internal/util.py +20 -0
  159. ansible_test/_util/controller/sanity/pylint/config/ansible-test-target.cfg +1 -0
  160. ansible_test/_util/controller/sanity/pylint/config/ansible-test.cfg +1 -0
  161. ansible_test/_util/controller/sanity/pylint/config/code-smell.cfg +1 -0
  162. ansible_test/_util/controller/sanity/pylint/config/collection.cfg +1 -0
  163. ansible_test/_util/controller/sanity/pylint/config/default.cfg +1 -0
  164. ansible_test/_util/controller/sanity/pylint/plugins/deprecated_calls.py +73 -8
  165. ansible_test/_util/target/setup/bootstrap.sh +31 -0
  166. ansible/_internal/_errors/_utils.py +0 -310
  167. {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.dist-info}/entry_points.txt +0 -0
  168. {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.dist-info/licenses}/COPYING +0 -0
  169. {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.dist-info/licenses/licenses}/Apache-License.txt +0 -0
  170. {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.dist-info/licenses/licenses}/BSD-3-Clause.txt +0 -0
  171. {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.dist-info/licenses/licenses}/MIT-license.txt +0 -0
  172. {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.dist-info/licenses/licenses}/PSF-license.txt +0 -0
  173. {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.dist-info/licenses/licenses}/simplified_bsd.txt +0 -0
  174. {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.dist-info}/top_level.txt +0 -0
@@ -151,7 +151,7 @@ class LinuxVirtual(Virtual):
151
151
  sys_vendor = get_file_content('/sys/devices/virtual/dmi/id/sys_vendor')
152
152
  product_family = get_file_content('/sys/devices/virtual/dmi/id/product_family')
153
153
 
154
- if product_name in ('KVM', 'KVM Server', 'Bochs', 'AHV'):
154
+ if product_name in ('KVM', 'KVM Server', 'Bochs', 'AHV', 'CloudStack KVM Hypervisor'):
155
155
  guest_tech.add('kvm')
156
156
  if not found_virt:
157
157
  virtual_facts['virtualization_type'] = 'kvm'
@@ -201,7 +201,7 @@ class LinuxVirtual(Virtual):
201
201
  virtual_facts['virtualization_type'] = 'virtualbox'
202
202
  found_virt = True
203
203
 
204
- if bios_vendor in ('Amazon EC2', 'DigitalOcean', 'Hetzner'):
204
+ if bios_vendor in ('Amazon EC2', 'DigitalOcean', 'Hetzner', 'Linode'):
205
205
  guest_tech.add('kvm')
206
206
  if not found_virt:
207
207
  virtual_facts['virtualization_type'] = 'kvm'
@@ -3,6 +3,8 @@
3
3
 
4
4
  from __future__ import annotations
5
5
 
6
+ import collections.abc as c
7
+
6
8
  from ansible.module_utils.six import binary_type, text_type
7
9
  from ansible.module_utils.common.text.converters import to_text
8
10
 
@@ -17,9 +19,13 @@ def boolean(value, strict=True):
17
19
  return value
18
20
 
19
21
  normalized_value = value
22
+
20
23
  if isinstance(value, (text_type, binary_type)):
21
24
  normalized_value = to_text(value, errors='surrogate_or_strict').lower().strip()
22
25
 
26
+ if not isinstance(value, c.Hashable):
27
+ normalized_value = None # prevent unhashable types from bombing, but keep the rest of the existing fallback/error behavior
28
+
23
29
  if normalized_value in BOOLEANS_TRUE:
24
30
  return True
25
31
  elif normalized_value in BOOLEANS_FALSE or not strict:
@@ -36,7 +36,7 @@ import select
36
36
  import shlex
37
37
  import subprocess
38
38
 
39
- from ansible.module_utils.six import PY2, b
39
+ from ansible.module_utils.six import b
40
40
  from ansible.module_utils.common.text.converters import to_bytes, to_text
41
41
 
42
42
 
@@ -187,12 +187,8 @@ def daemonize(module, cmd):
187
187
  if pid == 0:
188
188
  os.close(pipe[0])
189
189
 
190
- # if command is string deal with py2 vs py3 conversions for shlex
191
190
  if not isinstance(cmd, list):
192
- if PY2:
193
- cmd = shlex.split(to_bytes(cmd, errors=errors))
194
- else:
195
- cmd = shlex.split(to_text(cmd, errors=errors))
191
+ cmd = shlex.split(to_text(cmd, errors=errors))
196
192
 
197
193
  # make sure we always use byte strings
198
194
  run_cmd = []
@@ -247,9 +243,6 @@ def daemonize(module, cmd):
247
243
  break
248
244
  return_data += to_bytes(data, errors=errors)
249
245
 
250
- # Note: no need to specify encoding on py3 as this module sends the
251
- # pickle to itself (thus same python interpreter so we aren't mixing
252
- # py2 and py3)
253
246
  return pickle.loads(to_bytes(return_data, errors=errors))
254
247
 
255
248
 
@@ -88,8 +88,8 @@ options:
88
88
  description:
89
89
  - Whether to automatically try to install the Python apt library or not, if it is not already installed.
90
90
  Without this library, the module does not work.
91
- - Runs C(apt-get install python-apt) for Python 2, and C(apt-get install python3-apt) for Python 3.
92
- - Only works with the system Python 2 or Python 3. If you are using a Python on the remote that is not
91
+ - Runs C(apt-get install python3-apt).
92
+ - Only works with the system Python. If you are using a Python on the remote that is not
93
93
  the system Python, set O(install_python_apt=false) and ensure that the Python apt library
94
94
  for your Python version is installed some other way.
95
95
  type: bool
@@ -98,8 +98,7 @@ author:
98
98
  - Alexander Saltanov (@sashka)
99
99
  version_added: "0.7"
100
100
  requirements:
101
- - python-apt (python 2)
102
- - python3-apt (python 3)
101
+ - python3-apt
103
102
  - apt-key or gpg
104
103
  """
105
104
 
@@ -232,14 +231,15 @@ class SourcesList(object):
232
231
  self.files_mapping = {} # internal DS for tracking symlinks
233
232
  # Repositories that we're adding -- used to implement mode param
234
233
  self.new_repos = set()
235
- self.default_file = self._apt_cfg_file('Dir::Etc::sourcelist')
234
+ self.default_file = apt_pkg.config.find_file('Dir::Etc::sourcelist')
236
235
 
237
236
  # read sources.list if it exists
238
237
  if os.path.isfile(self.default_file):
239
238
  self.load(self.default_file)
240
239
 
241
240
  # read sources.list.d
242
- for file in glob.iglob('%s/*.list' % self._apt_cfg_dir('Dir::Etc::sourceparts')):
241
+ self.sources_dir = apt_pkg.config.find_dir('Dir::Etc::sourceparts')
242
+ for file in glob.iglob(f'{self.sources_dir}/*.list'):
243
243
  if os.path.islink(file):
244
244
  self.files_mapping[file] = os.readlink(file)
245
245
  self.load(file)
@@ -255,7 +255,7 @@ class SourcesList(object):
255
255
  if '/' in filename:
256
256
  return filename
257
257
  else:
258
- return os.path.abspath(os.path.join(self._apt_cfg_dir('Dir::Etc::sourceparts'), filename))
258
+ return os.path.abspath(os.path.join(self.sources_dir, filename))
259
259
 
260
260
  def _suggest_filename(self, line):
261
261
  def _cleanup_filename(s):
@@ -313,28 +313,6 @@ class SourcesList(object):
313
313
 
314
314
  return valid, enabled, source, comment
315
315
 
316
- @staticmethod
317
- def _apt_cfg_file(filespec):
318
- """
319
- Wrapper for `apt_pkg` module for running with Python 2.5
320
- """
321
- try:
322
- result = apt_pkg.config.find_file(filespec)
323
- except AttributeError:
324
- result = apt_pkg.Config.FindFile(filespec)
325
- return result
326
-
327
- @staticmethod
328
- def _apt_cfg_dir(dirspec):
329
- """
330
- Wrapper for `apt_pkg` module for running with Python 2.5
331
- """
332
- try:
333
- result = apt_pkg.config.find_dir(dirspec)
334
- except AttributeError:
335
- result = apt_pkg.Config.FindDir(dirspec)
336
- return result
337
-
338
316
  def load(self, file):
339
317
  group = []
340
318
  f = open(file, 'r')
@@ -181,7 +181,7 @@ def assemble_from_fragments(src_path, delimiter=None, compiled_regexp=None, igno
181
181
  return temp_path
182
182
 
183
183
 
184
- def cleanup(path, result=None):
184
+ def cleanup(module, path, result=None):
185
185
  # cleanup just in case
186
186
  if os.path.exists(path):
187
187
  try:
@@ -189,7 +189,7 @@ def cleanup(path, result=None):
189
189
  except (IOError, OSError) as e:
190
190
  # don't error on possible race conditions, but keep warning
191
191
  if result is not None:
192
- result['warnings'] = ['Unable to remove temp file (%s): %s' % (path, to_native(e))]
192
+ module.warn('Unable to remove temp file (%s): %s' % (path, to_native(e)))
193
193
 
194
194
 
195
195
  def main():
@@ -261,7 +261,7 @@ def main():
261
261
  (rc, out, err) = module.run_command(validate % path)
262
262
  result['validation'] = dict(rc=rc, stdout=out, stderr=err)
263
263
  if rc != 0:
264
- cleanup(path)
264
+ cleanup(module, path)
265
265
  module.fail_json(msg="failed to validate: rc:%s error:%s" % (rc, err))
266
266
  if backup and dest_hash is not None:
267
267
  result['backup_file'] = module.backup_local(dest)
@@ -269,7 +269,7 @@ def main():
269
269
  module.atomic_move(path, dest, unsafe_writes=module.params['unsafe_writes'])
270
270
  changed = True
271
271
 
272
- cleanup(path, result)
272
+ cleanup(module, path, result)
273
273
 
274
274
  # handle file permissions
275
275
  file_args = module.load_file_common_arguments(module.params)
@@ -28,6 +28,8 @@ options:
28
28
  type: str
29
29
  choices: [ cleanup, status ]
30
30
  default: status
31
+ notes:
32
+ - The RV(started) and RV(finished) return values were updated to return V(True) or V(False) instead of V(1) or V(0) in ansible-core 2.19.
31
33
  extends_documentation_fragment:
32
34
  - action_common_attributes
33
35
  - action_common_attributes.flow
@@ -85,15 +87,15 @@ ansible_job_id:
85
87
  type: str
86
88
  sample: '360874038559.4169'
87
89
  finished:
88
- description: Whether the asynchronous job has finished (V(1)) or not (V(0))
90
+ description: Whether the asynchronous job has finished or not
89
91
  returned: always
90
- type: int
91
- sample: 1
92
+ type: bool
93
+ sample: true
92
94
  started:
93
- description: Whether the asynchronous job has started (V(1)) or not (V(0))
95
+ description: Whether the asynchronous job has started or not
94
96
  returned: always
95
- type: int
96
- sample: 1
97
+ type: bool
98
+ sample: true
97
99
  stdout:
98
100
  description: Any output returned by async_wrapper
99
101
  returned: always
@@ -134,7 +136,7 @@ def main():
134
136
  log_path = os.path.join(async_dir, jid)
135
137
 
136
138
  if not os.path.exists(log_path):
137
- module.fail_json(msg="could not find job", ansible_job_id=jid, started=1, finished=1)
139
+ module.fail_json(msg="could not find job", ansible_job_id=jid, started=True, finished=True)
138
140
 
139
141
  if mode == 'cleanup':
140
142
  os.unlink(log_path)
@@ -151,16 +153,16 @@ def main():
151
153
  except Exception:
152
154
  if not data:
153
155
  # file not written yet? That means it is running
154
- module.exit_json(results_file=log_path, ansible_job_id=jid, started=1, finished=0)
156
+ module.exit_json(results_file=log_path, ansible_job_id=jid, started=True, finished=False)
155
157
  else:
156
158
  module.fail_json(ansible_job_id=jid, results_file=log_path,
157
- msg="Could not parse job output: %s" % data, started=1, finished=1)
159
+ msg="Could not parse job output: %s" % data, started=True, finished=True)
158
160
 
159
161
  if 'started' not in data:
160
- data['finished'] = 1
162
+ data['finished'] = True
161
163
  data['ansible_job_id'] = jid
162
164
  elif 'finished' not in data:
163
- data['finished'] = 0
165
+ data['finished'] = False
164
166
 
165
167
  # just write the module output directly to stdout and exit; bypass other processing done by exit_json since it's already been done
166
168
  print(f"\n{json.dumps(data)}") # pylint: disable=ansible-bad-function
@@ -149,7 +149,7 @@ def _run_module(wrapped_cmd, jid):
149
149
 
150
150
  # DTFIX-FUTURE: needs rework for serialization profiles
151
151
 
152
- jwrite({"started": 1, "finished": 0, "ansible_job_id": jid})
152
+ jwrite({"started": True, "finished": False, "ansible_job_id": jid})
153
153
 
154
154
  result = {}
155
155
 
@@ -203,7 +203,7 @@ def _run_module(wrapped_cmd, jid):
203
203
  except (OSError, IOError):
204
204
  e = sys.exc_info()[1]
205
205
  result = {
206
- "failed": 1,
206
+ "failed": True,
207
207
  "cmd": wrapped_cmd,
208
208
  "msg": to_text(e),
209
209
  "outdata": outdata, # temporary notice only
@@ -214,7 +214,7 @@ def _run_module(wrapped_cmd, jid):
214
214
 
215
215
  except (ValueError, Exception):
216
216
  result = {
217
- "failed": 1,
217
+ "failed": True,
218
218
  "cmd": wrapped_cmd,
219
219
  "data": outdata, # temporary notice only
220
220
  "stderr": stderr,
@@ -260,7 +260,7 @@ def main():
260
260
  _make_temp_dir(jobdir)
261
261
  except Exception as e:
262
262
  end({
263
- "failed": 1,
263
+ "failed": True,
264
264
  "msg": "could not create directory: %s - %s" % (jobdir, to_text(e)),
265
265
  "exception": to_text(traceback.format_exc()), # NB: task executor compat will coerce to the correct dataclass type
266
266
  }, 1)
@@ -293,7 +293,7 @@ def main():
293
293
  continue
294
294
 
295
295
  notice("Return async_wrapper task started.")
296
- end({"failed": 0, "started": 1, "finished": 0, "ansible_job_id": jid, "results_file": job_path,
296
+ end({"failed": False, "started": True, "finished": False, "ansible_job_id": jid, "results_file": job_path,
297
297
  "_ansible_suppress_tmpdir_delete": (not preserve_tmp)}, 0)
298
298
  else:
299
299
  # The actual wrapper process
ansible/modules/cron.py CHANGED
@@ -618,7 +618,6 @@ def main():
618
618
 
619
619
  changed = False
620
620
  res_args = dict()
621
- warnings = list()
622
621
 
623
622
  if cron_file:
624
623
 
@@ -627,8 +626,8 @@ def main():
627
626
 
628
627
  cron_file_basename = os.path.basename(cron_file)
629
628
  if not re.search(r'^[A-Z0-9_-]+$', cron_file_basename, re.I):
630
- warnings.append('Filename portion of cron_file ("%s") should consist' % cron_file_basename +
631
- ' solely of upper- and lower-case letters, digits, underscores, and hyphens')
629
+ module.warn('Filename portion of cron_file ("%s") should consist' % cron_file_basename +
630
+ ' solely of upper- and lower-case letters, digits, underscores, and hyphens')
632
631
 
633
632
  # Ensure all files generated are only writable by the owning user. Primarily relevant for the cron_file option.
634
633
  os.umask(int('022', 8))
@@ -693,7 +692,7 @@ def main():
693
692
  if do_install:
694
693
  for char in ['\r', '\n']:
695
694
  if char in job.strip('\r\n'):
696
- warnings.append('Job should not contain line breaks')
695
+ module.warn('Job should not contain line breaks')
697
696
  break
698
697
 
699
698
  job = crontab.get_cron_job(minute, hour, day, month, weekday, job, special_time, disabled)
@@ -734,7 +733,6 @@ def main():
734
733
  res_args = dict(
735
734
  jobs=crontab.get_jobnames(),
736
735
  envs=crontab.get_envnames(),
737
- warnings=warnings,
738
736
  changed=changed
739
737
  )
740
738
 
ansible/modules/dnf5.py CHANGED
@@ -365,7 +365,7 @@ from ansible.module_utils.yumdnf import YumDnf, yumdnf_argument_spec
365
365
 
366
366
  libdnf5 = None
367
367
  # Through dnf5-5.2.12 all exceptions raised through swig became RuntimeError
368
- LIBDNF5_ERROR = RuntimeError
368
+ LIBDNF5_ERRORS = RuntimeError
369
369
 
370
370
 
371
371
  def is_installed(base, spec):
@@ -423,7 +423,9 @@ def is_newer_version_installed(base, spec):
423
423
 
424
424
  try:
425
425
  spec_nevra = next(iter(libdnf5.rpm.Nevra.parse(spec)))
426
- except (LIBDNF5_ERROR, StopIteration):
426
+ except LIBDNF5_ERRORS:
427
+ return False
428
+ except StopIteration:
427
429
  return False
428
430
 
429
431
  spec_version = spec_nevra.get_version()
@@ -517,7 +519,7 @@ class Dnf5Module(YumDnf):
517
519
  os.environ["LANGUAGE"] = os.environ["LANG"] = locale
518
520
 
519
521
  global libdnf5
520
- global LIBDNF5_ERROR
522
+ global LIBDNF5_ERRORS
521
523
  has_dnf = True
522
524
  try:
523
525
  import libdnf5 # type: ignore[import]
@@ -526,7 +528,7 @@ class Dnf5Module(YumDnf):
526
528
 
527
529
  try:
528
530
  import libdnf5.exception # type: ignore[import-not-found]
529
- LIBDNF5_ERROR = libdnf5.exception.Error
531
+ LIBDNF5_ERRORS = (libdnf5.exception.Error, libdnf5.exception.NonLibdnf5Exception)
530
532
  except (ImportError, AttributeError):
531
533
  pass
532
534
 
@@ -581,15 +583,7 @@ class Dnf5Module(YumDnf):
581
583
  if self.conf_file:
582
584
  conf.config_file_path = self.conf_file
583
585
 
584
- try:
585
- base.load_config()
586
- except LIBDNF5_ERROR as e:
587
- self.module.fail_json(
588
- msg=str(e),
589
- conf_file=self.conf_file,
590
- failures=[],
591
- rc=1,
592
- )
586
+ base.load_config()
593
587
 
594
588
  if self.releasever is not None:
595
589
  variables = base.get_vars()
@@ -722,6 +716,7 @@ class Dnf5Module(YumDnf):
722
716
  if self.security:
723
717
  types.append("security")
724
718
  advisory_query.filter_type(types)
719
+ conf.skip_unavailable = True # ignore packages that are of a different type, for backwards compat
725
720
  settings.set_advisory_filter(advisory_query)
726
721
 
727
722
  goal = libdnf5.base.Goal(base)
@@ -744,19 +739,13 @@ class Dnf5Module(YumDnf):
744
739
  goal.add_install(spec, settings)
745
740
  elif self.state in {"absent", "removed"}:
746
741
  for spec in self.names:
747
- try:
748
- goal.add_remove(spec, settings)
749
- except LIBDNF5_ERROR as e:
750
- self.module.fail_json(msg=str(e), failures=[], rc=1)
742
+ goal.add_remove(spec, settings)
751
743
  if self.autoremove:
752
744
  for pkg in get_unneeded_pkgs(base):
753
745
  goal.add_rpm_remove(pkg, settings)
754
746
 
755
747
  goal.set_allow_erasing(self.allowerasing)
756
- try:
757
- transaction = goal.resolve()
758
- except LIBDNF5_ERROR as e:
759
- self.module.fail_json(msg=str(e), failures=[], rc=1)
748
+ transaction = goal.resolve()
760
749
 
761
750
  if transaction.get_problems():
762
751
  failures = []
@@ -832,7 +821,11 @@ def main():
832
821
  auto_install_module_deps=dict(type="bool", default=True),
833
822
  )
834
823
  )
835
- Dnf5Module(AnsibleModule(**yumdnf_argument_spec)).run()
824
+ module = AnsibleModule(**yumdnf_argument_spec)
825
+ try:
826
+ Dnf5Module(module).run()
827
+ except LIBDNF5_ERRORS as e:
828
+ module.fail_json(msg=str(e), failures=[], rc=1)
836
829
 
837
830
 
838
831
  if __name__ == "__main__":
ansible/modules/git.py CHANGED
@@ -317,11 +317,6 @@ remote_url_changed:
317
317
  returned: success
318
318
  type: bool
319
319
  sample: True
320
- warnings:
321
- description: List of warnings if requested features were not available due to a too old git version.
322
- returned: error
323
- type: str
324
- sample: git version is too old to fully support the depth argument. Falling back to full checkouts.
325
320
  git_dir_now:
326
321
  description: Contains the new path of .git directory if it is changed.
327
322
  returned: success
@@ -1240,7 +1235,7 @@ def main():
1240
1235
  archive_prefix = module.params['archive_prefix']
1241
1236
  separate_git_dir = module.params['separate_git_dir']
1242
1237
 
1243
- result = dict(changed=False, warnings=list())
1238
+ result = dict(changed=False)
1244
1239
 
1245
1240
  if module.params['accept_hostkey']:
1246
1241
  if ssh_opts is not None:
@@ -1,4 +1,3 @@
1
- #!/usr/bin/python
2
1
  # -*- coding: utf-8 -*-
3
2
 
4
3
  # Copyright: (c) 2013, Hiroaki Nakamura <hnakamur@gmail.com>
ansible/modules/pip.py CHANGED
@@ -814,10 +814,8 @@ def main():
814
814
  elif requirements:
815
815
  cmd.extend(['-r', requirements])
816
816
  else:
817
- module.exit_json(
818
- changed=False,
819
- warnings=["No valid name or requirements file found."],
820
- )
817
+ module.warn("No valid name or requirements file found.")
818
+ module.exit_json(changed=False)
821
819
 
822
820
  if module.check_mode:
823
821
  if extra_args or requirements or state == 'latest' or not name:
@@ -180,7 +180,7 @@ 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 PY2, b
183
+ from ansible.module_utils.six import b
184
184
 
185
185
 
186
186
  class Service(object):
@@ -285,14 +285,8 @@ class Service(object):
285
285
  os._exit(0)
286
286
 
287
287
  # Start the command
288
- if PY2:
289
- # Python 2.6's shlex.split can't handle text strings correctly
290
- cmd = to_bytes(cmd, errors='surrogate_or_strict')
291
- cmd = shlex.split(cmd)
292
- else:
293
- # Python3.x shex.split text strings.
294
- cmd = to_text(cmd, errors='surrogate_or_strict')
295
- cmd = [to_bytes(c, errors='surrogate_or_strict') for c in shlex.split(cmd)]
288
+ cmd = to_text(cmd, errors='surrogate_or_strict')
289
+ cmd = [to_bytes(c, errors='surrogate_or_strict') for c in shlex.split(cmd)]
296
290
  # In either of the above cases, pass a list of byte strings to Popen
297
291
 
298
292
  # chkconfig localizes messages and we're screen scraping so make
@@ -88,9 +88,9 @@ EXAMPLES = """
88
88
 
89
89
  - name: Sleep for 5 seconds between stop and start command of badly behaving service
90
90
  ansible.builtin.sysvinit:
91
- name: apache2
92
- state: restarted
93
- sleep: 5
91
+ name: apache2
92
+ state: restarted
93
+ sleep: 5
94
94
 
95
95
  - name: Make sure apache2 is started on runlevels 3 and 5
96
96
  ansible.builtin.sysvinit:
ansible/parsing/ajson.py CHANGED
@@ -4,15 +4,13 @@
4
4
  from __future__ import annotations as _annotations
5
5
 
6
6
  # from ansible.utils.display import Display as _Display
7
-
8
-
9
- # DTFIX-RELEASE: The pylint deprecated checker does not detect `Display().deprecated` calls, of which we have many.
10
-
7
+ #
8
+ #
11
9
  # deprecated: description='deprecate ajson' core_version='2.23'
12
10
  # _Display().deprecated(
13
11
  # msg='The `ansible.parsing.ajson` module is deprecated.',
14
12
  # version='2.27',
15
- # help_text="", # DTFIX-RELEASE: complete this help text
13
+ # help_text="", # DTFIX-FUTURE: complete this help text
16
14
  # )
17
15
 
18
16
  # Imported for backward compat
@@ -15,7 +15,7 @@ import typing as t
15
15
 
16
16
  from ansible import constants as C
17
17
  from ansible.errors import AnsibleFileNotFound, AnsibleParserError
18
- from ansible._internal._errors import _utils
18
+ from ansible._internal._errors import _error_utils
19
19
  from ansible.module_utils.basic import is_executable
20
20
  from ansible._internal._datatag._tags import Origin, TrustedAsTemplate, SourceWasEncrypted
21
21
  from ansible.module_utils._internal._datatag import AnsibleTagHelper
@@ -81,12 +81,12 @@ class DataLoader:
81
81
  def load(
82
82
  self,
83
83
  data: str,
84
- file_name: str | None = None, # DTFIX-RELEASE: consider deprecating this in favor of tagging Origin on data
85
- show_content: bool = True, # DTFIX-RELEASE: consider future deprecation, but would need RedactAnnotatedSourceContext public
84
+ file_name: str | None = None, # DTFIX-FUTURE: consider deprecating this in favor of tagging Origin on data
85
+ show_content: bool = True, # DTFIX-FUTURE: consider future deprecation, but would need RedactAnnotatedSourceContext public
86
86
  json_only: bool = False,
87
87
  ) -> t.Any:
88
88
  """Backwards compat for now"""
89
- with _utils.RedactAnnotatedSourceContext.when(not show_content):
89
+ with _error_utils.RedactAnnotatedSourceContext.when(not show_content):
90
90
  return from_yaml(data=data, file_name=file_name, json_only=json_only)
91
91
 
92
92
  def load_from_file(self, file_name: str, cache: str = 'all', unsafe: bool = False, json_only: bool = False, trusted_as_template: bool = False) -> t.Any:
@@ -160,7 +160,7 @@ class ModuleArgsParser:
160
160
  final_args = dict()
161
161
  if additional_args:
162
162
  if isinstance(additional_args, (str, EncryptedString)):
163
- # DTFIX-RELEASE: should this be is_possibly_template?
163
+ # DTFIX5: should this be is_possibly_template?
164
164
  if TemplateEngine().is_template(additional_args):
165
165
  final_args['_variable_params'] = additional_args
166
166
  else:
@@ -41,7 +41,7 @@ def read_docstring_from_yaml_file(filename, verbose=True, ignore_errors=True):
41
41
  file_data = yaml.load(yamlfile, Loader=AnsibleLoader)
42
42
  except Exception as ex:
43
43
  msg = f"Unable to parse yaml file {filename}"
44
- # DTFIX-RELEASE: find a better pattern for this (can we use the new optional error behavior?)
44
+ # DTFIX-FUTURE: find a better pattern for this (can we use the new optional error behavior?)
45
45
  if not ignore_errors:
46
46
  raise AnsibleParserError(f'{msg}.') from ex
47
47
  elif verbose:
@@ -93,7 +93,7 @@ def read_docstring_from_python_file(filename, verbose=True, ignore_errors=True):
93
93
 
94
94
  except Exception as ex:
95
95
  msg = f"Unable to parse documentation in python file {filename!r}"
96
- # DTFIX-RELEASE: better pattern to conditionally raise/display
96
+ # DTFIX-FUTURE: better pattern to conditionally raise/display
97
97
  if not ignore_errors:
98
98
  raise AnsibleParserError(f'{msg}.') from ex
99
99
  elif verbose:
@@ -11,7 +11,7 @@ import typing as t
11
11
  import yaml
12
12
 
13
13
  from ansible.errors import AnsibleJSONParserError
14
- from ansible._internal._errors import _utils
14
+ from ansible._internal._errors import _error_utils
15
15
  from ansible.parsing.vault import VaultSecret
16
16
  from ansible.parsing.yaml.loader import AnsibleLoader
17
17
  from ansible._internal._yaml._errors import AnsibleYAMLParserError
@@ -34,7 +34,7 @@ def from_yaml(
34
34
 
35
35
  data = origin.tag(data)
36
36
 
37
- with _utils.RedactAnnotatedSourceContext.when(not show_content):
37
+ with _error_utils.RedactAnnotatedSourceContext.when(not show_content):
38
38
  try:
39
39
  # we first try to load this data as JSON.
40
40
  # Fixes issues with extra vars json strings not being parsed correctly by the yaml parser
@@ -48,6 +48,6 @@ def from_yaml(
48
48
  try:
49
49
  return yaml.load(data, Loader=AnsibleLoader) # type: ignore[arg-type]
50
50
  except Exception as yaml_ex:
51
- # DTFIX-RELEASE: how can we indicate in Origin that the data is in-memory only, to support context information -- is that useful?
51
+ # DTFIX-FUTURE: how can we indicate in Origin that the data is in-memory only, to support context information -- is that useful?
52
52
  # we'd need to pass data to handle_exception so it could be used as the content instead of reading from disk
53
53
  AnsibleYAMLParserError.handle_exception(yaml_ex, origin=origin)
@@ -149,7 +149,7 @@ def _parse_vaulttext_envelope(b_vaulttext_envelope, default_vault_id=None):
149
149
  vault_id = to_text(b_tmpheader[3].strip())
150
150
 
151
151
  b_ciphertext = b''.join(b_tmpdata[1:])
152
- # DTFIX-RELEASE: possible candidate for propagate_origin
152
+ # DTFIX7: possible candidate for propagate_origin
153
153
  b_ciphertext = AnsibleTagHelper.tag_copy(b_vaulttext_envelope, b_ciphertext)
154
154
 
155
155
  return b_ciphertext, b_version, cipher_name, vault_id
@@ -222,7 +222,7 @@ def format_vaulttext_envelope(b_ciphertext, cipher_name, version=None, vault_id=
222
222
 
223
223
  def _unhexlify(b_data):
224
224
  try:
225
- # DTFIX-RELEASE: possible candidate for propagate_origin
225
+ # DTFIX7: possible candidate for propagate_origin
226
226
  return AnsibleTagHelper.tag_copy(b_data, unhexlify(b_data))
227
227
  except (BinasciiError, TypeError) as ex:
228
228
  raise AnsibleVaultFormatError('Vault format unhexlify error.', obj=b_data) from ex
@@ -712,7 +712,7 @@ class VaultLib:
712
712
  # secret = self.secrets[vault_secret_id]
713
713
  display.vvvv(u'Trying secret %s for vault_id=%s' % (to_text(vault_secret), to_text(vault_secret_id)))
714
714
  b_plaintext = this_cipher.decrypt(b_vaulttext, vault_secret)
715
- # DTFIX-RELEASE: possible candidate for propagate_origin
715
+ # DTFIX7: possible candidate for propagate_origin
716
716
  b_plaintext = AnsibleTagHelper.tag_copy(vaulttext, b_plaintext)
717
717
  if b_plaintext is not None:
718
718
  vault_id_used = vault_secret_id
@@ -1520,7 +1520,7 @@ class VaultHelper:
1520
1520
  tags = AnsibleTagHelper.tags(ciphertext) # ciphertext has tags but value does not
1521
1521
  elif value_type is EncryptedString:
1522
1522
  ciphertext = value._ciphertext
1523
- elif value_type in _jinja_common.Marker.concrete_subclasses: # avoid wasteful raise/except of Marker when calling get_tag below
1523
+ elif value_type in _jinja_common.Marker._concrete_subclasses: # avoid wasteful raise/except of Marker when calling get_tag below
1524
1524
  ciphertext = None
1525
1525
  elif vaulted_value := VaultedValue.get_tag(value):
1526
1526
  ciphertext = vaulted_value.ciphertext
@@ -164,5 +164,5 @@ class PlaybookInclude(Base, Conditional, Taggable):
164
164
  if len(items) == 0:
165
165
  raise AnsibleParserError("import_playbook statements must specify the file name to import", obj=ds)
166
166
 
167
- # DTFIX-RELEASE: investigate this as a possible "problematic strip"
167
+ # DTFIX3: investigate this as a possible "problematic strip"
168
168
  new_ds['import_playbook'] = AnsibleTagHelper.tag_copy(v, items[0].strip())