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
@@ -6,8 +6,12 @@ import collections.abc as c
|
|
6
6
|
import dataclasses
|
7
7
|
import datetime
|
8
8
|
import functools
|
9
|
+
import inspect
|
10
|
+
import re
|
9
11
|
import typing as t
|
10
12
|
|
13
|
+
from jinja2 import defaults
|
14
|
+
|
11
15
|
from ansible.module_utils._internal._ambient_context import AmbientContextBase
|
12
16
|
from ansible.module_utils.common.collections import is_sequence
|
13
17
|
from ansible.module_utils._internal._datatag import AnsibleTagHelper
|
@@ -115,7 +119,7 @@ class JinjaPluginIntercept(c.MutableMapping):
|
|
115
119
|
except MarkerError as ex:
|
116
120
|
return ex.source
|
117
121
|
except Exception as ex:
|
118
|
-
raise AnsibleTemplatePluginRuntimeError(instance.plugin_type, instance.ansible_name) from ex # DTFIX-
|
122
|
+
raise AnsibleTemplatePluginRuntimeError(instance.plugin_type, instance.ansible_name) from ex # DTFIX-FUTURE: which name to use? use plugin info?
|
119
123
|
|
120
124
|
def _wrap_test(self, instance: AnsibleJinja2Plugin) -> t.Callable:
|
121
125
|
"""Intercept point for all test plugins to ensure that args are properly templated/lazified."""
|
@@ -124,10 +128,12 @@ class JinjaPluginIntercept(c.MutableMapping):
|
|
124
128
|
def wrapper(*args, **kwargs) -> bool | Marker:
|
125
129
|
result = self._invoke_plugin(instance, *args, **kwargs)
|
126
130
|
|
131
|
+
if isinstance(result, Marker):
|
132
|
+
return result
|
133
|
+
|
127
134
|
if not isinstance(result, bool):
|
128
135
|
template = TemplateContext.current().template_value
|
129
136
|
|
130
|
-
# DTFIX-RELEASE: which name to use? use plugin info?
|
131
137
|
_display.deprecated(
|
132
138
|
msg=f"The test plugin {instance.ansible_name!r} returned a non-boolean result of type {type(result)!r}. "
|
133
139
|
"Test plugins must have a boolean result.",
|
@@ -157,7 +163,7 @@ class JinjaPluginIntercept(c.MutableMapping):
|
|
157
163
|
class _DirectCall:
|
158
164
|
"""Functions/methods marked `_DirectCall` bypass Jinja Environment checks for `Marker`."""
|
159
165
|
|
160
|
-
_marker_attr: str = "_directcall"
|
166
|
+
_marker_attr: t.Final[str] = "_directcall"
|
161
167
|
|
162
168
|
@classmethod
|
163
169
|
def mark(cls, src: _TCallable) -> _TCallable:
|
@@ -166,7 +172,7 @@ class _DirectCall:
|
|
166
172
|
|
167
173
|
@classmethod
|
168
174
|
def is_marked(cls, value: t.Callable) -> bool:
|
169
|
-
return callable(value) and getattr(value,
|
175
|
+
return callable(value) and getattr(value, cls._marker_attr, False)
|
170
176
|
|
171
177
|
|
172
178
|
@_DirectCall.mark
|
@@ -254,7 +260,7 @@ def _invoke_lookup(*, plugin_name: str, lookup_terms: list, lookup_kwargs: dict[
|
|
254
260
|
except MarkerError as ex:
|
255
261
|
return ex.source
|
256
262
|
except Exception as ex:
|
257
|
-
# DTFIX-
|
263
|
+
# DTFIX-FUTURE: convert this to the new error/warn/ignore context manager
|
258
264
|
if errors == 'warn':
|
259
265
|
_display.error_as_warning(
|
260
266
|
msg=f'An error occurred while running the lookup plugin {plugin_name!r}.',
|
@@ -339,3 +345,28 @@ def _wrap_plugin_output(o: t.Any) -> t.Any:
|
|
339
345
|
o = list(o)
|
340
346
|
|
341
347
|
return _AnsibleLazyTemplateMixin._try_create(o, LazyOptions.SKIP_TEMPLATES)
|
348
|
+
|
349
|
+
|
350
|
+
_PLUGIN_SOURCES = dict(
|
351
|
+
filter=defaults.DEFAULT_FILTERS,
|
352
|
+
test=defaults.DEFAULT_TESTS,
|
353
|
+
)
|
354
|
+
|
355
|
+
|
356
|
+
def _get_builtin_short_description(plugin: object) -> str:
|
357
|
+
"""
|
358
|
+
Make a reasonable effort to break a function docstring down to a single sentence.
|
359
|
+
We can't use the full docstring due to embedded formatting, particularly RST.
|
360
|
+
This isn't intended to be perfect, just good enough until we can write our own docs for these.
|
361
|
+
"""
|
362
|
+
value = re.split(r'(\.|!|\s\(|:\s)', inspect.getdoc(plugin), 1)[0].replace('\n', ' ')
|
363
|
+
|
364
|
+
if value:
|
365
|
+
value += '.'
|
366
|
+
|
367
|
+
return value
|
368
|
+
|
369
|
+
|
370
|
+
def get_jinja_builtin_plugin_descriptions(plugin_type: str) -> dict[str, str]:
|
371
|
+
"""Returns a dictionary of Jinja builtin plugin names and their short descriptions."""
|
372
|
+
return {f'ansible.builtin.{name}': _get_builtin_short_description(plugin) for name, plugin in _PLUGIN_SOURCES[plugin_type].items() if name.isidentifier()}
|
@@ -43,7 +43,7 @@ _KNOWN_TYPES: t.Final[set[type]] = (
|
|
43
43
|
TemplateModule, # example: '{% import "importme.j2" as im %}{{ im | type_debug }}'
|
44
44
|
}
|
45
45
|
| set(PASS_THROUGH_SCALAR_VAR_TYPES)
|
46
|
-
| set(Marker.
|
46
|
+
| set(Marker._concrete_subclasses)
|
47
47
|
)
|
48
48
|
"""
|
49
49
|
These types are known to the templating system.
|
@@ -195,7 +195,7 @@ class _AnsibleLazyTemplateMixin:
|
|
195
195
|
Return an iterable that wraps each of the given elements in a lazy wrapper.
|
196
196
|
Only elements wrapped this way will receive lazy processing when retrieved from the collection.
|
197
197
|
"""
|
198
|
-
# DTFIX-
|
198
|
+
# DTFIX-FUTURE: check relative performance of method-local vs stored generator expressions on implementations of this method
|
199
199
|
raise NotImplementedError() # pragma: nocover
|
200
200
|
|
201
201
|
def _proxy_or_render_lazy_value(self, key: t.Any, value: t.Any) -> t.Any:
|
@@ -346,13 +346,13 @@ class _AnsibleLazyTemplateDict(_AnsibleTaggedDict, _AnsibleLazyTemplateMixin):
|
|
346
346
|
return super().__ne__(other)
|
347
347
|
|
348
348
|
def __or__(self, other):
|
349
|
-
# DTFIX-
|
349
|
+
# DTFIX-FUTURE: support preservation of laziness when possible like we do for list
|
350
350
|
# Both sides end up going through _proxy_or_render_lazy_value, so there's no Templar preservation needed.
|
351
351
|
# In the future this could be made more lazy when both Templar instances are the same, or if per-value Templar tracking was used.
|
352
352
|
return super().__or__(other)
|
353
353
|
|
354
354
|
def __ror__(self, other):
|
355
|
-
# DTFIX-
|
355
|
+
# DTFIX-FUTURE: support preservation of laziness when possible like we do for list
|
356
356
|
# Both sides end up going through _proxy_or_render_lazy_value, so there's no Templar preservation needed.
|
357
357
|
# In the future this could be made more lazy when both Templar instances are the same, or if per-value Templar tracking was used.
|
358
358
|
return super().__ror__(other)
|
@@ -549,7 +549,7 @@ class _AnsibleLazyAccessTuple(_AnsibleTaggedTuple, _AnsibleLazyTemplateMixin):
|
|
549
549
|
created as a results of managed access.
|
550
550
|
"""
|
551
551
|
|
552
|
-
#
|
552
|
+
# DTFIX5: ensure we have tests that explicitly verify this behavior
|
553
553
|
|
554
554
|
# nonempty __slots__ not supported for subtype of 'tuple'
|
555
555
|
|
@@ -0,0 +1,72 @@
|
|
1
|
+
from __future__ import annotations as _annotations
|
2
|
+
|
3
|
+
import datetime as _datetime
|
4
|
+
import os as _os
|
5
|
+
import pwd as _pwd
|
6
|
+
import time as _time
|
7
|
+
|
8
|
+
from ansible import constants as _constants
|
9
|
+
from ansible.module_utils._internal import _datatag
|
10
|
+
|
11
|
+
|
12
|
+
def generate_ansible_template_vars(
|
13
|
+
path: str,
|
14
|
+
fullpath: str | None = None,
|
15
|
+
dest_path: str | None = None,
|
16
|
+
include_ansible_managed: bool = True,
|
17
|
+
) -> dict[str, object]:
|
18
|
+
"""
|
19
|
+
Generate and return a dictionary with variable metadata about the template specified by `fullpath`.
|
20
|
+
If `fullpath` is `None`, `path` will be used instead.
|
21
|
+
"""
|
22
|
+
# deprecated description="update the ansible.windows collection to inline this logic instead of calling this internal function" core_version="2.23"
|
23
|
+
if fullpath is None:
|
24
|
+
fullpath = _os.path.abspath(path)
|
25
|
+
|
26
|
+
template_path = fullpath
|
27
|
+
template_stat = _os.stat(template_path)
|
28
|
+
|
29
|
+
template_uid: int | str
|
30
|
+
|
31
|
+
try:
|
32
|
+
template_uid = _pwd.getpwuid(template_stat.st_uid).pw_name
|
33
|
+
except KeyError:
|
34
|
+
template_uid = template_stat.st_uid
|
35
|
+
|
36
|
+
temp_vars = dict(
|
37
|
+
template_host=_os.uname()[1],
|
38
|
+
template_path=path,
|
39
|
+
template_mtime=_datetime.datetime.fromtimestamp(template_stat.st_mtime),
|
40
|
+
template_uid=template_uid,
|
41
|
+
template_run_date=_datetime.datetime.now(),
|
42
|
+
template_destpath=dest_path,
|
43
|
+
template_fullpath=fullpath,
|
44
|
+
)
|
45
|
+
|
46
|
+
if include_ansible_managed: # only inject the config default value if the variable wasn't set
|
47
|
+
temp_vars['ansible_managed'] = _generate_ansible_managed(template_stat)
|
48
|
+
|
49
|
+
return temp_vars
|
50
|
+
|
51
|
+
|
52
|
+
def _generate_ansible_managed(template_stat: _os.stat_result) -> str:
|
53
|
+
"""Generate and return the `ansible_managed` variable."""
|
54
|
+
# deprecated description="remove the `_generate_ansible_managed` function and use a constant instead" core_version="2.23"
|
55
|
+
|
56
|
+
from ansible.template import trust_as_template
|
57
|
+
|
58
|
+
managed_default = _constants.config.get_config_value('DEFAULT_MANAGED_STR')
|
59
|
+
|
60
|
+
managed_str = managed_default.format(
|
61
|
+
# IMPORTANT: These values must be constant strings to avoid template injection.
|
62
|
+
# Use Jinja template expressions where variables are needed.
|
63
|
+
host="{{ template_host }}",
|
64
|
+
uid="{{ template_uid }}",
|
65
|
+
file="{{ template_path }}",
|
66
|
+
)
|
67
|
+
|
68
|
+
ansible_managed = _time.strftime(managed_str, _time.localtime(template_stat.st_mtime))
|
69
|
+
ansible_managed = _datatag.AnsibleTagHelper.tag_copy(managed_default, ansible_managed)
|
70
|
+
ansible_managed = trust_as_template(ansible_managed)
|
71
|
+
|
72
|
+
return ansible_managed
|
@@ -5,39 +5,46 @@ from __future__ import annotations
|
|
5
5
|
import dataclasses
|
6
6
|
import typing as t
|
7
7
|
|
8
|
-
from ansible.module_utils._internal import _traceback
|
9
|
-
from ansible.module_utils.common.messages import PluginInfo, ErrorSummary, WarningSummary, DeprecationSummary
|
8
|
+
from ansible.module_utils._internal import _traceback, _event_utils, _messages
|
10
9
|
from ansible.parsing.vault import EncryptedString, VaultHelper
|
11
10
|
from ansible.utils.display import Display
|
12
11
|
|
13
12
|
from ._jinja_common import VaultExceptionMarker
|
14
|
-
from .._errors import _captured,
|
13
|
+
from .._errors import _captured, _error_factory
|
14
|
+
from .. import _event_formatting
|
15
15
|
|
16
16
|
display = Display()
|
17
17
|
|
18
18
|
|
19
|
-
def plugin_info(value: PluginInfo) -> dict[str, str]:
|
19
|
+
def plugin_info(value: _messages.PluginInfo) -> dict[str, str]:
|
20
20
|
"""Render PluginInfo as a dictionary."""
|
21
21
|
return dataclasses.asdict(value)
|
22
22
|
|
23
23
|
|
24
|
-
def
|
24
|
+
def plugin_type(value: _messages.PluginType) -> str:
|
25
|
+
"""Render PluginType as a string."""
|
26
|
+
return value.value
|
27
|
+
|
28
|
+
|
29
|
+
def error_summary(value: _messages.ErrorSummary) -> str:
|
25
30
|
"""Render ErrorSummary as a formatted traceback for backward-compatibility with pre-2.19 TaskResult.exception."""
|
26
|
-
|
31
|
+
if _traceback._is_traceback_enabled(_traceback.TracebackEvent.ERROR):
|
32
|
+
return _event_formatting.format_event_traceback(value.event)
|
33
|
+
|
34
|
+
return '(traceback unavailable)'
|
27
35
|
|
28
36
|
|
29
|
-
def warning_summary(value: WarningSummary) -> str:
|
37
|
+
def warning_summary(value: _messages.WarningSummary) -> str:
|
30
38
|
"""Render WarningSummary as a simple message string for backward-compatibility with pre-2.19 TaskResult.warnings."""
|
31
|
-
return value.
|
39
|
+
return _event_utils.format_event_brief_message(value.event)
|
32
40
|
|
33
41
|
|
34
|
-
def deprecation_summary(value: DeprecationSummary) -> dict[str, t.Any]:
|
42
|
+
def deprecation_summary(value: _messages.DeprecationSummary) -> dict[str, t.Any]:
|
35
43
|
"""Render DeprecationSummary as dict values for backward-compatibility with pre-2.19 TaskResult.deprecations."""
|
36
|
-
|
37
|
-
|
38
|
-
result.pop('details')
|
44
|
+
transformed = _event_utils.deprecation_as_dict(value)
|
45
|
+
transformed.update(deprecator=value.deprecator)
|
39
46
|
|
40
|
-
return
|
47
|
+
return transformed
|
41
48
|
|
42
49
|
|
43
50
|
def encrypted_string(value: EncryptedString) -> str | VaultExceptionMarker:
|
@@ -47,17 +54,17 @@ def encrypted_string(value: EncryptedString) -> str | VaultExceptionMarker:
|
|
47
54
|
except Exception as ex:
|
48
55
|
return VaultExceptionMarker(
|
49
56
|
ciphertext=VaultHelper.get_ciphertext(value, with_tags=True),
|
50
|
-
|
51
|
-
traceback=_traceback.maybe_extract_traceback(ex, _traceback.TracebackEvent.ERROR),
|
57
|
+
event=_error_factory.ControllerEventFactory.from_exception(ex, _traceback.is_traceback_enabled(_traceback.TracebackEvent.ERROR)),
|
52
58
|
)
|
53
59
|
|
54
60
|
|
55
61
|
_type_transform_mapping: dict[type, t.Callable[[t.Any], t.Any]] = {
|
56
62
|
_captured.CapturedErrorSummary: error_summary,
|
57
|
-
PluginInfo: plugin_info,
|
58
|
-
|
59
|
-
|
60
|
-
|
63
|
+
_messages.PluginInfo: plugin_info,
|
64
|
+
_messages.PluginType: plugin_type,
|
65
|
+
_messages.ErrorSummary: error_summary,
|
66
|
+
_messages.WarningSummary: warning_summary,
|
67
|
+
_messages.DeprecationSummary: deprecation_summary,
|
61
68
|
EncryptedString: encrypted_string,
|
62
69
|
}
|
63
70
|
"""This mapping is consulted by `Templar.template` to provide custom views of some objects."""
|
@@ -99,7 +99,7 @@ Omit = object.__new__(_OmitType)
|
|
99
99
|
_datatag._untaggable_types.add(_OmitType)
|
100
100
|
|
101
101
|
|
102
|
-
#
|
102
|
+
# DTFIX5: review these type sets to ensure they're not overly permissive/dynamic
|
103
103
|
IGNORE_SCALAR_VAR_TYPES = {value for value in _datatag._ANSIBLE_ALLOWED_SCALAR_VAR_TYPES if not issubclass(value, str)}
|
104
104
|
|
105
105
|
PASS_THROUGH_SCALAR_VAR_TYPES = _datatag._ANSIBLE_ALLOWED_SCALAR_VAR_TYPES | {
|
@@ -4,13 +4,13 @@ import abc
|
|
4
4
|
import copy
|
5
5
|
import typing as t
|
6
6
|
|
7
|
-
from yaml import Node
|
7
|
+
from yaml import Node, ScalarNode
|
8
8
|
from yaml.constructor import SafeConstructor
|
9
9
|
from yaml.resolver import BaseResolver
|
10
10
|
|
11
11
|
from ansible import constants as C
|
12
12
|
from ansible.module_utils.common.text.converters import to_text
|
13
|
-
from ansible.module_utils._internal._datatag import AnsibleTagHelper
|
13
|
+
from ansible.module_utils._internal._datatag import AnsibleTagHelper, AnsibleDatatagBase
|
14
14
|
from ansible._internal._datatag._tags import Origin, TrustedAsTemplate
|
15
15
|
from ansible.parsing.vault import EncryptedString
|
16
16
|
from ansible.utils.display import Display
|
@@ -117,13 +117,13 @@ class AnsibleInstrumentedConstructor(_BaseConstructor):
|
|
117
117
|
items = [origin.tag(item) for item in items]
|
118
118
|
yield origin.tag(items)
|
119
119
|
|
120
|
-
def construct_yaml_str(self, node):
|
120
|
+
def construct_yaml_str(self, node: ScalarNode) -> str:
|
121
121
|
# Override the default string handling function
|
122
122
|
# to always return unicode objects
|
123
123
|
# DTFIX-FUTURE: is this to_text conversion still necessary under Py3?
|
124
124
|
value = to_text(self.construct_scalar(node))
|
125
125
|
|
126
|
-
tags = [self._node_position_info(node)]
|
126
|
+
tags: list[AnsibleDatatagBase] = [self._node_position_info(node)]
|
127
127
|
|
128
128
|
if self.trusted_as_template:
|
129
129
|
# NB: since we're not context aware, this will happily add trust to dictionary keys; this is actually necessary for
|
@@ -4,8 +4,10 @@ import abc
|
|
4
4
|
import collections.abc as c
|
5
5
|
import typing as t
|
6
6
|
|
7
|
-
from yaml.
|
7
|
+
from yaml.nodes import ScalarNode, Node
|
8
8
|
|
9
|
+
from ansible._internal._templating import _jinja_common
|
10
|
+
from ansible.module_utils import _internal
|
9
11
|
from ansible.module_utils._internal._datatag import AnsibleTaggedObject, Tripwire, AnsibleTagHelper
|
10
12
|
from ansible.parsing.vault import VaultHelper
|
11
13
|
from ansible.module_utils.common.yaml import HAS_LIBYAML
|
@@ -32,30 +34,36 @@ class _BaseDumper(SafeDumper, metaclass=abc.ABCMeta):
|
|
32
34
|
class AnsibleDumper(_BaseDumper):
|
33
35
|
"""A simple stub class that allows us to add representers for our custom types."""
|
34
36
|
|
35
|
-
# DTFIX-RELEASE: need a better way to handle serialization controls during YAML dumping
|
36
|
-
def __init__(self, *args, dump_vault_tags: bool | None = None, **kwargs):
|
37
|
-
super().__init__(*args, **kwargs)
|
38
|
-
|
39
|
-
self._dump_vault_tags = dump_vault_tags
|
40
|
-
|
41
37
|
@classmethod
|
42
38
|
def _register_representers(cls) -> None:
|
43
39
|
cls.add_multi_representer(AnsibleTaggedObject, cls.represent_ansible_tagged_object)
|
44
40
|
cls.add_multi_representer(Tripwire, cls.represent_tripwire)
|
45
|
-
cls.add_multi_representer(c.Mapping,
|
46
|
-
cls.add_multi_representer(c.
|
47
|
-
|
48
|
-
def represent_ansible_tagged_object(self, data):
|
49
|
-
if self._dump_vault_tags is not False and (ciphertext := VaultHelper.get_ciphertext(data, with_tags=False)):
|
50
|
-
# deprecated: description='enable the deprecation warning below' core_version='2.23'
|
51
|
-
# if self._dump_vault_tags is None:
|
52
|
-
# Display().deprecated(
|
53
|
-
# msg="Implicit YAML dumping of vaulted value ciphertext is deprecated. Set `dump_vault_tags` to explicitly specify the desired behavior",
|
54
|
-
# version="2.27",
|
55
|
-
# )
|
41
|
+
cls.add_multi_representer(c.Mapping, cls.represent_dict)
|
42
|
+
cls.add_multi_representer(c.Collection, cls.represent_list)
|
43
|
+
cls.add_multi_representer(_jinja_common.VaultExceptionMarker, cls.represent_vault_exception_marker)
|
56
44
|
|
45
|
+
def get_node_from_ciphertext(self, data: object) -> ScalarNode | None:
|
46
|
+
if ciphertext := VaultHelper.get_ciphertext(data, with_tags=False):
|
57
47
|
return self.represent_scalar('!vault', ciphertext, style='|')
|
58
48
|
|
49
|
+
return None
|
50
|
+
|
51
|
+
def represent_vault_exception_marker(self, data: _jinja_common.VaultExceptionMarker) -> ScalarNode:
|
52
|
+
if node := self.get_node_from_ciphertext(data):
|
53
|
+
return node
|
54
|
+
|
55
|
+
data.trip()
|
56
|
+
|
57
|
+
def represent_ansible_tagged_object(self, data: AnsibleTaggedObject) -> Node:
|
58
|
+
if _internal.is_intermediate_mapping(data):
|
59
|
+
return self.represent_dict(data)
|
60
|
+
|
61
|
+
if _internal.is_intermediate_iterable(data):
|
62
|
+
return self.represent_list(data)
|
63
|
+
|
64
|
+
if node := self.get_node_from_ciphertext(data):
|
65
|
+
return node
|
66
|
+
|
59
67
|
return self.represent_data(AnsibleTagHelper.as_native_type(data)) # automatically decrypts encrypted strings
|
60
68
|
|
61
69
|
def represent_tripwire(self, data: Tripwire) -> t.NoReturn:
|
@@ -7,7 +7,7 @@ import typing as t
|
|
7
7
|
from yaml import MarkedYAMLError
|
8
8
|
from yaml.constructor import ConstructorError
|
9
9
|
|
10
|
-
from ansible._internal._errors import
|
10
|
+
from ansible._internal._errors import _error_utils
|
11
11
|
from ansible.errors import AnsibleParserError
|
12
12
|
from ansible._internal._datatag._tags import Origin
|
13
13
|
|
@@ -34,7 +34,7 @@ class AnsibleYAMLParserError(AnsibleParserError):
|
|
34
34
|
if isinstance(exception, MarkedYAMLError):
|
35
35
|
origin = origin.replace(line_num=exception.problem_mark.line + 1, col_num=exception.problem_mark.column + 1)
|
36
36
|
|
37
|
-
source_context =
|
37
|
+
source_context = _error_utils.SourceContext.from_origin(origin)
|
38
38
|
|
39
39
|
target_line = source_context.target_line or '' # for these cases, we don't need to distinguish between None and empty string
|
40
40
|
|
@@ -66,12 +66,12 @@ class AnsibleYAMLParserError(AnsibleParserError):
|
|
66
66
|
# There may be cases where there is a valid tab in a line that has other errors.
|
67
67
|
# That's OK, users should "fix" their tab usage anyway -- at which point later error handling logic will hopefully find the real issue.
|
68
68
|
elif (tab_idx := target_line.find('\t')) >= 0:
|
69
|
-
source_context =
|
69
|
+
source_context = _error_utils.SourceContext.from_origin(origin.replace(col_num=tab_idx + 1))
|
70
70
|
message = "Tabs are usually invalid in YAML."
|
71
71
|
|
72
72
|
# Check for unquoted templates.
|
73
73
|
elif match := re.search(r'^\s*(?:-\s+)*(?:[\w\s]+:\s+)?(?P<value>\{\{.*}})', target_line):
|
74
|
-
source_context =
|
74
|
+
source_context = _error_utils.SourceContext.from_origin(origin.replace(col_num=match.start('value') + 1))
|
75
75
|
message = 'This may be an issue with missing quotes around a template block.'
|
76
76
|
# FIXME: Use the captured value to show the actual fix required.
|
77
77
|
help_text = """
|
@@ -95,7 +95,7 @@ Should be:
|
|
95
95
|
# look for an unquoted colon in the value
|
96
96
|
and (colon_match := re.search(r':($| )', target_fragment))
|
97
97
|
):
|
98
|
-
source_context =
|
98
|
+
source_context = _error_utils.SourceContext.from_origin(origin.replace(col_num=value_match.start('value') + colon_match.start() + 1))
|
99
99
|
message = 'Colons in unquoted values must be followed by a non-space character.'
|
100
100
|
# FIXME: Use the captured value to show the actual fix required.
|
101
101
|
help_text = """
|
@@ -114,7 +114,7 @@ Should be:
|
|
114
114
|
first, last = suspected_value[0], suspected_value[-1]
|
115
115
|
|
116
116
|
if first != last: # "foo" in bar
|
117
|
-
source_context =
|
117
|
+
source_context = _error_utils.SourceContext.from_origin(origin.replace(col_num=match.start('value') + 1))
|
118
118
|
message = 'Values starting with a quote must end with the same quote.'
|
119
119
|
# FIXME: Use the captured value to show the actual fix required, and use that same logic to improve the origin further.
|
120
120
|
help_text = """
|
@@ -127,7 +127,7 @@ Should be:
|
|
127
127
|
raw: '"foo" in bar'
|
128
128
|
"""
|
129
129
|
elif first == last and target_line.count(first) > 2: # "foo" and "bar"
|
130
|
-
source_context =
|
130
|
+
source_context = _error_utils.SourceContext.from_origin(origin.replace(col_num=match.start('value') + 1))
|
131
131
|
message = 'Values starting with a quote must end with the same quote, and not contain that quote.'
|
132
132
|
# FIXME: Use the captured value to show the actual fix required, and use that same logic to improve the origin further.
|
133
133
|
help_text = """
|
@@ -12,7 +12,7 @@ from ansible.errors import AnsibleError
|
|
12
12
|
|
13
13
|
def unmask(value: object, type_names: str | list[str]) -> object:
|
14
14
|
"""
|
15
|
-
Internal filter to suppress automatic type transformation in Jinja (e.g.,
|
15
|
+
Internal filter to suppress automatic type transformation in Jinja (e.g., WarningSummary, DeprecationSummary, ErrorSummary).
|
16
16
|
Lazy collection caching is in play - the first attempt to access a value in a given lazy container must be with unmasking in place, or the transformed value
|
17
17
|
will already be cached.
|
18
18
|
"""
|
ansible/cli/__init__.py
CHANGED
@@ -7,7 +7,6 @@ from __future__ import annotations
|
|
7
7
|
|
8
8
|
import locale
|
9
9
|
import os
|
10
|
-
import signal
|
11
10
|
import sys
|
12
11
|
|
13
12
|
# We overload the ``ansible`` adhoc command to provide the functionality for
|
@@ -76,8 +75,6 @@ def initialize_locale():
|
|
76
75
|
initialize_locale()
|
77
76
|
|
78
77
|
|
79
|
-
import atexit
|
80
|
-
import errno
|
81
78
|
import getpass
|
82
79
|
import subprocess
|
83
80
|
import traceback
|
@@ -96,7 +93,7 @@ try:
|
|
96
93
|
display = Display()
|
97
94
|
except Exception as ex:
|
98
95
|
if isinstance(ex, AnsibleError):
|
99
|
-
ex_msg = ' '.join((ex.message, ex._help_text)).strip()
|
96
|
+
ex_msg = ' '.join((ex.message, ex._help_text or '')).strip()
|
100
97
|
else:
|
101
98
|
ex_msg = str(ex)
|
102
99
|
|
@@ -112,17 +109,17 @@ from ansible.module_utils.six import string_types
|
|
112
109
|
from ansible.module_utils.common.text.converters import to_bytes, to_text
|
113
110
|
from ansible.module_utils.common.collections import is_sequence
|
114
111
|
from ansible.module_utils.common.file import is_executable
|
115
|
-
from ansible.module_utils.common.process import get_bin_path
|
116
112
|
from ansible.parsing.dataloader import DataLoader
|
117
113
|
from ansible.parsing.vault import PromptVaultSecret, get_file_vault_secret, VaultSecretsContext
|
118
114
|
from ansible.plugins.loader import add_all_plugin_dirs, init_plugin_loader
|
119
115
|
from ansible.release import __version__
|
120
|
-
from ansible.utils._ssh_agent import SshAgentClient
|
121
116
|
from ansible.utils.collection_loader import AnsibleCollectionConfig
|
122
117
|
from ansible.utils.collection_loader._collection_finder import _get_collection_name_from_path
|
123
118
|
from ansible.utils.path import unfrackpath
|
124
119
|
from ansible.vars.manager import VariableManager
|
125
120
|
from ansible.module_utils._internal import _deprecator
|
121
|
+
from ansible._internal._ssh import _agent_launch
|
122
|
+
|
126
123
|
|
127
124
|
try:
|
128
125
|
import argcomplete
|
@@ -131,77 +128,6 @@ except ImportError:
|
|
131
128
|
HAS_ARGCOMPLETE = False
|
132
129
|
|
133
130
|
|
134
|
-
_SSH_AGENT_STDOUT_READ_TIMEOUT = 5 # seconds
|
135
|
-
|
136
|
-
|
137
|
-
def _ssh_agent_timeout_handler(signum, frame):
|
138
|
-
raise TimeoutError
|
139
|
-
|
140
|
-
|
141
|
-
def _launch_ssh_agent() -> None:
|
142
|
-
ssh_agent_cfg = C.config.get_config_value('SSH_AGENT')
|
143
|
-
match ssh_agent_cfg:
|
144
|
-
case 'none':
|
145
|
-
display.debug('SSH_AGENT set to none')
|
146
|
-
return
|
147
|
-
case 'auto':
|
148
|
-
try:
|
149
|
-
ssh_agent_bin = get_bin_path('ssh-agent')
|
150
|
-
except ValueError as e:
|
151
|
-
raise AnsibleError('SSH_AGENT set to auto, but cannot find ssh-agent binary') from e
|
152
|
-
ssh_agent_dir = os.path.join(C.DEFAULT_LOCAL_TMP, 'ssh_agent')
|
153
|
-
os.mkdir(ssh_agent_dir, 0o700)
|
154
|
-
sock = os.path.join(ssh_agent_dir, 'agent.sock')
|
155
|
-
display.vvv('SSH_AGENT: starting...')
|
156
|
-
try:
|
157
|
-
p = subprocess.Popen(
|
158
|
-
[ssh_agent_bin, '-D', '-s', '-a', sock],
|
159
|
-
stdin=subprocess.PIPE,
|
160
|
-
stdout=subprocess.PIPE,
|
161
|
-
stderr=subprocess.PIPE,
|
162
|
-
)
|
163
|
-
except OSError as e:
|
164
|
-
raise AnsibleError(
|
165
|
-
f'Could not start ssh-agent: {e}'
|
166
|
-
) from e
|
167
|
-
|
168
|
-
if p.poll() is not None:
|
169
|
-
raise AnsibleError(
|
170
|
-
f'Could not start ssh-agent: rc={p.returncode} stderr="{p.stderr.read().decode()}"'
|
171
|
-
)
|
172
|
-
|
173
|
-
old_sigalrm_handler = signal.signal(signal.SIGALRM, _ssh_agent_timeout_handler)
|
174
|
-
signal.alarm(_SSH_AGENT_STDOUT_READ_TIMEOUT)
|
175
|
-
try:
|
176
|
-
stdout = p.stdout.read(13)
|
177
|
-
except TimeoutError:
|
178
|
-
stdout = b''
|
179
|
-
finally:
|
180
|
-
signal.alarm(0)
|
181
|
-
signal.signal(signal.SIGALRM, old_sigalrm_handler)
|
182
|
-
|
183
|
-
if stdout != b'SSH_AUTH_SOCK':
|
184
|
-
display.warning(
|
185
|
-
f'The first 13 characters of stdout did not match the '
|
186
|
-
f'expected SSH_AUTH_SOCK. This may not be the right binary, '
|
187
|
-
f'or an incompatible agent: {stdout.decode()}'
|
188
|
-
)
|
189
|
-
display.vvv(f'SSH_AGENT: ssh-agent[{p.pid}] started and bound to {sock}')
|
190
|
-
atexit.register(p.terminate)
|
191
|
-
case _:
|
192
|
-
sock = ssh_agent_cfg
|
193
|
-
|
194
|
-
try:
|
195
|
-
with SshAgentClient(sock) as client:
|
196
|
-
client.list()
|
197
|
-
except Exception as e:
|
198
|
-
raise AnsibleError(
|
199
|
-
f'Could not communicate with ssh-agent using auth sock {sock}: {e}'
|
200
|
-
) from e
|
201
|
-
|
202
|
-
os.environ['SSH_AUTH_SOCK'] = os.environ['ANSIBLE_SSH_AGENT'] = sock
|
203
|
-
|
204
|
-
|
205
131
|
class CLI(ABC):
|
206
132
|
""" code behind bin/ansible* programs """
|
207
133
|
|
@@ -599,9 +525,7 @@ class CLI(ABC):
|
|
599
525
|
try:
|
600
526
|
cmd = subprocess.Popen(CLI.PAGER, shell=True, stdin=subprocess.PIPE, stdout=sys.stdout)
|
601
527
|
cmd.communicate(input=to_bytes(text))
|
602
|
-
except
|
603
|
-
pass
|
604
|
-
except KeyboardInterrupt:
|
528
|
+
except (OSError, KeyboardInterrupt):
|
605
529
|
pass
|
606
530
|
|
607
531
|
def _play_prereqs(self):
|
@@ -636,10 +560,7 @@ class CLI(ABC):
|
|
636
560
|
loader.set_vault_secrets(vault_secrets)
|
637
561
|
|
638
562
|
if self.USES_CONNECTION:
|
639
|
-
|
640
|
-
_launch_ssh_agent()
|
641
|
-
except Exception as e:
|
642
|
-
raise AnsibleError('Failed to launch ssh agent', orig_exc=e)
|
563
|
+
_agent_launch.launch_ssh_agent()
|
643
564
|
|
644
565
|
# create the inventory, and filter it based on the subset specified (if any)
|
645
566
|
inventory = InventoryManager(loader=loader, sources=options['inventory'], cache=(not options.get('flush_cache')))
|
@@ -709,8 +630,8 @@ class CLI(ABC):
|
|
709
630
|
try:
|
710
631
|
with open(b_pwd_file, "rb") as password_file:
|
711
632
|
secret = password_file.read().strip()
|
712
|
-
except
|
713
|
-
raise AnsibleError("Could not read password file
|
633
|
+
except OSError as ex:
|
634
|
+
raise AnsibleError(f"Could not read password file {pwd_file!r}.") from ex
|
714
635
|
|
715
636
|
secret = secret.strip(b'\r\n')
|
716
637
|
|
@@ -729,12 +650,9 @@ class CLI(ABC):
|
|
729
650
|
|
730
651
|
ansible_dir = Path(C.ANSIBLE_HOME).expanduser()
|
731
652
|
try:
|
732
|
-
ansible_dir.mkdir(mode=0o700)
|
733
|
-
except OSError as
|
734
|
-
|
735
|
-
display.warning(
|
736
|
-
"Failed to create the directory '%s': %s" % (ansible_dir, to_text(exc, errors='surrogate_or_replace'))
|
737
|
-
)
|
653
|
+
ansible_dir.mkdir(mode=0o700, exist_ok=True)
|
654
|
+
except OSError as ex:
|
655
|
+
display.error_as_warning(f"Failed to create the directory {ansible_dir!r}.", ex)
|
738
656
|
else:
|
739
657
|
display.debug("Created the '%s' directory" % ansible_dir)
|
740
658
|
|
@@ -750,7 +668,7 @@ class CLI(ABC):
|
|
750
668
|
try:
|
751
669
|
raise AnsibleError("Unexpected Exception, this is probably a bug.") from ex
|
752
670
|
except AnsibleError as ex2:
|
753
|
-
# DTFIX-
|
671
|
+
# DTFIX-FUTURE: clean this up so we're not hacking the internals- re-wrap in an AnsibleCLIUnhandledError that always shows TB, or?
|
754
672
|
from ansible.module_utils._internal import _traceback
|
755
673
|
_traceback._is_traceback_enabled = lambda *_args, **_kwargs: True
|
756
674
|
display.error(ex2)
|