ansible-core 2.19.0b6__py3-none-any.whl → 2.19.0b7__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 (101) hide show
  1. ansible/_internal/_templating/_jinja_bits.py +16 -1
  2. ansible/_internal/_templating/_jinja_common.py +1 -1
  3. ansible/cli/__init__.py +2 -2
  4. ansible/cli/adhoc.py +6 -3
  5. ansible/cli/console.py +1 -1
  6. ansible/cli/doc.py +2 -2
  7. ansible/config/base.yml +9 -6
  8. ansible/executor/module_common.py +8 -5
  9. ansible/executor/powershell/psrp_put_file.ps1 +1 -1
  10. ansible/executor/task_executor.py +2 -2
  11. ansible/executor/task_queue_manager.py +34 -70
  12. ansible/executor/task_result.py +1 -1
  13. ansible/galaxy/api.py +2 -2
  14. ansible/galaxy/collection/concrete_artifact_manager.py +2 -2
  15. ansible/galaxy/dependency_resolution/providers.py +3 -3
  16. ansible/inventory/group.py +6 -1
  17. ansible/inventory/host.py +6 -1
  18. ansible/module_utils/_internal/_deprecator.py +12 -1
  19. ansible/module_utils/ansible_release.py +1 -1
  20. ansible/module_utils/basic.py +14 -16
  21. ansible/module_utils/common/yaml.py +1 -1
  22. ansible/module_utils/csharp/Ansible.Basic.cs +1 -1
  23. ansible/module_utils/csharp/Ansible.Privilege.cs +2 -2
  24. ansible/module_utils/facts/hardware/base.py +1 -1
  25. ansible/module_utils/facts/other/facter.py +1 -1
  26. ansible/module_utils/facts/system/distribution.py +2 -2
  27. ansible/module_utils/powershell/Ansible.ModuleUtils.AddType.psm1 +1 -1
  28. ansible/module_utils/powershell/Ansible.ModuleUtils.CamelConversion.psm1 +1 -1
  29. ansible/module_utils/powershell/Ansible.ModuleUtils.CommandUtil.psm1 +1 -1
  30. ansible/module_utils/powershell/Ansible.ModuleUtils.WebRequest.psm1 +1 -1
  31. ansible/module_utils/urls.py +1 -1
  32. ansible/modules/apt.py +9 -3
  33. ansible/modules/assemble.py +5 -3
  34. ansible/modules/expect.py +5 -5
  35. ansible/modules/hostname.py +2 -2
  36. ansible/modules/pip.py +9 -11
  37. ansible/modules/raw.py +2 -2
  38. ansible/modules/stat.py +1 -1
  39. ansible/modules/wait_for.py +10 -3
  40. ansible/parsing/mod_args.py +38 -20
  41. ansible/parsing/vault/__init__.py +3 -3
  42. ansible/playbook/base.py +0 -2
  43. ansible/playbook/helpers.py +1 -1
  44. ansible/playbook/playbook_include.py +23 -56
  45. ansible/playbook/role/__init__.py +38 -21
  46. ansible/plugins/action/__init__.py +2 -2
  47. ansible/plugins/action/assemble.py +2 -1
  48. ansible/plugins/action/assert.py +2 -2
  49. ansible/plugins/action/script.py +5 -4
  50. ansible/plugins/action/template.py +1 -1
  51. ansible/plugins/callback/__init__.py +77 -87
  52. ansible/plugins/callback/default.py +0 -3
  53. ansible/plugins/callback/junit.py +0 -6
  54. ansible/plugins/connection/ssh.py +1 -1
  55. ansible/plugins/filter/pow.yml +1 -1
  56. ansible/plugins/filter/root.yml +1 -1
  57. ansible/plugins/filter/strftime.yml +3 -3
  58. ansible/plugins/filter/to_uuid.yml +1 -1
  59. ansible/plugins/inventory/script.py +1 -1
  60. ansible/plugins/loader.py +5 -0
  61. ansible/plugins/lookup/password.py +4 -6
  62. ansible/release.py +1 -1
  63. ansible/utils/display.py +16 -26
  64. ansible/utils/path.py +1 -1
  65. ansible/utils/vars.py +4 -1
  66. ansible/vars/manager.py +6 -3
  67. ansible/vars/reserved.py +6 -4
  68. {ansible_core-2.19.0b6.dist-info → ansible_core-2.19.0b7.dist-info}/METADATA +1 -1
  69. {ansible_core-2.19.0b6.dist-info → ansible_core-2.19.0b7.dist-info}/RECORD +101 -99
  70. ansible_test/_internal/__init__.py +5 -0
  71. ansible_test/_internal/ansible_util.py +1 -1
  72. ansible_test/_internal/classification/python.py +6 -0
  73. ansible_test/_internal/cli/commands/__init__.py +0 -5
  74. ansible_test/_internal/cli/environments.py +51 -5
  75. ansible_test/_internal/commands/coverage/__init__.py +1 -1
  76. ansible_test/_internal/commands/integration/__init__.py +18 -5
  77. ansible_test/_internal/commands/integration/cloud/httptester.py +1 -1
  78. ansible_test/_internal/commands/sanity/__init__.py +3 -1
  79. ansible_test/_internal/commands/sanity/integration_aliases.py +11 -0
  80. ansible_test/_internal/commands/shell/__init__.py +43 -4
  81. ansible_test/_internal/commands/units/__init__.py +4 -1
  82. ansible_test/_internal/config.py +21 -13
  83. ansible_test/_internal/debugging.py +166 -0
  84. ansible_test/_internal/delegation.py +21 -13
  85. ansible_test/_internal/host_profiles.py +197 -6
  86. ansible_test/_internal/inventory.py +4 -0
  87. ansible_test/_internal/metadata.py +94 -4
  88. ansible_test/_internal/processes.py +80 -0
  89. ansible_test/_internal/python_requirements.py +27 -0
  90. ansible_test/_internal/target.py +8 -0
  91. ansible_test/_internal/util_common.py +13 -3
  92. ansible_test/_util/target/injector/python.py +8 -0
  93. {ansible_core-2.19.0b6.dist-info → ansible_core-2.19.0b7.dist-info}/WHEEL +0 -0
  94. {ansible_core-2.19.0b6.dist-info → ansible_core-2.19.0b7.dist-info}/entry_points.txt +0 -0
  95. {ansible_core-2.19.0b6.dist-info → ansible_core-2.19.0b7.dist-info}/licenses/COPYING +0 -0
  96. {ansible_core-2.19.0b6.dist-info → ansible_core-2.19.0b7.dist-info}/licenses/licenses/Apache-License.txt +0 -0
  97. {ansible_core-2.19.0b6.dist-info → ansible_core-2.19.0b7.dist-info}/licenses/licenses/BSD-3-Clause.txt +0 -0
  98. {ansible_core-2.19.0b6.dist-info → ansible_core-2.19.0b7.dist-info}/licenses/licenses/MIT-license.txt +0 -0
  99. {ansible_core-2.19.0b6.dist-info → ansible_core-2.19.0b7.dist-info}/licenses/licenses/PSF-license.txt +0 -0
  100. {ansible_core-2.19.0b6.dist-info → ansible_core-2.19.0b7.dist-info}/licenses/licenses/simplified_bsd.txt +0 -0
  101. {ansible_core-2.19.0b6.dist-info → ansible_core-2.19.0b7.dist-info}/top_level.txt +0 -0
