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.
- ansible/_internal/_templating/_jinja_bits.py +16 -1
- ansible/_internal/_templating/_jinja_common.py +1 -1
- ansible/cli/__init__.py +2 -2
- ansible/cli/adhoc.py +6 -3
- ansible/cli/console.py +1 -1
- ansible/cli/doc.py +2 -2
- ansible/config/base.yml +9 -6
- ansible/executor/module_common.py +8 -5
- ansible/executor/powershell/psrp_put_file.ps1 +1 -1
- ansible/executor/task_executor.py +2 -2
- ansible/executor/task_queue_manager.py +34 -70
- ansible/executor/task_result.py +1 -1
- ansible/galaxy/api.py +2 -2
- ansible/galaxy/collection/concrete_artifact_manager.py +2 -2
- ansible/galaxy/dependency_resolution/providers.py +3 -3
- ansible/inventory/group.py +6 -1
- ansible/inventory/host.py +6 -1
- ansible/module_utils/_internal/_deprecator.py +12 -1
- ansible/module_utils/ansible_release.py +1 -1
- ansible/module_utils/basic.py +14 -16
- ansible/module_utils/common/yaml.py +1 -1
- ansible/module_utils/csharp/Ansible.Basic.cs +1 -1
- ansible/module_utils/csharp/Ansible.Privilege.cs +2 -2
- ansible/module_utils/facts/hardware/base.py +1 -1
- ansible/module_utils/facts/other/facter.py +1 -1
- ansible/module_utils/facts/system/distribution.py +2 -2
- ansible/module_utils/powershell/Ansible.ModuleUtils.AddType.psm1 +1 -1
- ansible/module_utils/powershell/Ansible.ModuleUtils.CamelConversion.psm1 +1 -1
- ansible/module_utils/powershell/Ansible.ModuleUtils.CommandUtil.psm1 +1 -1
- ansible/module_utils/powershell/Ansible.ModuleUtils.WebRequest.psm1 +1 -1
- ansible/module_utils/urls.py +1 -1
- ansible/modules/apt.py +9 -3
- ansible/modules/assemble.py +5 -3
- ansible/modules/expect.py +5 -5
- ansible/modules/hostname.py +2 -2
- ansible/modules/pip.py +9 -11
- ansible/modules/raw.py +2 -2
- ansible/modules/stat.py +1 -1
- ansible/modules/wait_for.py +10 -3
- ansible/parsing/mod_args.py +38 -20
- ansible/parsing/vault/__init__.py +3 -3
- ansible/playbook/base.py +0 -2
- ansible/playbook/helpers.py +1 -1
- ansible/playbook/playbook_include.py +23 -56
- ansible/playbook/role/__init__.py +38 -21
- ansible/plugins/action/__init__.py +2 -2
- ansible/plugins/action/assemble.py +2 -1
- ansible/plugins/action/assert.py +2 -2
- ansible/plugins/action/script.py +5 -4
- ansible/plugins/action/template.py +1 -1
- ansible/plugins/callback/__init__.py +77 -87
- ansible/plugins/callback/default.py +0 -3
- ansible/plugins/callback/junit.py +0 -6
- ansible/plugins/connection/ssh.py +1 -1
- ansible/plugins/filter/pow.yml +1 -1
- ansible/plugins/filter/root.yml +1 -1
- ansible/plugins/filter/strftime.yml +3 -3
- ansible/plugins/filter/to_uuid.yml +1 -1
- ansible/plugins/inventory/script.py +1 -1
- ansible/plugins/loader.py +5 -0
- ansible/plugins/lookup/password.py +4 -6
- ansible/release.py +1 -1
- ansible/utils/display.py +16 -26
- ansible/utils/path.py +1 -1
- ansible/utils/vars.py +4 -1
- ansible/vars/manager.py +6 -3
- ansible/vars/reserved.py +6 -4
- {ansible_core-2.19.0b6.dist-info → ansible_core-2.19.0b7.dist-info}/METADATA +1 -1
- {ansible_core-2.19.0b6.dist-info → ansible_core-2.19.0b7.dist-info}/RECORD +101 -99
- ansible_test/_internal/__init__.py +5 -0
- ansible_test/_internal/ansible_util.py +1 -1
- ansible_test/_internal/classification/python.py +6 -0
- ansible_test/_internal/cli/commands/__init__.py +0 -5
- ansible_test/_internal/cli/environments.py +51 -5
- ansible_test/_internal/commands/coverage/__init__.py +1 -1
- ansible_test/_internal/commands/integration/__init__.py +18 -5
- ansible_test/_internal/commands/integration/cloud/httptester.py +1 -1
- ansible_test/_internal/commands/sanity/__init__.py +3 -1
- ansible_test/_internal/commands/sanity/integration_aliases.py +11 -0
- ansible_test/_internal/commands/shell/__init__.py +43 -4
- ansible_test/_internal/commands/units/__init__.py +4 -1
- ansible_test/_internal/config.py +21 -13
- ansible_test/_internal/debugging.py +166 -0
- ansible_test/_internal/delegation.py +21 -13
- ansible_test/_internal/host_profiles.py +197 -6
- ansible_test/_internal/inventory.py +4 -0
- ansible_test/_internal/metadata.py +94 -4
- ansible_test/_internal/processes.py +80 -0
- ansible_test/_internal/python_requirements.py +27 -0
- ansible_test/_internal/target.py +8 -0
- ansible_test/_internal/util_common.py +13 -3
- ansible_test/_util/target/injector/python.py +8 -0
- {ansible_core-2.19.0b6.dist-info → ansible_core-2.19.0b7.dist-info}/WHEEL +0 -0
- {ansible_core-2.19.0b6.dist-info → ansible_core-2.19.0b7.dist-info}/entry_points.txt +0 -0
- {ansible_core-2.19.0b6.dist-info → ansible_core-2.19.0b7.dist-info}/licenses/COPYING +0 -0
- {ansible_core-2.19.0b6.dist-info → ansible_core-2.19.0b7.dist-info}/licenses/licenses/Apache-License.txt +0 -0
- {ansible_core-2.19.0b6.dist-info → ansible_core-2.19.0b7.dist-info}/licenses/licenses/BSD-3-Clause.txt +0 -0
- {ansible_core-2.19.0b6.dist-info → ansible_core-2.19.0b7.dist-info}/licenses/licenses/MIT-license.txt +0 -0
- {ansible_core-2.19.0b6.dist-info → ansible_core-2.19.0b7.dist-info}/licenses/licenses/PSF-license.txt +0 -0
- {ansible_core-2.19.0b6.dist-info → ansible_core-2.19.0b7.dist-info}/licenses/licenses/simplified_bsd.txt +0 -0
- {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
|
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.
|
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
|
-
///
|
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
|
-
//
|
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
|
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
|
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
|
-
#
|
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
|
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
|
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
|
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
|
-
|
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
|
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
|
ansible/module_utils/urls.py
CHANGED
@@ -1155,7 +1155,7 @@ def url_argument_spec():
|
|
1155
1155
|
|
1156
1156
|
def url_redirect_argument_spec():
|
1157
1157
|
"""
|
1158
|
-
Creates an addition
|
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=
|
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,
|
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 != '*']
|
ansible/modules/assemble.py
CHANGED
@@ -80,7 +80,7 @@ attributes:
|
|
80
80
|
bypass_host_loop:
|
81
81
|
support: none
|
82
82
|
check_mode:
|
83
|
-
support:
|
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
|
-
|
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
|
-
|
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
|
-
|
250
|
-
delta =
|
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(
|
260
|
-
end=str(
|
259
|
+
start=str(start_date),
|
260
|
+
end=str(end_date),
|
261
261
|
delta=str(delta),
|
262
262
|
changed=True,
|
263
263
|
)
|
ansible/modules/hostname.py
CHANGED
@@ -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
|
-
|
612
|
-
self.strategy =
|
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.
|
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.
|
97
|
-
in the system and you want to run pip for the Python 3.
|
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.
|
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
|
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-
|
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.
|
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.
|
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
|
-
|
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
|
77
|
-
ansible.builtin.raw: dnf install -y
|
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
|
411
|
+
# Some Berkeley based
|
412
412
|
('st_gen', 'generation'),
|
413
413
|
('st_birthtime', 'birthtime'),
|
414
414
|
# RISCOS
|
ansible/modules/wait_for.py
CHANGED
@@ -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
|
ansible/parsing/mod_args.py
CHANGED
@@ -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
|
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
|
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
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
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(
|
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,
|
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,
|
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,
|
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',
|
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
|
574
|
-
# ie, consider all
|
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
|
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():
|
ansible/playbook/helpers.py
CHANGED
@@ -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.
|