ansible-core 2.19.0b4__py3-none-any.whl → 2.19.0b6__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/__init__.py +1 -1
- ansible/_internal/_ansiballz/__init__.py +0 -0
- ansible/_internal/_ansiballz/_builder.py +101 -0
- ansible/_internal/{_ansiballz.py → _ansiballz/_wrapper.py} +11 -11
- ansible/_internal/_collection_proxy.py +1 -1
- ansible/_internal/_errors/_alarm_timeout.py +66 -0
- ansible/_internal/_errors/_captured.py +25 -30
- ansible/_internal/_errors/_error_factory.py +89 -0
- ansible/_internal/_errors/_error_utils.py +240 -0
- ansible/_internal/_errors/_task_timeout.py +28 -0
- ansible/_internal/_event_formatting.py +127 -0
- ansible/_internal/_json/__init__.py +5 -5
- ansible/_internal/_json/_profiles/_cache_persistence.py +2 -0
- ansible/_internal/_json/_profiles/_inventory_legacy.py +1 -1
- ansible/_internal/_json/_profiles/_legacy.py +3 -11
- ansible/_internal/_ssh/__init__.py +0 -0
- ansible/_internal/_ssh/_agent_launch.py +91 -0
- ansible/{utils → _internal/_ssh}/_ssh_agent.py +55 -93
- ansible/_internal/_templating/__init__.py +5 -3
- ansible/_internal/_templating/_datatag.py +2 -1
- ansible/_internal/_templating/_engine.py +3 -4
- ansible/_internal/_templating/_jinja_bits.py +28 -20
- ansible/_internal/_templating/_jinja_common.py +18 -27
- ansible/_internal/_templating/_jinja_plugins.py +36 -5
- ansible/_internal/_templating/_lazy_containers.py +5 -5
- ansible/_internal/_templating/_template_vars.py +72 -0
- ansible/_internal/_templating/_transform.py +26 -19
- ansible/_internal/_templating/_utils.py +1 -1
- ansible/_internal/_yaml/_constructor.py +4 -4
- ansible/_internal/_yaml/_dumper.py +26 -18
- ansible/_internal/_yaml/_errors.py +7 -7
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/true_type.py +1 -1
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/unmask.py +1 -1
- ansible/cli/__init__.py +11 -93
- ansible/cli/arguments/option_helpers.py +3 -4
- ansible/cli/console.py +1 -1
- ansible/cli/doc.py +86 -30
- ansible/cli/inventory.py +5 -7
- ansible/compat/importlib_resources.py +9 -12
- ansible/config/base.yml +46 -0
- ansible/errors/__init__.py +98 -50
- ansible/executor/module_common.py +75 -49
- ansible/executor/powershell/async_watchdog.ps1 +2 -2
- ansible/executor/powershell/async_wrapper.ps1 +3 -3
- ansible/executor/powershell/become_wrapper.ps1 +20 -2
- ansible/executor/powershell/bootstrap_wrapper.ps1 +28 -6
- ansible/executor/powershell/coverage_wrapper.ps1 +15 -6
- ansible/executor/powershell/exec_wrapper.ps1 +219 -6
- ansible/executor/powershell/module_manifest.py +52 -0
- ansible/executor/powershell/module_wrapper.ps1 +47 -21
- ansible/executor/powershell/powershell_expand_user.ps1 +20 -0
- ansible/executor/powershell/powershell_mkdtemp.ps1 +17 -0
- ansible/executor/process/worker.py +40 -115
- ansible/executor/task_executor.py +26 -61
- ansible/executor/task_result.py +2 -4
- ansible/galaxy/api.py +1 -4
- ansible/galaxy/collection/__init__.py +2 -10
- ansible/galaxy/collection/concrete_artifact_manager.py +2 -8
- ansible/galaxy/role.py +2 -2
- ansible/inventory/manager.py +1 -1
- ansible/module_utils/_internal/__init__.py +7 -7
- ansible/module_utils/_internal/_ambient_context.py +3 -3
- ansible/module_utils/_internal/_ansiballz/__init__.py +0 -0
- ansible/module_utils/_internal/_ansiballz/_extensions/__init__.py +0 -0
- ansible/module_utils/_internal/_ansiballz/_extensions/_coverage.py +45 -0
- ansible/module_utils/_internal/_ansiballz/_extensions/_pydevd.py +62 -0
- ansible/module_utils/_internal/{_ansiballz.py → _ansiballz/_loader.py} +13 -39
- ansible/module_utils/_internal/_ansiballz/_respawn.py +32 -0
- ansible/module_utils/_internal/_ansiballz/_respawn_wrapper.py +23 -0
- ansible/module_utils/_internal/_datatag/__init__.py +43 -15
- ansible/module_utils/_internal/_datatag/_tags.py +2 -2
- ansible/module_utils/_internal/_deprecator.py +67 -55
- ansible/module_utils/_internal/_errors.py +88 -17
- ansible/module_utils/_internal/_event_utils.py +61 -0
- ansible/module_utils/_internal/_json/_profiles/__init__.py +22 -4
- ansible/module_utils/_internal/_json/_profiles/_module_legacy_c2m.py +2 -0
- ansible/module_utils/_internal/_json/_profiles/_module_legacy_m2c.py +2 -0
- ansible/module_utils/_internal/_json/_profiles/_tagless.py +3 -1
- ansible/module_utils/{common/messages.py → _internal/_messages.py} +54 -49
- ansible/module_utils/_internal/_patches/_dataclass_annotation_patch.py +1 -3
- ansible/module_utils/_internal/_plugin_info.py +15 -2
- ansible/module_utils/_internal/_stack.py +22 -0
- ansible/module_utils/_internal/_text_utils.py +6 -0
- ansible/module_utils/_internal/_traceback.py +11 -8
- ansible/module_utils/ansible_release.py +1 -1
- ansible/module_utils/basic.py +95 -71
- ansible/module_utils/common/arg_spec.py +2 -2
- ansible/module_utils/common/collections.py +6 -0
- ansible/module_utils/common/json.py +2 -2
- ansible/module_utils/common/respawn.py +4 -41
- ansible/module_utils/common/text/converters.py +3 -3
- ansible/module_utils/common/validation.py +1 -1
- ansible/module_utils/common/warnings.py +80 -23
- ansible/module_utils/common/yaml.py +1 -1
- ansible/module_utils/connection.py +8 -11
- ansible/module_utils/datatag.py +5 -2
- ansible/module_utils/facts/hardware/linux.py +1 -1
- ansible/module_utils/facts/sysctl.py +4 -6
- ansible/module_utils/facts/system/caps.py +2 -2
- ansible/module_utils/facts/system/distribution.py +16 -3
- ansible/module_utils/facts/system/local.py +1 -1
- ansible/module_utils/facts/virtual/linux.py +2 -2
- ansible/module_utils/service.py +3 -10
- ansible/module_utils/urls.py +4 -4
- ansible/modules/apt_repository.py +17 -39
- ansible/modules/assemble.py +2 -2
- ansible/modules/async_status.py +13 -11
- ansible/modules/async_wrapper.py +12 -22
- ansible/modules/command.py +3 -3
- ansible/modules/copy.py +4 -4
- ansible/modules/cron.py +1 -1
- ansible/modules/dnf5.py +14 -22
- ansible/modules/file.py +16 -17
- ansible/modules/find.py +3 -3
- ansible/modules/get_url.py +17 -0
- ansible/modules/git.py +9 -7
- ansible/modules/hostname.py +0 -1
- ansible/modules/known_hosts.py +12 -14
- ansible/modules/package.py +6 -0
- ansible/modules/replace.py +2 -2
- ansible/modules/service.py +3 -9
- ansible/modules/slurp.py +10 -13
- ansible/modules/stat.py +5 -7
- ansible/modules/unarchive.py +6 -6
- ansible/modules/user.py +1 -1
- ansible/modules/wait_for.py +28 -30
- ansible/modules/yum_repository.py +4 -3
- ansible/parsing/ajson.py +3 -5
- ansible/parsing/dataloader.py +6 -6
- ansible/parsing/mod_args.py +1 -1
- ansible/parsing/plugin_docs.py +2 -2
- ansible/parsing/utils/yaml.py +3 -3
- ansible/parsing/vault/__init__.py +10 -14
- ansible/playbook/base.py +7 -2
- ansible/playbook/included_file.py +3 -1
- ansible/playbook/play_context.py +2 -0
- ansible/playbook/playbook_include.py +1 -1
- ansible/playbook/taggable.py +19 -8
- ansible/playbook/task.py +2 -0
- ansible/plugins/__init__.py +0 -25
- ansible/plugins/action/__init__.py +8 -31
- ansible/plugins/action/add_host.py +1 -1
- ansible/plugins/action/assemble.py +8 -16
- ansible/plugins/action/async_status.py +7 -2
- ansible/plugins/action/copy.py +8 -7
- ansible/plugins/action/fetch.py +3 -3
- ansible/plugins/action/gather_facts.py +8 -8
- ansible/plugins/action/package.py +5 -8
- ansible/plugins/action/script.py +8 -15
- ansible/plugins/action/service.py +3 -7
- ansible/plugins/action/template.py +11 -10
- ansible/plugins/action/unarchive.py +5 -15
- ansible/plugins/action/uri.py +9 -20
- ansible/plugins/cache/__init__.py +17 -19
- ansible/plugins/callback/__init__.py +4 -6
- ansible/plugins/callback/junit.py +4 -2
- ansible/plugins/callback/tree.py +5 -5
- ansible/plugins/connection/local.py +6 -6
- ansible/plugins/connection/paramiko_ssh.py +5 -5
- ansible/plugins/connection/ssh.py +25 -15
- ansible/plugins/connection/winrm.py +6 -3
- ansible/plugins/doc_fragments/constructed.py +2 -2
- ansible/plugins/filter/core.py +32 -27
- ansible/plugins/filter/encryption.py +14 -6
- ansible/plugins/inventory/__init__.py +11 -10
- ansible/plugins/inventory/script.py +1 -1
- ansible/plugins/list.py +73 -19
- ansible/plugins/loader.py +7 -7
- ansible/plugins/lookup/csvfile.py +16 -71
- ansible/plugins/lookup/first_found.py +2 -1
- ansible/plugins/lookup/template.py +9 -4
- ansible/plugins/shell/__init__.py +56 -2
- ansible/plugins/shell/powershell.py +67 -9
- ansible/plugins/shell/sh.py +10 -5
- ansible/plugins/strategy/__init__.py +3 -3
- ansible/plugins/test/core.py +22 -16
- ansible/plugins/test/finished.yml +1 -1
- ansible/plugins/test/uri.py +2 -5
- ansible/release.py +1 -1
- ansible/template/__init__.py +38 -54
- ansible/utils/collection_loader/_collection_finder.py +3 -3
- ansible/utils/display.py +124 -138
- ansible/utils/galaxy.py +2 -2
- ansible/utils/hashing.py +6 -8
- ansible/utils/listify.py +6 -4
- ansible/utils/path.py +5 -7
- ansible/utils/py3compat.py +2 -1
- ansible/utils/ssh_functions.py +3 -2
- ansible/utils/unsafe_proxy.py +1 -1
- ansible/vars/hostvars.py +1 -1
- ansible/vars/plugins.py +3 -3
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/METADATA +1 -1
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/RECORD +224 -204
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/WHEEL +1 -1
- ansible_test/_data/completion/docker.txt +3 -3
- ansible_test/_data/completion/remote.txt +1 -0
- ansible_test/_data/requirements/sanity.ansible-doc.txt +1 -1
- ansible_test/_data/requirements/sanity.changelog.txt +2 -2
- ansible_test/_data/requirements/sanity.pep8.txt +1 -1
- ansible_test/_data/requirements/sanity.pylint.txt +4 -4
- ansible_test/_data/requirements/sanity.yamllint.txt +1 -1
- ansible_test/_internal/commands/integration/coverage.py +7 -2
- ansible_test/_internal/host_profiles.py +62 -10
- ansible_test/_internal/provisioning.py +10 -4
- ansible_test/_internal/ssh.py +1 -5
- ansible_test/_internal/thread.py +2 -1
- ansible_test/_internal/timeout.py +1 -1
- ansible_test/_internal/util.py +40 -12
- ansible_test/_util/controller/sanity/pylint/config/ansible-test-target.cfg +1 -0
- ansible_test/_util/controller/sanity/pylint/config/ansible-test.cfg +1 -0
- ansible_test/_util/controller/sanity/pylint/config/code-smell.cfg +1 -0
- ansible_test/_util/controller/sanity/pylint/config/collection.cfg +1 -0
- ansible_test/_util/controller/sanity/pylint/config/default.cfg +1 -0
- ansible_test/_util/controller/sanity/pylint/plugins/deprecated_calls.py +61 -7
- ansible_test/_util/target/setup/bootstrap.sh +31 -0
- ansible_test/_util/target/setup/requirements.py +3 -9
- ansible/_internal/_errors/_utils.py +0 -310
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/entry_points.txt +0 -0
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/COPYING +0 -0
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/Apache-License.txt +0 -0
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/BSD-3-Clause.txt +0 -0
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/MIT-license.txt +0 -0
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/PSF-license.txt +0 -0
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/simplified_bsd.txt +0 -0
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/top_level.txt +0 -0
@@ -22,7 +22,7 @@ DOCUMENTATION = """
|
|
22
22
|
default: true
|
23
23
|
deprecated:
|
24
24
|
why: This option is no longer used in the Ansible Core code base.
|
25
|
-
version: "2.
|
25
|
+
version: "2.23"
|
26
26
|
alternatives: Jinja2 native mode is now the default and only option, which is mutually exclusive with this option.
|
27
27
|
variable_start_string:
|
28
28
|
description: The string marking the beginning of a print statement.
|
@@ -45,7 +45,7 @@ DOCUMENTATION = """
|
|
45
45
|
type: bool
|
46
46
|
deprecated:
|
47
47
|
why: This option is no longer used in the Ansible Core code base.
|
48
|
-
version: "2.
|
48
|
+
version: "2.23"
|
49
49
|
alternatives: Jinja2 native mode is now the default and only option.
|
50
50
|
template_vars:
|
51
51
|
description: A dictionary, the keys become additional variables available for templating.
|
@@ -105,7 +105,8 @@ import os
|
|
105
105
|
|
106
106
|
from ansible.errors import AnsibleError
|
107
107
|
from ansible.plugins.lookup import LookupBase
|
108
|
-
from ansible.template import
|
108
|
+
from ansible.template import trust_as_template
|
109
|
+
from ansible._internal._templating import _template_vars
|
109
110
|
from ansible.utils.display import Display
|
110
111
|
|
111
112
|
|
@@ -157,7 +158,11 @@ class LookupModule(LookupBase):
|
|
157
158
|
# argument.
|
158
159
|
# FIXME: why isn't this a chainmap with a sacrificial bottom layer?
|
159
160
|
vars = deepcopy(variables)
|
160
|
-
vars.update(generate_ansible_template_vars(
|
161
|
+
vars.update(_template_vars.generate_ansible_template_vars(
|
162
|
+
path=term,
|
163
|
+
fullpath=lookupfile,
|
164
|
+
include_ansible_managed='ansible_managed' not in vars, # do not clobber ansible_managed when set by the user
|
165
|
+
))
|
161
166
|
vars.update(lookup_template_vars)
|
162
167
|
|
163
168
|
overrides = dict(
|
@@ -16,6 +16,7 @@
|
|
16
16
|
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
17
17
|
from __future__ import annotations
|
18
18
|
|
19
|
+
import dataclasses
|
19
20
|
import os
|
20
21
|
import os.path
|
21
22
|
import re
|
@@ -33,6 +34,13 @@ from ansible.plugins import AnsiblePlugin
|
|
33
34
|
_USER_HOME_PATH_RE = re.compile(r'^~[_.A-Za-z0-9][-_.A-Za-z0-9]*$')
|
34
35
|
|
35
36
|
|
37
|
+
@dataclasses.dataclass(frozen=True, kw_only=True, slots=True)
|
38
|
+
class _ShellCommand:
|
39
|
+
"""Internal type returned by shell subsystems that may require both an execution payload and a command (eg powershell)."""
|
40
|
+
command: str
|
41
|
+
input_data: bytes | None = None
|
42
|
+
|
43
|
+
|
36
44
|
class ShellBase(AnsiblePlugin):
|
37
45
|
def __init__(self):
|
38
46
|
|
@@ -121,7 +129,13 @@ class ShellBase(AnsiblePlugin):
|
|
121
129
|
cmd = ['test', '-e', self.quote(path)]
|
122
130
|
return ' '.join(cmd)
|
123
131
|
|
124
|
-
def mkdtemp(
|
132
|
+
def mkdtemp(
|
133
|
+
self,
|
134
|
+
basefile: str | None = None,
|
135
|
+
system: bool = False,
|
136
|
+
mode: int = 0o700,
|
137
|
+
tmpdir: str | None = None,
|
138
|
+
) -> str:
|
125
139
|
if not basefile:
|
126
140
|
basefile = self.__class__._generate_temp_dir_name()
|
127
141
|
|
@@ -163,7 +177,31 @@ class ShellBase(AnsiblePlugin):
|
|
163
177
|
|
164
178
|
return cmd
|
165
179
|
|
166
|
-
def
|
180
|
+
def _mkdtemp2(
|
181
|
+
self,
|
182
|
+
basefile: str | None = None,
|
183
|
+
system: bool = False,
|
184
|
+
mode: int = 0o700,
|
185
|
+
tmpdir: str | None = None,
|
186
|
+
) -> _ShellCommand:
|
187
|
+
"""Gets command info to create a temporary directory.
|
188
|
+
|
189
|
+
This is an internal API that should not be used publicly.
|
190
|
+
|
191
|
+
:args basefile: The base name of the temporary directory.
|
192
|
+
:args system: If True, create the directory in a system-wide location.
|
193
|
+
:args mode: The permissions mode for the directory.
|
194
|
+
:args tmpdir: The directory in which to create the temporary directory.
|
195
|
+
:returns: The shell command to run to create the temp directory.
|
196
|
+
"""
|
197
|
+
cmd = self.mkdtemp(basefile=basefile, system=system, mode=mode, tmpdir=tmpdir)
|
198
|
+
return _ShellCommand(command=cmd, input_data=None)
|
199
|
+
|
200
|
+
def expand_user(
|
201
|
+
self,
|
202
|
+
user_home_path: str,
|
203
|
+
username: str = '',
|
204
|
+
) -> str:
|
167
205
|
""" Return a command to expand tildes in a path
|
168
206
|
|
169
207
|
It can be either "~" or "~username". We just ignore $HOME
|
@@ -184,6 +222,22 @@ class ShellBase(AnsiblePlugin):
|
|
184
222
|
|
185
223
|
return 'echo %s' % user_home_path
|
186
224
|
|
225
|
+
def _expand_user2(
|
226
|
+
self,
|
227
|
+
user_home_path: str,
|
228
|
+
username: str = '',
|
229
|
+
) -> _ShellCommand:
|
230
|
+
"""Gets command to expand user path.
|
231
|
+
|
232
|
+
This is an internal API that should not be used publicly.
|
233
|
+
|
234
|
+
:args user_home_path: The path to expand.
|
235
|
+
:args username: The username to use for expansion.
|
236
|
+
:returns: The shell command to run to get the expanded user path.
|
237
|
+
"""
|
238
|
+
cmd = self.expand_user(user_home_path, username=username)
|
239
|
+
return _ShellCommand(command=cmd, input_data=None)
|
240
|
+
|
187
241
|
def pwd(self):
|
188
242
|
"""Return the working directory after connecting"""
|
189
243
|
return 'echo %spwd%s' % (self._SHELL_SUB_LEFT, self._SHELL_SUB_RIGHT)
|
@@ -21,9 +21,13 @@ import shlex
|
|
21
21
|
import xml.etree.ElementTree as ET
|
22
22
|
import ntpath
|
23
23
|
|
24
|
-
from ansible.executor.powershell.module_manifest import _get_powershell_script
|
24
|
+
from ansible.executor.powershell.module_manifest import _bootstrap_powershell_script, _get_powershell_script
|
25
25
|
from ansible.module_utils.common.text.converters import to_bytes, to_text
|
26
|
-
from ansible.plugins.shell import ShellBase
|
26
|
+
from ansible.plugins.shell import ShellBase, _ShellCommand
|
27
|
+
from ansible.utils.display import Display
|
28
|
+
|
29
|
+
|
30
|
+
display = Display()
|
27
31
|
|
28
32
|
# This is weird, we are matching on byte sequences that match the utf-16-be
|
29
33
|
# matches for '_x(a-fA-F0-9){4}_'. The \x00 and {4} will match the hex sequence
|
@@ -225,9 +229,15 @@ class ShellModule(ShellBase):
|
|
225
229
|
else:
|
226
230
|
return self._encode_script("""Remove-Item '%s' -Force;""" % path)
|
227
231
|
|
228
|
-
def mkdtemp(
|
229
|
-
|
230
|
-
|
232
|
+
def mkdtemp(
|
233
|
+
self,
|
234
|
+
basefile: str | None = None,
|
235
|
+
system: bool = False,
|
236
|
+
mode: int = 0o700,
|
237
|
+
tmpdir: str | None = None,
|
238
|
+
) -> str:
|
239
|
+
# This is not called in Ansible anymore but it is kept for backwards
|
240
|
+
# compatibility in case other action plugins outside Ansible calls this.
|
231
241
|
if not basefile:
|
232
242
|
basefile = self.__class__._generate_temp_dir_name()
|
233
243
|
basefile = self._escape(self._unquote(basefile))
|
@@ -241,10 +251,38 @@ class ShellModule(ShellBase):
|
|
241
251
|
"""
|
242
252
|
return self._encode_script(script.strip())
|
243
253
|
|
244
|
-
def
|
245
|
-
|
246
|
-
|
247
|
-
|
254
|
+
def _mkdtemp2(
|
255
|
+
self,
|
256
|
+
basefile: str | None = None,
|
257
|
+
system: bool = False,
|
258
|
+
mode: int = 0o700,
|
259
|
+
tmpdir: str | None = None,
|
260
|
+
) -> _ShellCommand:
|
261
|
+
# Windows does not have an equivalent for the system temp files, so
|
262
|
+
# the param is ignored
|
263
|
+
if not basefile:
|
264
|
+
basefile = self.__class__._generate_temp_dir_name()
|
265
|
+
|
266
|
+
basefile = self._unquote(basefile)
|
267
|
+
basetmpdir = tmpdir if tmpdir else self.get_option('remote_tmp')
|
268
|
+
|
269
|
+
script, stdin = _bootstrap_powershell_script("powershell_mkdtemp.ps1", {
|
270
|
+
'Directory': basetmpdir,
|
271
|
+
'Name': basefile,
|
272
|
+
})
|
273
|
+
|
274
|
+
return _ShellCommand(
|
275
|
+
command=self._encode_script(script),
|
276
|
+
input_data=stdin,
|
277
|
+
)
|
278
|
+
|
279
|
+
def expand_user(
|
280
|
+
self,
|
281
|
+
user_home_path: str,
|
282
|
+
username: str = '',
|
283
|
+
) -> str:
|
284
|
+
# This is not called in Ansible anymore but it is kept for backwards
|
285
|
+
# compatibility in case other actions plugins outside Ansible called this.
|
248
286
|
user_home_path = self._unquote(user_home_path)
|
249
287
|
if user_home_path == '~':
|
250
288
|
script = 'Write-Output (Get-Location).Path'
|
@@ -254,6 +292,21 @@ class ShellModule(ShellBase):
|
|
254
292
|
script = "Write-Output '%s'" % self._escape(user_home_path)
|
255
293
|
return self._encode_script(f"{self._CONSOLE_ENCODING}; {script}")
|
256
294
|
|
295
|
+
def _expand_user2(
|
296
|
+
self,
|
297
|
+
user_home_path: str,
|
298
|
+
username: str = '',
|
299
|
+
) -> _ShellCommand:
|
300
|
+
user_home_path = self._unquote(user_home_path)
|
301
|
+
script, stdin = _bootstrap_powershell_script("powershell_expand_user.ps1", {
|
302
|
+
'Path': user_home_path,
|
303
|
+
})
|
304
|
+
|
305
|
+
return _ShellCommand(
|
306
|
+
command=self._encode_script(script),
|
307
|
+
input_data=stdin,
|
308
|
+
)
|
309
|
+
|
257
310
|
def exists(self, path):
|
258
311
|
path = self._escape(self._unquote(path))
|
259
312
|
script = """
|
@@ -271,6 +324,11 @@ class ShellModule(ShellBase):
|
|
271
324
|
return self._encode_script(script)
|
272
325
|
|
273
326
|
def checksum(self, path, *args, **kwargs):
|
327
|
+
display.deprecated(
|
328
|
+
msg="The `ShellModule.checksum` method is deprecated.",
|
329
|
+
version="2.23",
|
330
|
+
help_text="Use `ActionBase._execute_remote_stat()` instead.",
|
331
|
+
)
|
274
332
|
path = self._escape(self._unquote(path))
|
275
333
|
script = """
|
276
334
|
If (Test-Path -PathType Leaf '%(path)s')
|
ansible/plugins/shell/sh.py
CHANGED
@@ -14,6 +14,10 @@ extends_documentation_fragment:
|
|
14
14
|
"""
|
15
15
|
|
16
16
|
from ansible.plugins.shell import ShellBase
|
17
|
+
from ansible.utils.display import Display
|
18
|
+
|
19
|
+
|
20
|
+
display = Display()
|
17
21
|
|
18
22
|
|
19
23
|
class ShellModule(ShellBase):
|
@@ -43,6 +47,11 @@ class ShellModule(ShellBase):
|
|
43
47
|
_SHELL_GROUP_RIGHT = ')'
|
44
48
|
|
45
49
|
def checksum(self, path, python_interp):
|
50
|
+
display.deprecated(
|
51
|
+
msg="The `ShellModule.checksum` method is deprecated.",
|
52
|
+
version="2.23",
|
53
|
+
help_text="Use `ActionBase._execute_remote_stat()` instead.",
|
54
|
+
)
|
46
55
|
# In the following test, each condition is a check and logical
|
47
56
|
# comparison (|| or &&) that sets the rc value. Every check is run so
|
48
57
|
# the last check in the series to fail will be the rc that is returned.
|
@@ -67,11 +76,7 @@ class ShellModule(ShellBase):
|
|
67
76
|
# "one-liner".
|
68
77
|
shell_escaped_path = self.quote(path)
|
69
78
|
test = "rc=flag; [ -r %(p)s ] %(shell_or)s rc=2; [ -f %(p)s ] %(shell_or)s rc=1; [ -d %(p)s ] %(shell_and)s rc=3; %(i)s -V 2>/dev/null %(shell_or)s rc=4; [ x\"$rc\" != \"xflag\" ] %(shell_and)s echo \"${rc} \"%(p)s %(shell_and)s exit 0" % dict(p=shell_escaped_path, i=python_interp, shell_and=self._SHELL_AND, shell_or=self._SHELL_OR) # NOQA
|
70
|
-
|
71
|
-
u"({0} -c 'import hashlib; BLOCKSIZE = 65536; hasher = hashlib.sha1();{2}afile = open(\"'{1}'\", \"rb\"){2}buf = afile.read(BLOCKSIZE){2}while len(buf) > 0:{2}\thasher.update(buf){2}\tbuf = afile.read(BLOCKSIZE){2}afile.close(){2}print(hasher.hexdigest())' 2>/dev/null)".format(python_interp, shell_escaped_path, self._SHELL_EMBEDDED_PY_EOL), # NOQA Python > 2.4 (including python3)
|
72
|
-
u"({0} -c 'import sha; BLOCKSIZE = 65536; hasher = sha.sha();{2}afile = open(\"'{1}'\", \"rb\"){2}buf = afile.read(BLOCKSIZE){2}while len(buf) > 0:{2}\thasher.update(buf){2}\tbuf = afile.read(BLOCKSIZE){2}afile.close(){2}print(hasher.hexdigest())' 2>/dev/null)".format(python_interp, shell_escaped_path, self._SHELL_EMBEDDED_PY_EOL), # NOQA Python == 2.4
|
73
|
-
]
|
79
|
+
cmd = "({0} -c 'import hashlib; BLOCKSIZE = 65536; hasher = hashlib.sha1();{2}afile = open(\"'{1}'\", \"rb\"){2}buf = afile.read(BLOCKSIZE){2}while len(buf) > 0:{2}\thasher.update(buf){2}\tbuf = afile.read(BLOCKSIZE){2}afile.close(){2}print(hasher.hexdigest())' 2>/dev/null)".format(python_interp, shell_escaped_path, self._SHELL_EMBEDDED_PY_EOL) # NOQA
|
74
80
|
|
75
|
-
cmd = (" %s " % self._SHELL_OR).join(csums)
|
76
81
|
cmd = "%s; %s %s (echo \'0 \'%s)" % (test, cmd, self._SHELL_OR, shell_escaped_path)
|
77
82
|
return cmd
|
@@ -128,7 +128,7 @@ def results_thread_main(strategy: StrategyBase) -> None:
|
|
128
128
|
strategy._workers[result.worker_id].worker_queue.put(value)
|
129
129
|
else:
|
130
130
|
display.warning('Received an invalid object (%s) in the result queue: %r' % (type(result), result))
|
131
|
-
except (
|
131
|
+
except (OSError, EOFError):
|
132
132
|
break
|
133
133
|
except queue.Empty:
|
134
134
|
pass
|
@@ -402,9 +402,9 @@ class StrategyBase:
|
|
402
402
|
time.sleep(0.0001)
|
403
403
|
|
404
404
|
self._pending_results += 1
|
405
|
-
except (EOFError,
|
405
|
+
except (EOFError, OSError, AssertionError) as ex:
|
406
406
|
# most likely an abort
|
407
|
-
display.debug("got an error while queuing:
|
407
|
+
display.debug(f"got an error while queuing: {ex}")
|
408
408
|
return
|
409
409
|
display.debug("exiting _queue_task() for %s/%s" % (host.name, task.action))
|
410
410
|
|
ansible/plugins/test/core.py
CHANGED
@@ -31,7 +31,7 @@ from ansible import errors
|
|
31
31
|
from ansible.module_utils.common.text.converters import to_native, to_text, to_bytes
|
32
32
|
from ansible._internal._templating._jinja_common import Marker, UndefinedMarker
|
33
33
|
from ansible.module_utils.parsing.convert_bool import boolean
|
34
|
-
from ansible.
|
34
|
+
from ansible.template import accept_args_markers
|
35
35
|
from ansible.parsing.vault import is_encrypted_file, VaultHelper, VaultLib
|
36
36
|
from ansible.utils.display import Display
|
37
37
|
from ansible.utils.version import SemanticVersion
|
@@ -49,37 +49,41 @@ def timedout(result):
|
|
49
49
|
""" Test if task result yields a time out"""
|
50
50
|
if not isinstance(result, MutableMapping):
|
51
51
|
raise errors.AnsibleFilterError("The 'timedout' test expects a dictionary")
|
52
|
-
|
52
|
+
|
53
|
+
return bool(result.get('timedout') and bool(result['timedout'].get('period')))
|
53
54
|
|
54
55
|
|
55
56
|
def failed(result):
|
56
57
|
""" Test if task result yields failed """
|
57
58
|
if not isinstance(result, MutableMapping):
|
58
59
|
raise errors.AnsibleFilterError("The 'failed' test expects a dictionary")
|
59
|
-
|
60
|
+
|
61
|
+
return bool(result.get('failed'))
|
60
62
|
|
61
63
|
|
62
64
|
def success(result):
|
63
65
|
""" Test if task result yields success """
|
64
|
-
return not failed(result)
|
66
|
+
return not bool(failed(result))
|
65
67
|
|
66
68
|
|
67
69
|
def unreachable(result):
|
68
70
|
""" Test if task result yields unreachable """
|
69
71
|
if not isinstance(result, MutableMapping):
|
70
72
|
raise errors.AnsibleFilterError("The 'unreachable' test expects a dictionary")
|
71
|
-
|
73
|
+
|
74
|
+
return bool(result.get('unreachable'))
|
72
75
|
|
73
76
|
|
74
77
|
def reachable(result):
|
75
78
|
""" Test if task result yields reachable """
|
76
|
-
return not unreachable(result)
|
79
|
+
return bool(not unreachable(result))
|
77
80
|
|
78
81
|
|
79
82
|
def changed(result):
|
80
83
|
""" Test if task result yields changed """
|
81
84
|
if not isinstance(result, MutableMapping):
|
82
85
|
raise errors.AnsibleFilterError("The 'changed' test expects a dictionary")
|
86
|
+
|
83
87
|
if 'changed' not in result:
|
84
88
|
changed = False
|
85
89
|
if (
|
@@ -88,29 +92,31 @@ def changed(result):
|
|
88
92
|
isinstance(result['results'][0], MutableMapping)
|
89
93
|
):
|
90
94
|
for res in result['results']:
|
91
|
-
if res.get('changed'
|
95
|
+
if res.get('changed'):
|
92
96
|
changed = True
|
93
97
|
break
|
94
98
|
else:
|
95
|
-
changed = result.get('changed'
|
96
|
-
|
99
|
+
changed = result.get('changed')
|
100
|
+
|
101
|
+
return bool(changed)
|
97
102
|
|
98
103
|
|
99
104
|
def skipped(result):
|
100
105
|
""" Test if task result yields skipped """
|
101
106
|
if not isinstance(result, MutableMapping):
|
102
107
|
raise errors.AnsibleFilterError("The 'skipped' test expects a dictionary")
|
103
|
-
|
108
|
+
|
109
|
+
return bool(result.get('skipped'))
|
104
110
|
|
105
111
|
|
106
112
|
def started(result):
|
107
113
|
""" Test if async task has started """
|
108
114
|
if not isinstance(result, MutableMapping):
|
109
115
|
raise errors.AnsibleFilterError("The 'started' test expects a dictionary")
|
116
|
+
|
110
117
|
if 'started' in result:
|
111
118
|
# For async tasks, return status
|
112
|
-
|
113
|
-
return result.get('started', 0) == 1
|
119
|
+
return bool(result.get('started'))
|
114
120
|
else:
|
115
121
|
# For non-async tasks, warn user, but return as if started
|
116
122
|
display.warning("The 'started' test expects an async task, but a non-async task was tested")
|
@@ -121,10 +127,10 @@ def finished(result):
|
|
121
127
|
""" Test if async task has finished """
|
122
128
|
if not isinstance(result, MutableMapping):
|
123
129
|
raise errors.AnsibleFilterError("The 'finished' test expects a dictionary")
|
130
|
+
|
124
131
|
if 'finished' in result:
|
125
132
|
# For async tasks, return status
|
126
|
-
|
127
|
-
return result.get('finished', 0) == 1
|
133
|
+
return bool(result.get('finished'))
|
128
134
|
else:
|
129
135
|
# For non-async tasks, warn user, but return as if finished
|
130
136
|
display.warning("The 'finished' test expects an async task, but a non-async task was tested")
|
@@ -169,8 +175,8 @@ def vaulted_file(value):
|
|
169
175
|
try:
|
170
176
|
with open(to_bytes(value), 'rb') as f:
|
171
177
|
return is_encrypted_file(f)
|
172
|
-
except
|
173
|
-
raise errors.AnsibleFilterError(f"Cannot test if the file {value} is a vault"
|
178
|
+
except OSError as ex:
|
179
|
+
raise errors.AnsibleFilterError(f"Cannot test if the file {value!r} is a vault.") from ex
|
174
180
|
|
175
181
|
|
176
182
|
def match(value, pattern='', ignorecase=False, multiline=False):
|
@@ -5,7 +5,7 @@ DOCUMENTATION:
|
|
5
5
|
short_description: Did async task finish
|
6
6
|
description:
|
7
7
|
- Used to test if an async task has finished, it will also work with normal tasks but will issue a warning.
|
8
|
-
- This test checks for the existence of a C(finished) key in the input dictionary and that it is V(
|
8
|
+
- This test checks for the existence of a C(finished) key in the input dictionary and that it is V(True) if present
|
9
9
|
options:
|
10
10
|
_input:
|
11
11
|
description: registered result from an Ansible task
|
ansible/plugins/test/uri.py
CHANGED
@@ -20,11 +20,8 @@ def is_url(value, schemes=None):
|
|
20
20
|
|
21
21
|
isit = is_uri(value, schemes)
|
22
22
|
if isit:
|
23
|
-
|
24
|
-
|
25
|
-
isit = bool(x.netloc or x.scheme == 'file')
|
26
|
-
except Exception as e:
|
27
|
-
isit = False
|
23
|
+
x = urlparse(value)
|
24
|
+
isit = bool(x.netloc or x.scheme == 'file')
|
28
25
|
return isit
|
29
26
|
|
30
27
|
|
ansible/release.py
CHANGED
ansible/template/__init__.py
CHANGED
@@ -1,22 +1,18 @@
|
|
1
1
|
from __future__ import annotations as _annotations
|
2
2
|
|
3
3
|
import contextlib as _contextlib
|
4
|
-
import datetime as _datetime
|
5
4
|
import io as _io
|
6
5
|
import os as _os
|
7
|
-
import pwd as _pwd
|
8
|
-
import time as _time
|
9
6
|
import typing as _t
|
10
7
|
|
11
8
|
from jinja2 import environment as _environment
|
12
9
|
|
13
10
|
from ansible import _internal
|
14
|
-
from ansible import constants as _constants
|
15
11
|
from ansible import errors as _errors
|
16
12
|
from ansible._internal._datatag import _tags, _wrappers
|
17
|
-
from ansible._internal._templating import _jinja_bits, _engine, _jinja_common
|
13
|
+
from ansible._internal._templating import _jinja_bits, _engine, _jinja_common, _template_vars
|
14
|
+
|
18
15
|
from ansible.module_utils import datatag as _module_utils_datatag
|
19
|
-
from ansible.module_utils._internal import _datatag
|
20
16
|
from ansible.utils.display import Display as _Display
|
21
17
|
|
22
18
|
if _t.TYPE_CHECKING: # pragma: nocover
|
@@ -98,7 +94,7 @@ class Templar:
|
|
98
94
|
@property
|
99
95
|
def basedir(self) -> str:
|
100
96
|
"""The basedir from DataLoader."""
|
101
|
-
# DTFIX-
|
97
|
+
# DTFIX-FUTURE: come up with a better way to handle this so it can be deprecated
|
102
98
|
return self._engine.basedir
|
103
99
|
|
104
100
|
@property
|
@@ -352,57 +348,17 @@ class Templar:
|
|
352
348
|
)
|
353
349
|
|
354
350
|
|
355
|
-
def generate_ansible_template_vars(
|
351
|
+
def generate_ansible_template_vars(
|
352
|
+
path: str,
|
353
|
+
fullpath: str | None = None,
|
354
|
+
dest_path: str | None = None,
|
355
|
+
) -> dict[str, object]:
|
356
356
|
"""
|
357
357
|
Generate and return a dictionary with variable metadata about the template specified by `fullpath`.
|
358
358
|
If `fullpath` is `None`, `path` will be used instead.
|
359
359
|
"""
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
template_path = fullpath
|
364
|
-
template_stat = _os.stat(template_path)
|
365
|
-
|
366
|
-
template_uid: int | str
|
367
|
-
|
368
|
-
try:
|
369
|
-
template_uid = _pwd.getpwuid(template_stat.st_uid).pw_name
|
370
|
-
except KeyError:
|
371
|
-
template_uid = template_stat.st_uid
|
372
|
-
|
373
|
-
managed_default = _constants.config.get_config_value('DEFAULT_MANAGED_STR')
|
374
|
-
|
375
|
-
managed_str = managed_default.format(
|
376
|
-
# IMPORTANT: These values must be constant strings to avoid template injection.
|
377
|
-
# Use Jinja template expressions where variables are needed.
|
378
|
-
host="{{ template_host }}",
|
379
|
-
uid="{{ template_uid }}",
|
380
|
-
file="{{ template_path }}",
|
381
|
-
)
|
382
|
-
|
383
|
-
ansible_managed = _time.strftime(managed_str, _time.localtime(template_stat.st_mtime))
|
384
|
-
# DTFIX-RELEASE: this should not be tag_copy, it should either be an origin copy or some kind of derived origin
|
385
|
-
ansible_managed = _datatag.AnsibleTagHelper.tag_copy(managed_default, ansible_managed)
|
386
|
-
ansible_managed = trust_as_template(ansible_managed)
|
387
|
-
ansible_managed = _module_utils_datatag.deprecate_value(
|
388
|
-
value=ansible_managed,
|
389
|
-
msg="The `ansible_managed` variable is deprecated.",
|
390
|
-
help_text="Define and use a custom variable instead.",
|
391
|
-
version='2.23',
|
392
|
-
)
|
393
|
-
|
394
|
-
temp_vars = dict(
|
395
|
-
template_host=_os.uname()[1],
|
396
|
-
template_path=path,
|
397
|
-
template_mtime=_datetime.datetime.fromtimestamp(template_stat.st_mtime),
|
398
|
-
template_uid=template_uid,
|
399
|
-
template_run_date=_datetime.datetime.now(),
|
400
|
-
template_destpath=dest_path,
|
401
|
-
template_fullpath=fullpath,
|
402
|
-
ansible_managed=ansible_managed,
|
403
|
-
)
|
404
|
-
|
405
|
-
return temp_vars
|
360
|
+
# deprecated description="deprecate `generate_ansible_template_vars`, collections should inline the necessary variables" core_version="2.23"
|
361
|
+
return _template_vars.generate_ansible_template_vars(path=path, fullpath=fullpath, dest_path=dest_path, include_ansible_managed=True)
|
406
362
|
|
407
363
|
|
408
364
|
def trust_as_template(value: _TTrustable) -> _TTrustable:
|
@@ -427,3 +383,31 @@ def is_trusted_as_template(value: object) -> bool:
|
|
427
383
|
This function should not be needed for production code, but may be useful in unit tests.
|
428
384
|
"""
|
429
385
|
return isinstance(value, _TRUSTABLE_TYPES) and _tags.TrustedAsTemplate.is_tagged_on(value)
|
386
|
+
|
387
|
+
|
388
|
+
_TCallable = _t.TypeVar('_TCallable', bound=_t.Callable)
|
389
|
+
|
390
|
+
|
391
|
+
def accept_args_markers(plugin: _TCallable) -> _TCallable:
|
392
|
+
"""
|
393
|
+
A decorator to mark a Jinja plugin as capable of handling `Marker` values for its top-level arguments.
|
394
|
+
Non-decorated plugin invocation is skipped when a top-level argument is a `Marker`, with the first such value substituted as the plugin result.
|
395
|
+
This ensures that only plugins which understand `Marker` instances for top-level arguments will encounter them.
|
396
|
+
"""
|
397
|
+
plugin.accept_args_markers = True
|
398
|
+
|
399
|
+
return plugin
|
400
|
+
|
401
|
+
|
402
|
+
def accept_lazy_markers(plugin: _TCallable) -> _TCallable:
|
403
|
+
"""
|
404
|
+
A decorator to mark a Jinja plugin as capable of handling `Marker` values retrieved from lazy containers.
|
405
|
+
Non-decorated plugins will trigger a `MarkerError` exception when attempting to retrieve a `Marker` from a lazy container.
|
406
|
+
This ensures that only plugins which understand lazy retrieval of `Marker` instances will encounter them.
|
407
|
+
"""
|
408
|
+
plugin.accept_lazy_markers = True
|
409
|
+
|
410
|
+
return plugin
|
411
|
+
|
412
|
+
|
413
|
+
get_first_marker_arg = _jinja_common.get_first_marker_arg
|
@@ -1095,8 +1095,8 @@ def _get_collection_playbook_path(playbook):
|
|
1095
1095
|
try:
|
1096
1096
|
# get_collection_path
|
1097
1097
|
pkg = import_module(acr.n_python_collection_package_name)
|
1098
|
-
except (
|
1099
|
-
# leaving
|
1098
|
+
except (OSError, ModuleNotFoundError) as ex:
|
1099
|
+
# leaving ex as debug target, even though not used in normal code
|
1100
1100
|
pkg = None
|
1101
1101
|
|
1102
1102
|
if pkg:
|
@@ -1151,7 +1151,7 @@ def _get_collection_resource_path(name, ref_type, collection_list=None):
|
|
1151
1151
|
path = os.path.dirname(_to_bytes(sys.modules[acr.n_python_package_name].__file__))
|
1152
1152
|
return resource, _to_text(path), collection_name
|
1153
1153
|
|
1154
|
-
except (
|
1154
|
+
except (OSError, ModuleNotFoundError) as ex:
|
1155
1155
|
continue
|
1156
1156
|
except Exception as ex:
|
1157
1157
|
# FIXME: pick out typical import errors first, then error logging
|