@@ -26,7 +26,7 @@ else:
26
26
 
27
27
  # DTFIX-FUTURE: refactor this to share the implementation with the controller version
28
28
  # use an abstract base class, with __init_subclass__ for representer registration, and instance methods for overridable representers
29
- # then tests can be consolidated intead of having two nearly identical copies
29
+ # then tests can be consolidated instead of having two nearly identical copies
30
30
 
31
31
  if HAS_YAML:
32
32
  try:
@@ -1696,7 +1696,7 @@ namespace Ansible.Basic
1696
1696
  if ((attr & FileAttributes.ReadOnly) != 0)
1697
1697
  {
1698
1698
  // Windows does not allow files set with ReadOnly to be
1699
- // deleted. Pre-emptively unset the attribute.
1699
+ // deleted. Preemptively unset the attribute.
1700
1700
  // FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE is quite new,
1701
1701
  // look at using that flag with POSIX delete once Server 2019
1702
1702
  // is the baseline.
@@ -228,7 +228,7 @@ namespace Ansible.Privilege
228
228
  }
229
229
 
230
230
  /// <summary>
231
- /// Get's the status of all the privileges on the token specified
231
+ /// Gets the status of all the privileges on the token specified
232
232
  /// </summary>
233
233
  /// <param name="token">The process token to get the privilege status on</param>
234
234
  /// <returns>Dictionary where the key is the privilege constant and the value is the PrivilegeAttributes flags</returns>
