ansible-core 2.19.0b3__py3-none-any.whl → 2.19.0b5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ansible/_internal/__init__.py +2 -2
- 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 +6 -6
- 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 +21 -16
- ansible/_internal/_templating/_jinja_common.py +18 -27
- ansible/_internal/_templating/_jinja_plugins.py +31 -3
- ansible/_internal/_templating/_lazy_containers.py +5 -5
- ansible/_internal/_templating/_transform.py +20 -19
- ansible/_internal/_templating/_utils.py +1 -1
- ansible/_internal/_testing.py +26 -0
- ansible/_internal/_yaml/_dumper.py +1 -1
- 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 +5 -82
- ansible/cli/arguments/option_helpers.py +8 -5
- ansible/cli/doc.py +84 -28
- ansible/cli/inventory.py +1 -1
- ansible/compat/importlib_resources.py +9 -12
- ansible/config/base.yml +27 -23
- ansible/config/manager.py +142 -101
- ansible/constants.py +1 -1
- ansible/errors/__init__.py +96 -49
- ansible/executor/module_common.py +8 -10
- 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 +38 -113
- ansible/executor/task_executor.py +26 -61
- ansible/executor/task_result.py +2 -4
- ansible/galaxy/collection/__init__.py +1 -4
- ansible/inventory/manager.py +1 -0
- ansible/module_utils/_internal/__init__.py +0 -3
- ansible/module_utils/_internal/_ambient_context.py +3 -3
- ansible/module_utils/_internal/_ansiballz.py +4 -2
- ansible/module_utils/_internal/_datatag/__init__.py +20 -14
- ansible/module_utils/_internal/_datatag/_tags.py +2 -2
- ansible/module_utils/_internal/_deprecator.py +66 -48
- 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 +21 -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} +28 -47
- ansible/module_utils/_internal/_patches/_dataclass_annotation_patch.py +1 -3
- ansible/module_utils/_internal/_plugin_info.py +1 -1
- 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 +49 -15
- 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/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/datatag.py +5 -2
- ansible/module_utils/facts/system/distribution.py +16 -3
- ansible/module_utils/facts/virtual/linux.py +2 -2
- ansible/module_utils/parsing/convert_bool.py +6 -0
- ansible/module_utils/service.py +2 -9
- ansible/modules/apt_repository.py +7 -29
- ansible/modules/assemble.py +4 -4
- ansible/modules/async_status.py +13 -11
- ansible/modules/async_wrapper.py +5 -5
- ansible/modules/cron.py +3 -5
- ansible/modules/dnf5.py +15 -22
- ansible/modules/git.py +1 -6
- ansible/modules/hostname.py +0 -1
- ansible/modules/pip.py +2 -4
- ansible/modules/service.py +3 -9
- ansible/modules/sysvinit.py +3 -3
- ansible/parsing/ajson.py +3 -5
- ansible/parsing/dataloader.py +4 -4
- 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 +4 -4
- ansible/playbook/playbook_include.py +1 -1
- ansible/playbook/taggable.py +0 -3
- ansible/plugins/__init__.py +0 -25
- ansible/plugins/action/__init__.py +9 -32
- 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/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 +6 -8
- ansible/plugins/action/unarchive.py +5 -15
- ansible/plugins/action/uri.py +9 -20
- ansible/plugins/callback/__init__.py +4 -6
- ansible/plugins/callback/junit.py +4 -2
- ansible/plugins/connection/local.py +2 -2
- ansible/plugins/connection/ssh.py +17 -9
- ansible/plugins/connection/winrm.py +5 -2
- ansible/plugins/doc_fragments/constructed.py +2 -2
- ansible/plugins/filter/core.py +13 -6
- ansible/plugins/filter/encryption.py +4 -4
- ansible/plugins/inventory/__init__.py +11 -10
- ansible/plugins/inventory/script.py +1 -1
- ansible/plugins/list.py +69 -16
- ansible/plugins/loader.py +10 -9
- ansible/plugins/lookup/csvfile.py +16 -71
- ansible/plugins/lookup/first_found.py +2 -1
- ansible/plugins/shell/__init__.py +56 -2
- ansible/plugins/shell/powershell.py +66 -9
- ansible/plugins/shell/sh.py +9 -5
- ansible/plugins/test/core.py +21 -15
- ansible/plugins/test/finished.yml +1 -1
- ansible/plugins/test/uri.py +2 -5
- ansible/release.py +1 -1
- ansible/template/__init__.py +30 -2
- ansible/utils/collection_loader/__init__.py +2 -0
- ansible/utils/display.py +107 -128
- ansible/utils/hashing.py +0 -1
- ansible/utils/listify.py +6 -4
- ansible/utils/plugin_docs.py +2 -1
- ansible/utils/unsafe_proxy.py +1 -1
- ansible/vars/hostvars.py +1 -1
- {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.dist-info}/METADATA +3 -2
- {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.dist-info}/RECORD +173 -161
- {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.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/util.py +20 -0
- 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 +73 -8
- ansible_test/_util/target/setup/bootstrap.sh +31 -0
- ansible/_internal/_errors/_utils.py +0 -310
- {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.dist-info}/entry_points.txt +0 -0
- {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.dist-info/licenses}/COPYING +0 -0
- {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.dist-info/licenses/licenses}/Apache-License.txt +0 -0
- {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.dist-info/licenses/licenses}/BSD-3-Clause.txt +0 -0
- {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.dist-info/licenses/licenses}/MIT-license.txt +0 -0
- {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.dist-info/licenses/licenses}/PSF-license.txt +0 -0
- {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.dist-info/licenses/licenses}/simplified_bsd.txt +0 -0
- {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.dist-info}/top_level.txt +0 -0
@@ -103,76 +103,26 @@ RETURN = """
|
|
103
103
|
elements: str
|
104
104
|
"""
|
105
105
|
|
106
|
-
import codecs
|
107
106
|
import csv
|
108
107
|
|
109
108
|
from collections.abc import MutableSequence
|
110
109
|
|
111
|
-
from ansible.errors import AnsibleError
|
110
|
+
from ansible.errors import AnsibleError
|
112
111
|
from ansible.parsing.splitter import parse_kv
|
113
112
|
from ansible.plugins.lookup import LookupBase
|
114
|
-
from ansible.module_utils.six import PY2
|
115
|
-
from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
|
116
|
-
|
117
|
-
|
118
|
-
class CSVRecoder:
|
119
|
-
"""
|
120
|
-
Iterator that reads an encoded stream and encodes the input to UTF-8
|
121
|
-
"""
|
122
|
-
def __init__(self, f, encoding='utf-8'):
|
123
|
-
self.reader = codecs.getreader(encoding)(f)
|
124
|
-
|
125
|
-
def __iter__(self):
|
126
|
-
return self
|
127
|
-
|
128
|
-
def __next__(self):
|
129
|
-
return next(self.reader).encode("utf-8")
|
130
|
-
|
131
|
-
next = __next__ # For Python 2
|
132
|
-
|
133
|
-
|
134
|
-
class CSVReader:
|
135
|
-
"""
|
136
|
-
A CSV reader which will iterate over lines in the CSV file "f",
|
137
|
-
which is encoded in the given encoding.
|
138
|
-
"""
|
139
|
-
|
140
|
-
def __init__(self, f, dialect=csv.excel, encoding='utf-8', **kwds):
|
141
|
-
if PY2:
|
142
|
-
f = CSVRecoder(f, encoding)
|
143
|
-
else:
|
144
|
-
f = codecs.getreader(encoding)(f)
|
145
|
-
|
146
|
-
self.reader = csv.reader(f, dialect=dialect, **kwds)
|
147
|
-
|
148
|
-
def __next__(self):
|
149
|
-
row = next(self.reader)
|
150
|
-
return [to_text(s) for s in row]
|
151
|
-
|
152
|
-
next = __next__ # For Python 2
|
153
|
-
|
154
|
-
def __iter__(self):
|
155
|
-
return self
|
156
113
|
|
157
114
|
|
158
115
|
class LookupModule(LookupBase):
|
159
116
|
|
160
117
|
def read_csv(self, filename, key, delimiter, encoding='utf-8', dflt=None, col=1, keycol=0):
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
creader = CSVReader(f, delimiter=to_native(delimiter), encoding=encoding)
|
165
|
-
|
166
|
-
for row in creader:
|
167
|
-
if len(row) and row[keycol] == key:
|
118
|
+
with open(filename, encoding=encoding) as f:
|
119
|
+
for row in csv.reader(f, dialect=csv.excel, delimiter=delimiter):
|
120
|
+
if row and row[keycol] == key:
|
168
121
|
return row[col]
|
169
|
-
except Exception as e:
|
170
|
-
raise AnsibleError("csvfile: %s" % to_native(e))
|
171
122
|
|
172
123
|
return dflt
|
173
124
|
|
174
125
|
def run(self, terms, variables=None, **kwargs):
|
175
|
-
|
176
126
|
ret = []
|
177
127
|
|
178
128
|
self.set_options(var_options=variables, direct=kwargs)
|
@@ -192,23 +142,19 @@ class LookupModule(LookupBase):
|
|
192
142
|
key = kv['_raw_params']
|
193
143
|
|
194
144
|
# parameters override per term using k/v
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
raise AnsibleAssertionError('%s is not a valid option' % name)
|
202
|
-
|
203
|
-
self._deprecate_inline_kv()
|
204
|
-
self.set_option(name, value)
|
205
|
-
reset_params = True
|
145
|
+
reset_params = False
|
146
|
+
for name, value in kv.items():
|
147
|
+
if name == '_raw_params':
|
148
|
+
continue
|
149
|
+
if name not in paramvals:
|
150
|
+
raise ValueError(f'{name!r} is not a valid option')
|
206
151
|
|
207
|
-
|
208
|
-
|
152
|
+
self._deprecate_inline_kv()
|
153
|
+
self.set_option(name, value)
|
154
|
+
reset_params = True
|
209
155
|
|
210
|
-
|
211
|
-
|
156
|
+
if reset_params:
|
157
|
+
paramvals = self.get_options()
|
212
158
|
|
213
159
|
# default is just placeholder for real tab
|
214
160
|
if paramvals['delimiter'] == 'TAB':
|
@@ -218,8 +164,7 @@ class LookupModule(LookupBase):
|
|
218
164
|
var = self.read_csv(lookupfile, key, paramvals['delimiter'], paramvals['encoding'], paramvals['default'], paramvals['col'], paramvals['keycol'])
|
219
165
|
if var is not None:
|
220
166
|
if isinstance(var, MutableSequence):
|
221
|
-
|
222
|
-
ret.append(v)
|
167
|
+
ret.extend(var)
|
223
168
|
else:
|
224
169
|
ret.append(var)
|
225
170
|
|
@@ -149,6 +149,7 @@ from ansible.errors import AnsibleError
|
|
149
149
|
from ansible.plugins.lookup import LookupBase
|
150
150
|
from ansible._internal._templating import _jinja_common
|
151
151
|
from ansible._internal._templating import _jinja_plugins
|
152
|
+
from ansible import template as _template
|
152
153
|
from ansible.utils.path import unfrackpath
|
153
154
|
from ansible.utils.display import Display
|
154
155
|
from ansible.module_utils.datatag import native_type_name
|
@@ -222,7 +223,7 @@ class LookupModule(LookupBase):
|
|
222
223
|
return total_search
|
223
224
|
|
224
225
|
def run(self, terms: list, variables=None, **kwargs):
|
225
|
-
if (first_marker :=
|
226
|
+
if (first_marker := _template.get_first_marker_arg((), kwargs)) is not None:
|
226
227
|
first_marker.trip()
|
227
228
|
|
228
229
|
if _jinja_plugins._LookupContext.current().invoked_as_with:
|
@@ -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,10 @@ class ShellModule(ShellBase):
|
|
271
324
|
return self._encode_script(script)
|
272
325
|
|
273
326
|
def checksum(self, path, *args, **kwargs):
|
327
|
+
display.deprecated(
|
328
|
+
"The 'ShellModule.checksum' method is deprecated. Use 'ActionBase._execute_remote_stat()' instead.",
|
329
|
+
version="2.23"
|
330
|
+
)
|
274
331
|
path = self._escape(self._unquote(path))
|
275
332
|
script = """
|
276
333
|
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,10 @@ class ShellModule(ShellBase):
|
|
43
47
|
_SHELL_GROUP_RIGHT = ')'
|
44
48
|
|
45
49
|
def checksum(self, path, python_interp):
|
50
|
+
display.deprecated(
|
51
|
+
"The 'ShellModule.checksum' method is deprecated. Use 'ActionBase._execute_remote_stat()' instead.",
|
52
|
+
version="2.23"
|
53
|
+
)
|
46
54
|
# In the following test, each condition is a check and logical
|
47
55
|
# comparison (|| or &&) that sets the rc value. Every check is run so
|
48
56
|
# the last check in the series to fail will be the rc that is returned.
|
@@ -67,11 +75,7 @@ class ShellModule(ShellBase):
|
|
67
75
|
# "one-liner".
|
68
76
|
shell_escaped_path = self.quote(path)
|
69
77
|
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
|
-
]
|
78
|
+
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
79
|
|
75
|
-
cmd = (" %s " % self._SHELL_OR).join(csums)
|
76
80
|
cmd = "%s; %s %s (echo \'0 \'%s)" % (test, cmd, self._SHELL_OR, shell_escaped_path)
|
77
81
|
return cmd
|
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")
|
@@ -170,7 +176,7 @@ def vaulted_file(value):
|
|
170
176
|
with open(to_bytes(value), 'rb') as f:
|
171
177
|
return is_encrypted_file(f)
|
172
178
|
except (OSError, IOError) as e:
|
173
|
-
raise errors.AnsibleFilterError(f"Cannot test if the file {value} is a vault"
|
179
|
+
raise errors.AnsibleFilterError(f"Cannot test if the file {value} is a vault.") from e
|
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
@@ -98,7 +98,7 @@ class Templar:
|
|
98
98
|
@property
|
99
99
|
def basedir(self) -> str:
|
100
100
|
"""The basedir from DataLoader."""
|
101
|
-
# DTFIX-
|
101
|
+
# DTFIX-FUTURE: come up with a better way to handle this so it can be deprecated
|
102
102
|
return self._engine.basedir
|
103
103
|
|
104
104
|
@property
|
@@ -381,7 +381,7 @@ def generate_ansible_template_vars(path: str, fullpath: str | None = None, dest_
|
|
381
381
|
)
|
382
382
|
|
383
383
|
ansible_managed = _time.strftime(managed_str, _time.localtime(template_stat.st_mtime))
|
384
|
-
#
|
384
|
+
# DTFIX7: this should not be tag_copy, it should either be an origin copy or some kind of derived origin
|
385
385
|
ansible_managed = _datatag.AnsibleTagHelper.tag_copy(managed_default, ansible_managed)
|
386
386
|
ansible_managed = trust_as_template(ansible_managed)
|
387
387
|
ansible_managed = _module_utils_datatag.deprecate_value(
|
@@ -427,3 +427,31 @@ def is_trusted_as_template(value: object) -> bool:
|
|
427
427
|
This function should not be needed for production code, but may be useful in unit tests.
|
428
428
|
"""
|
429
429
|
return isinstance(value, _TRUSTABLE_TYPES) and _tags.TrustedAsTemplate.is_tagged_on(value)
|
430
|
+
|
431
|
+
|
432
|
+
_TCallable = _t.TypeVar('_TCallable', bound=_t.Callable)
|
433
|
+
|
434
|
+
|
435
|
+
def accept_args_markers(plugin: _TCallable) -> _TCallable:
|
436
|
+
"""
|
437
|
+
A decorator to mark a Jinja plugin as capable of handling `Marker` values for its top-level arguments.
|
438
|
+
Non-decorated plugin invocation is skipped when a top-level argument is a `Marker`, with the first such value substituted as the plugin result.
|
439
|
+
This ensures that only plugins which understand `Marker` instances for top-level arguments will encounter them.
|
440
|
+
"""
|
441
|
+
plugin.accept_args_markers = True
|
442
|
+
|
443
|
+
return plugin
|
444
|
+
|
445
|
+
|
446
|
+
def accept_lazy_markers(plugin: _TCallable) -> _TCallable:
|
447
|
+
"""
|
448
|
+
A decorator to mark a Jinja plugin as capable of handling `Marker` values retrieved from lazy containers.
|
449
|
+
Non-decorated plugins will trigger a `MarkerError` exception when attempting to retrieve a `Marker` from a lazy container.
|
450
|
+
This ensures that only plugins which understand lazy retrieval of `Marker` instances will encounter them.
|
451
|
+
"""
|
452
|
+
plugin.accept_lazy_markers = True
|
453
|
+
|
454
|
+
return plugin
|
455
|
+
|
456
|
+
|
457
|
+
get_first_marker_arg = _jinja_common.get_first_marker_arg
|
@@ -13,6 +13,8 @@ import typing as t
|
|
13
13
|
class _EncryptedStringProtocol(t.Protocol):
|
14
14
|
"""Protocol representing an `EncryptedString`, since it cannot be imported here."""
|
15
15
|
|
16
|
+
# DTFIX-FUTURE: collapse this with the one in config, once we can
|
17
|
+
|
16
18
|
def _decrypt(self) -> str: ...
|
17
19
|
|
18
20
|
|