@@ -342,7 +342,7 @@ namespace Ansible.Privilege
342
342
  // Need to manually marshal the bytes requires for newState as the constant size
343
343
  // of LUID_AND_ATTRIBUTES is set to 1 and can't be overridden at runtime, TOKEN_PRIVILEGES
344
344
  // always contains at least 1 entry so we need to calculate the extra size if there are
345
- // nore than 1 LUID_AND_ATTRIBUTES entry
345
+ // more than 1 LUID_AND_ATTRIBUTES entry
346
346
  int tokenPrivilegesSize = Marshal.SizeOf(typeof(NativeHelpers.TOKEN_PRIVILEGES));
347
347
  int luidAttrSize = 0;
348
348
  if (newState.Length > 1)
@@ -49,7 +49,7 @@ class HardwareCollector(BaseFactCollector):
49
49
  _fact_ids = set(['processor',
50
50
  'processor_cores',
51
51
  'processor_count',
52
- # TODO: mounts isnt exactly hardware
52
+ # TODO: mounts isn't exactly hardware
53
53
  'mounts',
54
54
  'devices']) # type: t.Set[str]
55
55
  _fact_class = Hardware
@@ -61,7 +61,7 @@ class FacterFactCollector(BaseFactCollector):
61
61
  return out
62
62
 
63
63
  def collect(self, module=None, collected_facts=None):
64
- # Note that this mirrors previous facter behavior, where there isnt
64
+ # Note that this mirrors previous facter behavior, where there isn't
65
65
  # a 'ansible_facter' key in the main fact dict, but instead, 'facter_whatever'
66
66
  # items are added to the main dict.
67
67
  facter_dict = {}
@@ -100,7 +100,7 @@ class DistributionFiles:
100
100
  return get_file_content(path)
101
101
 
102
102
  def _get_dist_file_content(self, path, allow_empty=False):
103
- # cant find that dist file or it is incorrectly empty
103
+ # can't find that dist file, or it is incorrectly empty
104
104
  if not _file_exists(path, allow_empty=allow_empty):
105
105
  return False, None
106
106
 
@@ -585,7 +585,7 @@ class Distribution(object):
585
585
  distribution_facts.update(dist_file_facts)
586
586
 
587
587
  distro = distribution_facts['distribution']
588
- # look for a os family alias for the 'distribution', if there isnt one, use 'distribution'
588
+ # look for an os family alias for the 'distribution', if there isn't one, use 'distribution'
589
589
  distribution_facts['os_family'] = self.OS_FAMILY.get(distro, None) or distro
590
590
 
591
591
  return distribution_facts
@@ -5,7 +5,7 @@ Function Add-CSharpType {
5
5
  <#
6
6
  .SYNOPSIS
7
7
  Compiles one or more C# scripts similar to Add-Type. This exposes
8
- more configuration options that are useable within Ansible and it
8
+ more configuration options that are usable within Ansible and it
9
9
  also allows multiple C# sources to be compiled together.
10
10
 
11
11
  .PARAMETER References
@@ -21,7 +21,7 @@ Function Convert-StringToSnakeCase($string) {
21
21
  return $string
22
22
  }
23
23
 
24
- # used by Convert-DictToSnakeCase to covert list entries from camelCase
24
+ # used by Convert-DictToSnakeCase to convert list entries from camelCase
25
25
  # to snake_case
26
26
  Function Convert-ListToSnakeCase($list) {
27
27
  $snake_list = [System.Collections.ArrayList]@()
@@ -6,7 +6,7 @@
6
6
  Function Get-ExecutablePath {
7
7
  <#
8
8
  .SYNOPSIS
9
- Get's the full path to an executable, will search the directory specified or ones in the PATH env var.
9
+ Gets the full path to an executable, will search the directory specified or ones in the PATH env var.
10
10
 
11
11
  .PARAMETER executable
12
12
  [String]The executable to search for.
@@ -17,7 +17,7 @@ Function Get-AnsibleWebRequest {
17
17
  The protocol method to use, if omitted, will use the default value for the URI protocol specified.
18
18
 
19
19
  .PARAMETER FollowRedirects
20
- Whether to follow redirect reponses. This is only valid when using a HTTP URI.
20
+ Whether to follow redirect responses. This is only valid when using a HTTP URI.
21
21
  all - Will follow all redirects
22
22
  none - Will follow no redirects
23
23
  safe - Will only follow redirects when GET or HEAD is used as the Method
@@ -1155,7 +1155,7 @@ def url_argument_spec():
1155
1155
 
1156
1156
  def url_redirect_argument_spec():
1157
1157
  """
1158
- Creates an addition arugment spec to `url_argument_spec`
1158
+ Creates an addition argument spec to `url_argument_spec`
1159
1159
  for `follow_redirects` argument
1160
1160
  """
1161
1161
  return dict(
ansible/modules/apt.py CHANGED
@@ -855,6 +855,7 @@ def install_deb(
855
855
  allow_downgrade,
856
856
  allow_change_held_packages,
857
857
  dpkg_options,
858
+ lock_timeout,
858
859
  ):
859
860
  changed = False
860
861
  deps_to_install = []
@@ -903,13 +904,14 @@ def install_deb(
903
904
  # install the deps through apt
904
905
  retvals = {}
905
906
  if deps_to_install:
907
+ install_dpkg_options = f"{expand_dpkg_options(dpkg_options)} -o DPkg::Lock::Timeout={lock_timeout}"
906
908
  (success, retvals) = install(m=m, pkgspec=deps_to_install, cache=cache,
907
909
  install_recommends=install_recommends,
908
910
  fail_on_autoremove=fail_on_autoremove,
909
911
  allow_unauthenticated=allow_unauthenticated,
910
912
  allow_downgrade=allow_downgrade,
911
913
  allow_change_held_packages=allow_change_held_packages,
912
- dpkg_options=expand_dpkg_options(dpkg_options))
914
+ dpkg_options=install_dpkg_options)
913
915
  if not success:
914
916
  m.fail_json(**retvals)
915
917
  changed = retvals.get('changed', False)
@@ -1269,7 +1271,7 @@ def main():
1269
1271
 
1270
1272
  p = module.params
1271
1273
  install_recommends = p['install_recommends']
1272
- dpkg_options = expand_dpkg_options(p['dpkg_options'])
1274
+ dpkg_options = f"{expand_dpkg_options(p['dpkg_options'])} -o DPkg::Lock::Timeout={p['lock_timeout']}"
1273
1275
 
1274
1276
  if not HAS_PYTHON_APT:
1275
1277
  # This interpreter can't see the apt Python library- we'll do the following to try and fix that:
@@ -1470,7 +1472,11 @@ def main():
1470
1472
  allow_unauthenticated=allow_unauthenticated,
1471
1473
  allow_change_held_packages=allow_change_held_packages,
1472
1474
  allow_downgrade=allow_downgrade,
1473
- force=force_yes, fail_on_autoremove=fail_on_autoremove, dpkg_options=p['dpkg_options'])
1475
+ force=force_yes,
1476
+ fail_on_autoremove=fail_on_autoremove,
1477
+ dpkg_options=p['dpkg_options'],
1478
+ lock_timeout=p['lock_timeout']
1479
+ )
1474
1480
 
1475
1481
  unfiltered_packages = p['package'] or ()
1476
1482
  packages = [package.strip() for package in unfiltered_packages if package != '*']
@@ -80,7 +80,7 @@ attributes:
80
80
  bypass_host_loop:
81
81
  support: none
82
82
  check_mode:
83
- support: none
83
+ support: full
84
84
  diff_mode:
85
85
  support: full
86
86
  platform:
@@ -212,6 +212,7 @@ def main():
212
212
  decrypt=dict(type='bool', default=True),
213
213
  ),
214
214
  add_file_common_args=True,
215
+ supports_check_mode=True,
215
216
  )
216
217
 
217
218
  changed = False
@@ -266,12 +267,13 @@ def main():
266
267
  if backup and dest_hash is not None:
267
268
  result['backup_file'] = module.backup_local(dest)
268
269
 
269
- module.atomic_move(path, dest, unsafe_writes=module.params['unsafe_writes'])
270
+ if not module.check_mode:
271
+ module.atomic_move(path, dest, unsafe_writes=module.params['unsafe_writes'])
270
272
  changed = True
271
273
 
272
274
  cleanup(module, path, result)
273
275
 
274
- # handle file permissions
276
+ # handle file permissions (check mode aware)
275
277
  file_args = module.load_file_common_arguments(module.params)
276
278
  result['changed'] = module.set_fs_attributes_if_different(file_args, changed)
277
279
 
ansible/modules/expect.py CHANGED
@@ -218,7 +218,7 @@ def main():
218
218
  rc=0
219
219
  )
220
220
 
221
- startd = datetime.datetime.now()
221
+ start_date = datetime.datetime.now()
222
222
 
223
223
  try:
224
224
  try:
@@ -246,8 +246,8 @@ def main():
246
246
  except pexpect.ExceptionPexpect as e:
247
247
  module.fail_json(msg='%s' % to_native(e))
248
248
 
249
- endd = datetime.datetime.now()
250
- delta = endd - startd
249
+ end_date = datetime.datetime.now()
250
+ delta = end_date - start_date
251
251
 
252
252
  if b_out is None:
253
253
  b_out = b''
@@ -256,8 +256,8 @@ def main():
256
256
  cmd=args,
257
257
  stdout=to_native(b_out).rstrip('\r\n'),
258
258
  rc=rc,
259
- start=str(startd),
260
- end=str(endd),
259
+ start=str(start_date),
260
+ end=str(end_date),
261
261
  delta=str(delta),
262
262
  changed=True,
263
263
  )
@@ -608,8 +608,8 @@ class Hostname(object):
608
608
  self.use = module.params['use']
609
609
 
610
610
  if self.use is not None:
611
- strat = globals()['%sStrategy' % STRATS[self.use]]
612
- self.strategy = strat(module)
611
+ strategy = globals()['%sStrategy' % STRATS[self.use]]
612
+ self.strategy = strategy(module)
613
613
  elif platform.system() == 'Linux' and ServiceMgrFactCollector.is_systemd_managed(module):
614
614
  # This is Linux and systemd is active
615
615
  self.strategy = SystemdStrategy(module)
ansible/modules/pip.py CHANGED
@@ -60,7 +60,7 @@ options:
60
60
  virtualenv_python:
61
61
  description:
62
62
  - The Python executable used for creating the virtual environment.
63
- For example V(python3.12), V(python2.7). When not specified, the
63
+ For example V(python3.13). When not specified, the
64
64
  Python version used to run the ansible module is used. This parameter
65
65
  should not be used when O(virtualenv_command) is using V(pyvenv) or
66
66
  the C(-m venv) module.
@@ -93,8 +93,8 @@ options:
93
93
  description:
94
94
  - The explicit executable or pathname for the C(pip) executable,
95
95
  if different from the Ansible Python interpreter. For
96
- example V(pip3.3), if there are both Python 2.7 and 3.3 installations
97
- in the system and you want to run pip for the Python 3.3 installation.
96
+ example V(pip3.13), if there are multiple Python installations
97
+ in the system and you want to run pip for the Python 3.13 installation.
98
98
  - Mutually exclusive with O(virtualenv) (added in 2.1).
99
99
  - Does not affect the Ansible Python interpreter.
100
100
  - The C(setuptools) package must be installed for both the Ansible Python interpreter
@@ -134,7 +134,7 @@ notes:
134
134
  the virtualenv needs to be created.
135
135
  - Although it executes using the Ansible Python interpreter, the pip module shells out to
136
136
  run the actual pip command, so it can use any pip version you specify with O(executable).
137
- By default, it uses the pip version for the Ansible Python interpreter. For example, pip3 on python 3, and pip2 or pip on python 2.
137
+ By default, it uses the pip version for the Ansible Python interpreter.
138
138
  - The interpreter used by Ansible
139
139
  (see R(ansible_python_interpreter, ansible_python_interpreter))
140
140
  requires the setuptools package, regardless of the version of pip set with
@@ -197,11 +197,11 @@ EXAMPLES = """
197
197
  virtualenv: /my_app/venv
198
198
  virtualenv_site_packages: yes
199
199
 
200
- - name: Install bottle into the specified (virtualenv), using Python 2.7
200
+ - name: Install bottle into the specified (virtualenv), using Python 3.13
201
201
  ansible.builtin.pip:
202
202
  name: bottle
203
203
  virtualenv: /my_app/venv
204
- virtualenv_command: virtualenv-2.7
204
+ virtualenv_command: virtualenv-3.13
205
205
 
206
206
  - name: Install bottle within a user home directory
207
207
  ansible.builtin.pip:
@@ -227,10 +227,10 @@ EXAMPLES = """
227
227
  requirements: /my_app/requirements.txt
228
228
  extra_args: "--no-index --find-links=file:///my_downloaded_packages_dir"
229
229
 
230
- - name: Install bottle for Python 3.3 specifically, using the 'pip3.3' executable
230
+ - name: Install bottle for Python 3.13 specifically, using the 'pip3.13' executable
231
231
  ansible.builtin.pip:
232
232
  name: bottle
233
- executable: pip3.3
233
+ executable: pip3.13
234
234
 
235
235
  - name: Install bottle, forcing reinstallation if it's already installed
236
236
  ansible.builtin.pip:
@@ -460,9 +460,7 @@ def _get_pip(module, env=None, executable=None):
460
460
  candidate_pip_basenames = (executable,)
461
461
  elif executable is None and env is None and _have_pip_module():
462
462
  # If no executable or virtualenv were specified, use the pip module for the current Python interpreter if available.
463
- # Use of `__main__` is required to support Python 2.6 since support for executing packages with `runpy` was added in Python 2.7.
464
- # Without it Python 2.6 gives the following error: pip is a package and cannot be directly executed
465
- pip = [sys.executable, '-m', 'pip.__main__']
463
+ pip = [sys.executable, '-m', 'pip']
466
464
 
467
465
  if pip is None:
468
466
  if env is None:
ansible/modules/raw.py CHANGED
@@ -73,8 +73,8 @@ author:
73
73
  """
74
74
 
75
75
  EXAMPLES = r"""
76
- - name: Bootstrap a host without python2 installed
77
- ansible.builtin.raw: dnf install -y python2 python2-dnf libselinux-python
76
+ - name: Bootstrap a host without Python installed
77
+ ansible.builtin.raw: dnf install -y python3 python3-libdnf
78
78
 
79
79
  - name: Run a command that uses non-posix shell-isms (in this example /bin/sh doesn't handle redirection and wildcards together but bash does)
80
80
  ansible.builtin.raw: cat < /tmp/*txt
ansible/modules/stat.py CHANGED
@@ -408,7 +408,7 @@ def format_output(module, path, st):
408
408
  ('st_blksize', 'block_size'),
409
409
  ('st_rdev', 'device_type'),
410
410
  ('st_flags', 'flags'),
411
- # Some Berkley based
411
+ # Some Berkeley based
412
412
  ('st_gen', 'generation'),
413
413
  ('st_birthtime', 'birthtime'),
414
414
  # RISCOS
@@ -76,6 +76,8 @@ options:
76
76
  description:
77
77
  - Can be used to match a string in either a file or a socket connection.
78
78
  - Defaults to a multiline regex.
79
+ - When inspecting a system log file and a static string, remember that Ansible by default logs its own actions there;
80
+ see the notes and examples for information.
79
81
  type: str
80
82
  version_added: "1.4"
81
83
  exclude_hosts:
@@ -105,13 +107,13 @@ attributes:
105
107
  platform:
106
108
  platforms: posix
107
109
  notes:
108
- - The ability to use search_regex with a port connection was added in Ansible 1.7.
109
- - Prior to Ansible 2.4, testing for the absence of a directory or UNIX socket did not work correctly.
110
- - Prior to Ansible 2.4, testing for the presence of a file did not work correctly if the remote user did not have read access to that file.
111
110
  - Under some circumstances when using mandatory access control, a path may always be treated as being absent even if it exists, but
112
111
  can't be modified or created by the remote user either.
113
112
  - When waiting for a path, symbolic links will be followed. Many other modules that manipulate files do not follow symbolic links,
114
113
  so operations on the path using other modules may not work exactly as expected.
114
+ - When searching a static string within a system log file, it is important to account for potential self-matching against log entries
115
+ generated by the Ansible modules. To prevent this, add a regular expression construct into the search string. For example, to match
116
+ a literal string 'this thing', one could use a regular expression like 'this t[h]ing'.
115
117
  seealso:
116
118
  - module: ansible.builtin.wait_for_connection
117
119
  - module: ansible.windows.win_wait_for
@@ -156,6 +158,11 @@ EXAMPLES = r"""
156
158
  path: /tmp/foo
157
159
  search_regex: completed
158
160
 
161
+ - name: Wait until the string "tomcat up" is in syslog, use regex character set to avoid self match
162
+ ansible.builtin.wait_for:
163
+ path: /var/log/syslog
164
+ search_regex: 'tomcat [u]p'
165
+
159
166
  - name: Wait until regex pattern matches in the file /tmp/foo and print the matched group
160
167
  ansible.builtin.wait_for:
161
168
  path: /tmp/foo
@@ -20,17 +20,17 @@ from __future__ import annotations
20
20
  import ansible.constants as C
21
21
  from ansible.errors import AnsibleParserError, AnsibleError, AnsibleAssertionError
22
22
  from ansible.module_utils._internal._datatag import AnsibleTagHelper
23
- from ansible.module_utils.six import string_types
24
23
  from ansible.module_utils.common.sentinel import Sentinel
25
24
  from ansible.module_utils.common.text.converters import to_text
26
25
  from ansible.parsing.splitter import parse_kv, split_args
27
26
  from ansible.parsing.vault import EncryptedString
28
27
  from ansible.plugins.loader import module_loader, action_loader
29
- from ansible._internal._templating._engine import TemplateEngine
28
+ from ansible._internal._templating import _jinja_bits
29
+ from ansible.utils.display import Display
30
30
  from ansible.utils.fqcn import add_internal_fqcns
31
31
 
32
32
 
33
- # modules formated for user msg
33
+ # modules formatted for user msg
34
34
  _BUILTIN_RAW_PARAM_MODULES_SIMPLE = set([
35
35
  'include_vars',
36
36
  'include_tasks',
@@ -152,38 +152,43 @@ class ModuleArgsParser:
152
152
  arguments can be fuzzy. Deal with all the forms.
153
153
  """
154
154
 
155
- additional_args = {} if additional_args is None else additional_args
156
-
157
155
  # final args are the ones we'll eventually return, so first update
158
156
  # them with any additional args specified, which have lower priority
159
157
  # than those which may be parsed/normalized next
160
158
  final_args = dict()
161
- if additional_args:
162
- if isinstance(additional_args, (str, EncryptedString)):
163
- # DTFIX5: should this be is_possibly_template?
164
- if TemplateEngine().is_template(additional_args):
165
- final_args['_variable_params'] = additional_args
166
- else:
167
- raise AnsibleParserError("Complex args containing variables cannot use bare variables (without Jinja2 delimiters), "
168
- "and must use the full variable style ('{{var_name}}')")
159
+
160
+ if additional_args is not Sentinel:
161
+ if isinstance(additional_args, str) and _jinja_bits.is_possibly_all_template(additional_args):
162
+ final_args['_variable_params'] = additional_args
169
163
  elif isinstance(additional_args, dict):
170
164
  final_args.update(additional_args)
165
+ elif additional_args is None:
166
+ Display().deprecated(
167
+ msg="Ignoring empty task `args` keyword.",
168
+ version="2.23",
169
+ help_text='A mapping or template which resolves to a mapping is required.',
170
+ obj=self._task_ds,
171
+ )
171
172
  else:
172
- raise AnsibleParserError('Complex args must be a dictionary or variable string ("{{var}}").')
173
+ raise AnsibleParserError(
174
+ message='The value of the task `args` keyword is invalid.',
175
+ help_text='A mapping or template which resolves to a mapping is required.',
176
+ obj=additional_args,
177
+ )
173
178
 
174
179
  # how we normalize depends if we figured out what the module name is
175
180
  # yet. If we have already figured it out, it's a 'new style' invocation.
176
181
  # otherwise, it's not
177
182
 
178
183
  if action is not None:
179
- args = self._normalize_new_style_args(thing, action)
184
+ args = self._normalize_new_style_args(thing, action, additional_args)
180
185
  else:
181
186
  (action, args) = self._normalize_old_style_args(thing)
182
187
 
183
188
  # this can occasionally happen, simplify
184
189
  if args and 'args' in args:
185
190
  tmp_args = args.pop('args')
186
- if isinstance(tmp_args, string_types):
191
+ if isinstance(tmp_args, str):
187
192
  tmp_args = parse_kv(tmp_args)
188
193
  args.update(tmp_args)
189
194
 
@@ -206,7 +211,7 @@ class ModuleArgsParser:
206
211
 
207
212
  return (action, final_args)
208
213
 
209
- def _normalize_new_style_args(self, thing, action):
214
+ def _normalize_new_style_args(self, thing, action, additional_args):
210
215
  """
211
216
  deals with fuzziness in new style module invocations
212
217
  accepting key=value pairs and dictionaries, and returns
@@ -222,11 +227,23 @@ class ModuleArgsParser:
222
227
  if isinstance(thing, dict):
223
228
  # form is like: { xyz: { x: 2, y: 3 } }
224
229
  args = thing
225
- elif isinstance(thing, string_types):
230
+ elif isinstance(thing, str):
226
231
  # form is like: copy: src=a dest=b
227
232
  check_raw = action in FREEFORM_ACTIONS
228
233
  args = parse_kv(thing, check_raw=check_raw)
234
+ args_keys = set(args) - {'_raw_params'}
235
+
236
+ if args_keys and additional_args is not Sentinel:
237
+ kv_args = ', '.join(repr(arg) for arg in sorted(args_keys))
238
+
239
+ Display().deprecated(
240
+ msg=f"Merging legacy k=v args ({kv_args}) into task args.",
241
+ help_text="Include all task args in the task `args` mapping.",
242
+ version="2.23",
243
+ obj=thing,
244
+ )
229
245
  elif isinstance(thing, EncryptedString):
246
+ # k=v parsing intentionally omitted
230
247
  args = dict(_raw_params=thing)
231
248
  elif thing is None:
232
249
  # this can happen with modules which take no params, like ping:
@@ -253,6 +270,7 @@ class ModuleArgsParser:
253
270
 
254
271
  if isinstance(thing, dict):
255
272
  # form is like: action: { module: 'copy', src: 'a', dest: 'b' }
273
+ Display().deprecated("Using a mapping for `action` is deprecated.", version='2.23', help_text='Use a string value for `action`.', obj=thing)
256
274
  thing = thing.copy()
257
275
  if 'module' in thing:
258
276
  action, module_args = self._split_module_string(thing['module'])
@@ -261,7 +279,7 @@ class ModuleArgsParser:
261
279
  args.update(parse_kv(module_args, check_raw=check_raw))
262
280
  del args['module']
263
281
 
264
- elif isinstance(thing, string_types):
282
+ elif isinstance(thing, str):
265
283
  # form is like: action: copy src=a dest=b
266
284
  (action, args) = self._split_module_string(thing)
267
285
  check_raw = action in FREEFORM_ACTIONS
@@ -287,7 +305,7 @@ class ModuleArgsParser:
287
305
  # This is the standard YAML form for command-type modules. We grab
288
306
  # the args and pass them in as additional arguments, which can/will
289
307
  # be overwritten via dict updates from the other arg sources below
290
- additional_args = self._task_ds.get('args', dict())
308
+ additional_args = self._task_ds.get('args', Sentinel)
291
309
 
292
310
  # We can have one of action, local_action, or module specified
293
311
  # action
@@ -570,8 +570,8 @@ def match_encrypt_secret(secrets, encrypt_vault_id=None):
570
570
  return match_encrypt_vault_id_secret(secrets,
571
571
  encrypt_vault_id=encrypt_vault_id)
572
572
 
573
- # Find the best/first secret from secrets since we didnt specify otherwise
574
- # ie, consider all of the available secrets as matches
573
+ # Find the best/first secret from secrets since we didn't specify otherwise
574
+ # ie, consider all the available secrets as matches
575
575
  _vault_id_matchers = [_vault_id for _vault_id, dummy in secrets]
576
576
  best_secret = match_best_secret(secrets, _vault_id_matchers)
577
577
 
@@ -1413,7 +1413,7 @@ class EncryptedString(AnsibleTaggedObject):
1413
1413
  'ljust',
1414
1414
  'lower',
1415
1415
  'lstrip',
1416
- 'maketrans', # static, but implemented for simplicty/consistency
1416
+ 'maketrans', # static, but implemented for simplicity/consistency
1417
1417
  'partition',
1418
1418
  'removeprefix',
1419
1419
  'removesuffix',
ansible/playbook/base.py CHANGED
@@ -221,8 +221,6 @@ class FieldAttributeBase:
221
221
 
222
222
  def validate(self, all_vars=None):
223
223
  """ validation that is done at parse time, not load time """
224
- all_vars = {} if all_vars is None else all_vars
225
-
226
224
  if not self._validated:
227
225
  # walk all fields in the object
228
226
  for (name, attribute) in self.fattributes.items():
@@ -122,7 +122,7 @@ def load_list_of_tasks(ds, play, block=None, role=None, task_include=None, use_h
122
122
  except AnsibleParserError as ex:
123
123
  # if the raises exception was created with obj=ds args, then it includes the detail
124
124
  # so we dont need to add it so we can just re raise.
125
- if ex.obj:
125
+ if ex.obj is not None:
126
126
  raise
127
127
  # But if it wasn't, we can add the yaml object now to get more detail
128
128
  # DTFIX-FUTURE: this *should* be unnecessary- check code coverage.