ansible-core 2.19.2__py3-none-any.whl → 2.20.0b1__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.
Potentially problematic release.
This version of ansible-core might be problematic. Click here for more details.
- ansible/_internal/__init__.py +1 -4
- ansible/_internal/_ansiballz/_builder.py +1 -3
- ansible/_internal/_collection_proxy.py +7 -9
- ansible/_internal/_display_utils.py +145 -0
- ansible/_internal/_json/__init__.py +3 -4
- ansible/_internal/_templating/_engine.py +1 -1
- ansible/_internal/_templating/_jinja_plugins.py +1 -2
- ansible/_internal/_wrapt.py +105 -301
- ansible/cli/__init__.py +11 -10
- ansible/cli/adhoc.py +1 -2
- ansible/cli/arguments/option_helpers.py +1 -1
- ansible/cli/config.py +5 -6
- ansible/cli/doc.py +67 -67
- ansible/cli/galaxy.py +15 -24
- ansible/cli/inventory.py +0 -1
- ansible/cli/playbook.py +0 -1
- ansible/cli/pull.py +0 -1
- ansible/cli/scripts/ansible_connection_cli_stub.py +1 -1
- ansible/config/base.yml +1 -25
- ansible/config/manager.py +0 -2
- ansible/executor/play_iterator.py +42 -20
- ansible/executor/playbook_executor.py +0 -9
- ansible/executor/powershell/async_watchdog.ps1 +24 -4
- ansible/executor/task_executor.py +32 -22
- ansible/executor/task_queue_manager.py +1 -3
- ansible/galaxy/api.py +33 -80
- ansible/galaxy/collection/__init__.py +4 -17
- ansible/galaxy/dependency_resolution/dataclasses.py +0 -10
- ansible/galaxy/dependency_resolution/providers.py +1 -2
- ansible/galaxy/role.py +1 -33
- ansible/inventory/manager.py +2 -3
- ansible/keyword_desc.yml +0 -3
- ansible/module_utils/_internal/_datatag/__init__.py +2 -10
- ansible/module_utils/_internal/_no_six.py +86 -0
- ansible/module_utils/_text.py +28 -8
- ansible/module_utils/ansible_release.py +2 -2
- ansible/module_utils/basic.py +27 -24
- ansible/module_utils/common/_collections_compat.py +11 -2
- ansible/module_utils/common/collections.py +8 -3
- ansible/module_utils/common/dict_transformations.py +1 -2
- ansible/module_utils/common/network.py +4 -2
- ansible/module_utils/common/parameters.py +32 -41
- ansible/module_utils/common/text/converters.py +109 -23
- ansible/module_utils/common/text/formatters.py +6 -2
- ansible/module_utils/common/validation.py +11 -9
- ansible/module_utils/connection.py +8 -3
- ansible/module_utils/facts/hardware/linux.py +23 -7
- ansible/module_utils/facts/hardware/netbsd.py +1 -1
- ansible/module_utils/facts/hardware/sunos.py +2 -1
- ansible/module_utils/facts/packages.py +6 -2
- ansible/module_utils/facts/system/distribution.py +2 -1
- ansible/module_utils/facts/system/env.py +6 -3
- ansible/module_utils/facts/system/local.py +3 -1
- ansible/module_utils/parsing/convert_bool.py +6 -2
- ansible/module_utils/service.py +2 -3
- ansible/module_utils/six/__init__.py +11 -6
- ansible/module_utils/urls.py +6 -2
- ansible/module_utils/yumdnf.py +0 -5
- ansible/modules/apt.py +18 -13
- ansible/modules/apt_repository.py +1 -1
- ansible/modules/assemble.py +5 -9
- ansible/modules/blockinfile.py +39 -23
- ansible/modules/cron.py +26 -35
- ansible/modules/deb822_repository.py +83 -12
- ansible/modules/dnf.py +3 -7
- ansible/modules/dnf5.py +4 -6
- ansible/modules/expect.py +0 -3
- ansible/modules/find.py +1 -2
- ansible/modules/get_url.py +1 -1
- ansible/modules/git.py +4 -5
- ansible/modules/include_vars.py +1 -1
- ansible/modules/lineinfile.py +71 -63
- ansible/modules/package_facts.py +1 -1
- ansible/modules/pip.py +8 -2
- ansible/modules/replace.py +6 -6
- ansible/modules/service.py +3 -4
- ansible/modules/stat.py +20 -0
- ansible/modules/uri.py +9 -10
- ansible/modules/user.py +1 -2
- ansible/modules/wait_for.py +2 -2
- ansible/modules/wait_for_connection.py +2 -1
- ansible/modules/yum_repository.py +1 -16
- ansible/parsing/dataloader.py +24 -31
- ansible/parsing/mod_args.py +3 -0
- ansible/parsing/vault/__init__.py +1 -2
- ansible/playbook/base.py +8 -56
- ansible/playbook/block.py +0 -60
- ansible/playbook/collectionsearch.py +1 -2
- ansible/playbook/handler.py +1 -7
- ansible/playbook/helpers.py +0 -7
- ansible/playbook/included_file.py +1 -1
- ansible/playbook/play.py +103 -37
- ansible/playbook/play_context.py +4 -0
- ansible/playbook/role/__init__.py +10 -65
- ansible/playbook/role/definition.py +3 -4
- ansible/playbook/role/include.py +2 -3
- ansible/playbook/role/metadata.py +1 -12
- ansible/playbook/role/requirement.py +1 -2
- ansible/playbook/role_include.py +1 -2
- ansible/playbook/taggable.py +16 -5
- ansible/playbook/task.py +51 -55
- ansible/plugins/action/__init__.py +20 -19
- ansible/plugins/action/add_host.py +1 -2
- ansible/plugins/action/fetch.py +2 -4
- ansible/plugins/action/group_by.py +1 -2
- ansible/plugins/action/include_vars.py +20 -22
- ansible/plugins/action/script.py +1 -3
- ansible/plugins/action/template.py +1 -2
- ansible/plugins/action/uri.py +4 -2
- ansible/plugins/cache/__init__.py +1 -0
- ansible/plugins/callback/__init__.py +13 -6
- ansible/plugins/connection/__init__.py +3 -7
- ansible/plugins/connection/local.py +2 -3
- ansible/plugins/connection/psrp.py +0 -2
- ansible/plugins/connection/ssh.py +2 -7
- ansible/plugins/connection/winrm.py +0 -2
- ansible/plugins/doc_fragments/result_format_callback.py +15 -0
- ansible/plugins/filter/core.py +4 -5
- ansible/plugins/filter/encryption.py +3 -27
- ansible/plugins/filter/mathstuff.py +1 -2
- ansible/plugins/filter/to_nice_yaml.yml +31 -3
- ansible/plugins/filter/to_yaml.yml +29 -12
- ansible/plugins/inventory/__init__.py +1 -2
- ansible/plugins/inventory/script.py +2 -1
- ansible/plugins/inventory/toml.py +3 -6
- ansible/plugins/inventory/yaml.py +1 -2
- ansible/plugins/list.py +10 -3
- ansible/plugins/loader.py +6 -6
- ansible/plugins/lookup/password.py +1 -2
- ansible/plugins/lookup/subelements.py +2 -3
- ansible/plugins/lookup/url.py +1 -1
- ansible/plugins/lookup/varnames.py +1 -2
- ansible/plugins/shell/__init__.py +9 -4
- ansible/plugins/shell/powershell.py +8 -24
- ansible/plugins/strategy/__init__.py +6 -3
- ansible/plugins/test/core.py +4 -1
- ansible/plugins/test/regex.yml +18 -6
- ansible/release.py +2 -2
- ansible/template/__init__.py +3 -7
- ansible/utils/collection_loader/_collection_config.py +5 -0
- ansible/utils/collection_loader/_collection_finder.py +11 -14
- ansible/utils/context_objects.py +7 -4
- ansible/utils/display.py +28 -167
- ansible/utils/encrypt.py +0 -5
- ansible/utils/helpers.py +6 -2
- ansible/utils/jsonrpc.py +7 -3
- ansible/utils/plugin_docs.py +49 -38
- ansible/utils/ssh_functions.py +0 -19
- ansible/utils/unsafe_proxy.py +7 -7
- ansible/vars/clean.py +2 -3
- ansible/vars/manager.py +27 -20
- ansible/vars/plugins.py +1 -31
- {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/METADATA +3 -3
- {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/RECORD +200 -200
- ansible_test/_data/completion/docker.txt +7 -7
- ansible_test/_data/completion/network.txt +0 -1
- ansible_test/_data/completion/remote.txt +4 -4
- ansible_test/_data/requirements/ansible-test.txt +1 -1
- ansible_test/_data/requirements/sanity.changelog.txt +1 -1
- ansible_test/_data/requirements/sanity.pep8.txt +1 -1
- ansible_test/_data/requirements/sanity.pylint.txt +4 -4
- ansible_test/_internal/cache.py +2 -5
- ansible_test/_internal/cli/compat.py +1 -1
- ansible_test/_internal/commands/coverage/combine.py +1 -3
- ansible_test/_internal/commands/integration/__init__.py +3 -7
- ansible_test/_internal/commands/integration/cloud/httptester.py +1 -1
- ansible_test/_internal/commands/integration/coverage.py +1 -3
- ansible_test/_internal/commands/integration/filters.py +5 -10
- ansible_test/_internal/commands/sanity/validate_modules.py +1 -5
- ansible_test/_internal/commands/units/__init__.py +1 -13
- ansible_test/_internal/completion.py +2 -5
- ansible_test/_internal/config.py +2 -7
- ansible_test/_internal/coverage_util.py +1 -1
- ansible_test/_internal/delegation.py +2 -0
- ansible_test/_internal/docker_util.py +1 -1
- ansible_test/_internal/host_profiles.py +6 -11
- ansible_test/_internal/provider/__init__.py +2 -5
- ansible_test/_internal/provisioning.py +2 -5
- ansible_test/_internal/pypi_proxy.py +1 -1
- ansible_test/_internal/target.py +2 -6
- ansible_test/_internal/thread.py +1 -4
- ansible_test/_internal/util.py +9 -14
- ansible_test/_util/controller/sanity/code-smell/runtime-metadata.py +14 -19
- ansible_test/_util/controller/sanity/pylint/plugins/unwanted.py +30 -27
- ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py +31 -18
- ansible_test/_util/controller/sanity/validate-modules/validate_modules/module_args.py +1 -2
- ansible_test/_util/controller/sanity/validate-modules/validate_modules/schema.py +59 -71
- ansible_test/_util/controller/sanity/validate-modules/validate_modules/utils.py +1 -2
- ansible_test/_util/target/cli/ansible_test_cli_stub.py +4 -2
- ansible_test/_util/target/common/constants.py +2 -2
- ansible_test/_util/target/setup/bootstrap.sh +0 -6
- ansible/utils/py3compat.py +0 -27
- ansible_test/_data/pytest/config/legacy.ini +0 -4
- {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/WHEEL +0 -0
- {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/entry_points.txt +0 -0
- {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/licenses/COPYING +0 -0
- {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/licenses/licenses/Apache-License.txt +0 -0
- {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/licenses/licenses/BSD-3-Clause.txt +0 -0
- {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/licenses/licenses/MIT-license.txt +0 -0
- {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/licenses/licenses/PSF-license.txt +0 -0
- {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/licenses/licenses/simplified_bsd.txt +0 -0
- {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/top_level.txt +0 -0
ansible/cli/doc.py
CHANGED
|
@@ -32,7 +32,6 @@ from ansible.errors import AnsibleError, AnsibleOptionsError, AnsibleParserError
|
|
|
32
32
|
from ansible.module_utils.common.text.converters import to_native, to_text
|
|
33
33
|
from ansible.module_utils.common.collections import is_sequence
|
|
34
34
|
from ansible.module_utils.common.yaml import yaml_dump
|
|
35
|
-
from ansible.module_utils.six import string_types
|
|
36
35
|
from ansible.parsing.plugin_docs import read_docstub
|
|
37
36
|
from ansible.parsing.yaml.dumper import AnsibleDumper
|
|
38
37
|
from ansible.parsing.yaml.loader import AnsibleLoader
|
|
@@ -1265,7 +1264,7 @@ class DocCLI(CLI, RoleMixin):
|
|
|
1265
1264
|
|
|
1266
1265
|
# description is specifically formatted and can either be string or list of strings
|
|
1267
1266
|
if 'description' not in opt:
|
|
1268
|
-
raise AnsibleError("All (sub-)options and return values must have a 'description' field")
|
|
1267
|
+
raise AnsibleError("All (sub-)options and return values must have a 'description' field", obj=o)
|
|
1269
1268
|
text.append('')
|
|
1270
1269
|
|
|
1271
1270
|
# TODO: push this to top of for and sort by size, create indent on largest key?
|
|
@@ -1274,7 +1273,7 @@ class DocCLI(CLI, RoleMixin):
|
|
|
1274
1273
|
sub_indent = inline_indent + extra_indent
|
|
1275
1274
|
if is_sequence(opt['description']):
|
|
1276
1275
|
for entry_idx, entry in enumerate(opt['description'], 1):
|
|
1277
|
-
if not isinstance(entry,
|
|
1276
|
+
if not isinstance(entry, str):
|
|
1278
1277
|
raise AnsibleError("Expected string in description of %s at index %s, got %s" % (o, entry_idx, type(entry)))
|
|
1279
1278
|
if entry_idx == 1:
|
|
1280
1279
|
text.append(key + DocCLI.warp_fill(DocCLI.tty_ify(entry), limit,
|
|
@@ -1282,7 +1281,7 @@ class DocCLI(CLI, RoleMixin):
|
|
|
1282
1281
|
else:
|
|
1283
1282
|
text.append(DocCLI.warp_fill(DocCLI.tty_ify(entry), limit, initial_indent=sub_indent, subsequent_indent=sub_indent))
|
|
1284
1283
|
else:
|
|
1285
|
-
if not isinstance(opt['description'],
|
|
1284
|
+
if not isinstance(opt['description'], str):
|
|
1286
1285
|
raise AnsibleError("Expected string in description of %s, got %s" % (o, type(opt['description'])))
|
|
1287
1286
|
text.append(key + DocCLI.warp_fill(DocCLI.tty_ify(opt['description']), limit,
|
|
1288
1287
|
initial_indent=inline_indent, subsequent_indent=sub_indent, initial_extra=len(extra_indent)))
|
|
@@ -1344,6 +1343,51 @@ class DocCLI(CLI, RoleMixin):
|
|
|
1344
1343
|
text.append("%s%s:" % (opt_indent, subkey))
|
|
1345
1344
|
DocCLI.add_fields(text, subdata, limit, opt_indent + ' ', return_values, opt_indent)
|
|
1346
1345
|
|
|
1346
|
+
@staticmethod
|
|
1347
|
+
def _add_seealso(text: list[str], seealsos: list[dict[str, t.Any]], limit: int, opt_indent: str) -> None:
|
|
1348
|
+
for item in seealsos:
|
|
1349
|
+
if 'module' in item:
|
|
1350
|
+
text.append(DocCLI.warp_fill(DocCLI.tty_ify('Module %s' % item['module']),
|
|
1351
|
+
limit - 6, initial_indent=opt_indent[:-2] + "* ", subsequent_indent=opt_indent))
|
|
1352
|
+
description = item.get('description')
|
|
1353
|
+
if description is None and item['module'].startswith('ansible.builtin.'):
|
|
1354
|
+
description = 'The official documentation on the %s module.' % item['module']
|
|
1355
|
+
if description is not None:
|
|
1356
|
+
text.append(DocCLI.warp_fill(DocCLI.tty_ify(description),
|
|
1357
|
+
limit - 6, initial_indent=opt_indent + ' ', subsequent_indent=opt_indent + ' '))
|
|
1358
|
+
if item['module'].startswith('ansible.builtin.'):
|
|
1359
|
+
relative_url = 'collections/%s_module.html' % item['module'].replace('.', '/', 2)
|
|
1360
|
+
text.append(DocCLI.warp_fill(DocCLI.tty_ify(get_versioned_doclink(relative_url)),
|
|
1361
|
+
limit - 6, initial_indent=opt_indent + ' ', subsequent_indent=opt_indent))
|
|
1362
|
+
elif 'plugin' in item and 'plugin_type' in item:
|
|
1363
|
+
plugin_suffix = ' plugin' if item['plugin_type'] not in ('module', 'role') else ''
|
|
1364
|
+
text.append(DocCLI.warp_fill(DocCLI.tty_ify('%s%s %s' % (item['plugin_type'].title(), plugin_suffix, item['plugin'])),
|
|
1365
|
+
limit - 6, initial_indent=opt_indent[:-2] + "* ", subsequent_indent=opt_indent))
|
|
1366
|
+
description = item.get('description')
|
|
1367
|
+
if description is None and item['plugin'].startswith('ansible.builtin.'):
|
|
1368
|
+
description = 'The official documentation on the %s %s%s.' % (item['plugin'], item['plugin_type'], plugin_suffix)
|
|
1369
|
+
if description is not None:
|
|
1370
|
+
text.append(DocCLI.warp_fill(DocCLI.tty_ify(description),
|
|
1371
|
+
limit - 6, initial_indent=opt_indent + ' ', subsequent_indent=opt_indent + ' '))
|
|
1372
|
+
if item['plugin'].startswith('ansible.builtin.'):
|
|
1373
|
+
relative_url = 'collections/%s_%s.html' % (item['plugin'].replace('.', '/', 2), item['plugin_type'])
|
|
1374
|
+
text.append(DocCLI.warp_fill(DocCLI.tty_ify(get_versioned_doclink(relative_url)),
|
|
1375
|
+
limit - 6, initial_indent=opt_indent + ' ', subsequent_indent=opt_indent))
|
|
1376
|
+
elif 'name' in item and 'link' in item and 'description' in item:
|
|
1377
|
+
text.append(DocCLI.warp_fill(DocCLI.tty_ify(item['name']),
|
|
1378
|
+
limit - 6, initial_indent=opt_indent[:-2] + "* ", subsequent_indent=opt_indent))
|
|
1379
|
+
text.append(DocCLI.warp_fill(DocCLI.tty_ify(item['description']),
|
|
1380
|
+
limit - 6, initial_indent=opt_indent + ' ', subsequent_indent=opt_indent + ' '))
|
|
1381
|
+
text.append(DocCLI.warp_fill(DocCLI.tty_ify(item['link']),
|
|
1382
|
+
limit - 6, initial_indent=opt_indent + ' ', subsequent_indent=opt_indent + ' '))
|
|
1383
|
+
elif 'ref' in item and 'description' in item:
|
|
1384
|
+
text.append(DocCLI.warp_fill(DocCLI.tty_ify('Ansible documentation [%s]' % item['ref']),
|
|
1385
|
+
limit - 6, initial_indent=opt_indent[:-2] + "* ", subsequent_indent=opt_indent))
|
|
1386
|
+
text.append(DocCLI.warp_fill(DocCLI.tty_ify(item['description']),
|
|
1387
|
+
limit - 6, initial_indent=opt_indent + ' ', subsequent_indent=opt_indent + ' '))
|
|
1388
|
+
text.append(DocCLI.warp_fill(DocCLI.tty_ify(get_versioned_doclink('/#stq=%s&stp=1' % item['ref'])),
|
|
1389
|
+
limit - 6, initial_indent=opt_indent + ' ', subsequent_indent=opt_indent + ' '))
|
|
1390
|
+
|
|
1347
1391
|
def get_role_man_text(self, role, role_json):
|
|
1348
1392
|
"""Generate text for the supplied role suitable for display.
|
|
1349
1393
|
|
|
@@ -1371,6 +1415,9 @@ class DocCLI(CLI, RoleMixin):
|
|
|
1371
1415
|
text.append("ENTRY POINT: %s %s" % (_format(entry_point, "BOLD"), desc))
|
|
1372
1416
|
text.append('')
|
|
1373
1417
|
|
|
1418
|
+
if version_added := doc.pop('version_added', None):
|
|
1419
|
+
text.append(_format("ADDED IN:", 'bold') + " %s\n" % DocCLI._format_version_added(version_added))
|
|
1420
|
+
|
|
1374
1421
|
if doc.get('description'):
|
|
1375
1422
|
if isinstance(doc['description'], list):
|
|
1376
1423
|
descs = doc['description']
|
|
@@ -1384,29 +1431,24 @@ class DocCLI(CLI, RoleMixin):
|
|
|
1384
1431
|
text.append(_format("Options", 'bold') + " (%s indicates it is required):" % ("=" if C.ANSIBLE_NOCOLOR else 'red'))
|
|
1385
1432
|
DocCLI.add_fields(text, doc.pop('options'), limit, opt_indent)
|
|
1386
1433
|
|
|
1387
|
-
if doc.
|
|
1388
|
-
display.deprecated(
|
|
1389
|
-
f'The role {role}\'s argument spec {entry_point} contains the key "attributes", '
|
|
1390
|
-
'which will not be displayed by ansible-doc in the future. '
|
|
1391
|
-
'This was unintentionally allowed when plugin attributes were added, '
|
|
1392
|
-
'but the feature does not map well to role argument specs.',
|
|
1393
|
-
version='2.20',
|
|
1394
|
-
)
|
|
1434
|
+
if notes := doc.pop('notes', False):
|
|
1395
1435
|
text.append("")
|
|
1396
|
-
text.append(_format("
|
|
1397
|
-
for
|
|
1398
|
-
text.append(
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1436
|
+
text.append(_format("NOTES:", 'bold'))
|
|
1437
|
+
for note in notes:
|
|
1438
|
+
text.append(DocCLI.warp_fill(DocCLI.tty_ify(note), limit - 6,
|
|
1439
|
+
initial_indent=opt_indent[:-2] + "* ", subsequent_indent=opt_indent))
|
|
1440
|
+
|
|
1441
|
+
if seealso := doc.pop('seealso', False):
|
|
1442
|
+
text.append("")
|
|
1443
|
+
text.append(_format("SEE ALSO:", 'bold'))
|
|
1444
|
+
DocCLI._add_seealso(text, seealso, limit=limit, opt_indent=opt_indent)
|
|
1403
1445
|
|
|
1404
1446
|
# generic elements we will handle identically
|
|
1405
1447
|
for k in ('author',):
|
|
1406
1448
|
if k not in doc:
|
|
1407
1449
|
continue
|
|
1408
1450
|
text.append('')
|
|
1409
|
-
if isinstance(doc[k],
|
|
1451
|
+
if isinstance(doc[k], str):
|
|
1410
1452
|
text.append('%s: %s' % (k.upper(), DocCLI.warp_fill(DocCLI.tty_ify(doc[k]),
|
|
1411
1453
|
limit - (len(k) + 2), subsequent_indent=opt_indent)))
|
|
1412
1454
|
elif isinstance(doc[k], (list, tuple)):
|
|
@@ -1418,7 +1460,7 @@ class DocCLI(CLI, RoleMixin):
|
|
|
1418
1460
|
if doc.get('examples', False):
|
|
1419
1461
|
text.append('')
|
|
1420
1462
|
text.append(_format("EXAMPLES:", 'bold'))
|
|
1421
|
-
if isinstance(doc['examples'],
|
|
1463
|
+
if isinstance(doc['examples'], str):
|
|
1422
1464
|
text.append(doc.pop('examples').strip())
|
|
1423
1465
|
else:
|
|
1424
1466
|
try:
|
|
@@ -1497,49 +1539,7 @@ class DocCLI(CLI, RoleMixin):
|
|
|
1497
1539
|
if doc.get('seealso', False):
|
|
1498
1540
|
text.append("")
|
|
1499
1541
|
text.append(_format("SEE ALSO:", 'bold'))
|
|
1500
|
-
|
|
1501
|
-
if 'module' in item:
|
|
1502
|
-
text.append(DocCLI.warp_fill(DocCLI.tty_ify('Module %s' % item['module']),
|
|
1503
|
-
limit - 6, initial_indent=opt_indent[:-2] + "* ", subsequent_indent=opt_indent))
|
|
1504
|
-
description = item.get('description')
|
|
1505
|
-
if description is None and item['module'].startswith('ansible.builtin.'):
|
|
1506
|
-
description = 'The official documentation on the %s module.' % item['module']
|
|
1507
|
-
if description is not None:
|
|
1508
|
-
text.append(DocCLI.warp_fill(DocCLI.tty_ify(description),
|
|
1509
|
-
limit - 6, initial_indent=opt_indent + ' ', subsequent_indent=opt_indent + ' '))
|
|
1510
|
-
if item['module'].startswith('ansible.builtin.'):
|
|
1511
|
-
relative_url = 'collections/%s_module.html' % item['module'].replace('.', '/', 2)
|
|
1512
|
-
text.append(DocCLI.warp_fill(DocCLI.tty_ify(get_versioned_doclink(relative_url)),
|
|
1513
|
-
limit - 6, initial_indent=opt_indent + ' ', subsequent_indent=opt_indent))
|
|
1514
|
-
elif 'plugin' in item and 'plugin_type' in item:
|
|
1515
|
-
plugin_suffix = ' plugin' if item['plugin_type'] not in ('module', 'role') else ''
|
|
1516
|
-
text.append(DocCLI.warp_fill(DocCLI.tty_ify('%s%s %s' % (item['plugin_type'].title(), plugin_suffix, item['plugin'])),
|
|
1517
|
-
limit - 6, initial_indent=opt_indent[:-2] + "* ", subsequent_indent=opt_indent))
|
|
1518
|
-
description = item.get('description')
|
|
1519
|
-
if description is None and item['plugin'].startswith('ansible.builtin.'):
|
|
1520
|
-
description = 'The official documentation on the %s %s%s.' % (item['plugin'], item['plugin_type'], plugin_suffix)
|
|
1521
|
-
if description is not None:
|
|
1522
|
-
text.append(DocCLI.warp_fill(DocCLI.tty_ify(description),
|
|
1523
|
-
limit - 6, initial_indent=opt_indent + ' ', subsequent_indent=opt_indent + ' '))
|
|
1524
|
-
if item['plugin'].startswith('ansible.builtin.'):
|
|
1525
|
-
relative_url = 'collections/%s_%s.html' % (item['plugin'].replace('.', '/', 2), item['plugin_type'])
|
|
1526
|
-
text.append(DocCLI.warp_fill(DocCLI.tty_ify(get_versioned_doclink(relative_url)),
|
|
1527
|
-
limit - 6, initial_indent=opt_indent + ' ', subsequent_indent=opt_indent))
|
|
1528
|
-
elif 'name' in item and 'link' in item and 'description' in item:
|
|
1529
|
-
text.append(DocCLI.warp_fill(DocCLI.tty_ify(item['name']),
|
|
1530
|
-
limit - 6, initial_indent=opt_indent[:-2] + "* ", subsequent_indent=opt_indent))
|
|
1531
|
-
text.append(DocCLI.warp_fill(DocCLI.tty_ify(item['description']),
|
|
1532
|
-
limit - 6, initial_indent=opt_indent + ' ', subsequent_indent=opt_indent + ' '))
|
|
1533
|
-
text.append(DocCLI.warp_fill(DocCLI.tty_ify(item['link']),
|
|
1534
|
-
limit - 6, initial_indent=opt_indent + ' ', subsequent_indent=opt_indent + ' '))
|
|
1535
|
-
elif 'ref' in item and 'description' in item:
|
|
1536
|
-
text.append(DocCLI.warp_fill(DocCLI.tty_ify('Ansible documentation [%s]' % item['ref']),
|
|
1537
|
-
limit - 6, initial_indent=opt_indent[:-2] + "* ", subsequent_indent=opt_indent))
|
|
1538
|
-
text.append(DocCLI.warp_fill(DocCLI.tty_ify(item['description']),
|
|
1539
|
-
limit - 6, initial_indent=opt_indent + ' ', subsequent_indent=opt_indent + ' '))
|
|
1540
|
-
text.append(DocCLI.warp_fill(DocCLI.tty_ify(get_versioned_doclink('/#stq=%s&stp=1' % item['ref'])),
|
|
1541
|
-
limit - 6, initial_indent=opt_indent + ' ', subsequent_indent=opt_indent + ' '))
|
|
1542
|
-
|
|
1542
|
+
DocCLI._add_seealso(text, doc['seealso'], limit=limit, opt_indent=opt_indent)
|
|
1543
1543
|
del doc['seealso']
|
|
1544
1544
|
|
|
1545
1545
|
if doc.get('requirements', False):
|
|
@@ -1554,7 +1554,7 @@ class DocCLI(CLI, RoleMixin):
|
|
|
1554
1554
|
continue
|
|
1555
1555
|
text.append('')
|
|
1556
1556
|
header = _format(k.upper(), 'bold')
|
|
1557
|
-
if isinstance(doc[k],
|
|
1557
|
+
if isinstance(doc[k], str):
|
|
1558
1558
|
text.append('%s: %s' % (header, DocCLI.warp_fill(DocCLI.tty_ify(doc[k]), limit - (len(k) + 2), subsequent_indent=opt_indent)))
|
|
1559
1559
|
elif isinstance(doc[k], (list, tuple)):
|
|
1560
1560
|
text.append('%s: %s' % (header, ', '.join(doc[k])))
|
|
@@ -1566,7 +1566,7 @@ class DocCLI(CLI, RoleMixin):
|
|
|
1566
1566
|
if doc.get('plainexamples', False):
|
|
1567
1567
|
text.append('')
|
|
1568
1568
|
text.append(_format("EXAMPLES:", 'bold'))
|
|
1569
|
-
if isinstance(doc['plainexamples'],
|
|
1569
|
+
if isinstance(doc['plainexamples'], str):
|
|
1570
1570
|
text.append(doc.pop('plainexamples').strip())
|
|
1571
1571
|
else:
|
|
1572
1572
|
try:
|
|
@@ -1603,7 +1603,7 @@ def _do_yaml_snippet(doc):
|
|
|
1603
1603
|
|
|
1604
1604
|
for o in sorted(doc['options'].keys()):
|
|
1605
1605
|
opt = doc['options'][o]
|
|
1606
|
-
if isinstance(opt['description'],
|
|
1606
|
+
if isinstance(opt['description'], str):
|
|
1607
1607
|
desc = DocCLI.tty_ify(opt['description'])
|
|
1608
1608
|
else:
|
|
1609
1609
|
desc = DocCLI.tty_ify(" ".join(opt['description']))
|
ansible/cli/galaxy.py
CHANGED
|
@@ -54,7 +54,6 @@ from ansible.module_utils.common.collections import is_iterable
|
|
|
54
54
|
from ansible.module_utils.common.yaml import yaml_dump, yaml_load
|
|
55
55
|
from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
|
|
56
56
|
from ansible._internal._datatag._tags import TrustedAsTemplate
|
|
57
|
-
from ansible.module_utils import six
|
|
58
57
|
from ansible.parsing.dataloader import DataLoader
|
|
59
58
|
from ansible.playbook.role.requirement import RoleRequirement
|
|
60
59
|
from ansible._internal._templating._engine import TemplateEngine
|
|
@@ -65,7 +64,6 @@ from ansible.utils.plugin_docs import get_versioned_doclink
|
|
|
65
64
|
from ansible.utils.vars import load_extra_vars
|
|
66
65
|
|
|
67
66
|
display = Display()
|
|
68
|
-
urlparse = six.moves.urllib.parse.urlparse
|
|
69
67
|
|
|
70
68
|
|
|
71
69
|
def with_collection_artifacts_manager(wrapped_method):
|
|
@@ -213,6 +211,18 @@ class GalaxyCLI(CLI):
|
|
|
213
211
|
self.lazy_role_api = None
|
|
214
212
|
super(GalaxyCLI, self).__init__(args)
|
|
215
213
|
|
|
214
|
+
@property
|
|
215
|
+
def collection_paths(self):
|
|
216
|
+
"""
|
|
217
|
+
Exclude lib/ansible/_internal/ansible_collections/.
|
|
218
|
+
"""
|
|
219
|
+
# exclude bundled collections, e.g. ansible._protomatter
|
|
220
|
+
return [
|
|
221
|
+
path
|
|
222
|
+
for path in AnsibleCollectionConfig.collection_paths
|
|
223
|
+
if path != AnsibleCollectionConfig._internal_collections
|
|
224
|
+
]
|
|
225
|
+
|
|
216
226
|
def init_parser(self):
|
|
217
227
|
""" create an options parser for bin/ansible """
|
|
218
228
|
|
|
@@ -644,23 +654,10 @@ class GalaxyCLI(CLI):
|
|
|
644
654
|
client_secret = server_options.pop('client_secret')
|
|
645
655
|
token_val = server_options['token'] or NoTokenSentinel
|
|
646
656
|
username = server_options['username']
|
|
647
|
-
api_version = server_options.pop('api_version')
|
|
648
657
|
if server_options['validate_certs'] is None:
|
|
649
658
|
server_options['validate_certs'] = context.CLIARGS['resolved_validate_certs']
|
|
650
659
|
validate_certs = server_options['validate_certs']
|
|
651
660
|
|
|
652
|
-
# This allows a user to explicitly force use of an API version when
|
|
653
|
-
# multiple versions are supported. This was added for testing
|
|
654
|
-
# against pulp_ansible and I'm not sure it has a practical purpose
|
|
655
|
-
# outside of this use case. As such, this option is not documented
|
|
656
|
-
# as of now
|
|
657
|
-
if api_version:
|
|
658
|
-
display.warning(
|
|
659
|
-
f'The specified "api_version" configuration for the galaxy server "{server_key}" is '
|
|
660
|
-
'not a public configuration, and may be removed at any time without warning.'
|
|
661
|
-
)
|
|
662
|
-
server_options['available_api_versions'] = {'v%s' % api_version: '/v%s' % api_version}
|
|
663
|
-
|
|
664
661
|
# default case if no auth info is provided.
|
|
665
662
|
server_options['token'] = None
|
|
666
663
|
|
|
@@ -687,12 +684,6 @@ class GalaxyCLI(CLI):
|
|
|
687
684
|
))
|
|
688
685
|
|
|
689
686
|
cmd_server = context.CLIARGS['api_server']
|
|
690
|
-
if context.CLIARGS['api_version']:
|
|
691
|
-
api_version = context.CLIARGS['api_version']
|
|
692
|
-
display.warning(
|
|
693
|
-
'The --api-version is not a public argument, and may be removed at any time without warning.'
|
|
694
|
-
)
|
|
695
|
-
galaxy_options['available_api_versions'] = {'v%s' % api_version: '/v%s' % api_version}
|
|
696
687
|
|
|
697
688
|
cmd_token = GalaxyToken(token=context.CLIARGS['api_key'])
|
|
698
689
|
|
|
@@ -1281,7 +1272,7 @@ class GalaxyCLI(CLI):
|
|
|
1281
1272
|
"""Compare checksums with the collection(s) found on the server and the installed copy. This does not verify dependencies."""
|
|
1282
1273
|
|
|
1283
1274
|
collections = context.CLIARGS['args']
|
|
1284
|
-
search_paths =
|
|
1275
|
+
search_paths = self.collection_paths
|
|
1285
1276
|
ignore_errors = context.CLIARGS['ignore_errors']
|
|
1286
1277
|
local_verify_only = context.CLIARGS['offline']
|
|
1287
1278
|
requirements_file = context.CLIARGS['requirements']
|
|
@@ -1423,7 +1414,7 @@ class GalaxyCLI(CLI):
|
|
|
1423
1414
|
collections_path = C.COLLECTIONS_PATHS
|
|
1424
1415
|
|
|
1425
1416
|
managed_paths = set(validate_collection_path(p) for p in C.COLLECTIONS_PATHS)
|
|
1426
|
-
read_req_paths = set(validate_collection_path(p) for p in
|
|
1417
|
+
read_req_paths = set(validate_collection_path(p) for p in self.collection_paths)
|
|
1427
1418
|
|
|
1428
1419
|
unexpected_path = C.GALAXY_COLLECTIONS_PATH_WARNING and not any(p.startswith(path) for p in managed_paths)
|
|
1429
1420
|
if unexpected_path and any(p.startswith(path) for p in read_req_paths):
|
|
@@ -1639,7 +1630,7 @@ class GalaxyCLI(CLI):
|
|
|
1639
1630
|
collection_name = context.CLIARGS['collection']
|
|
1640
1631
|
default_collections_path = set(C.COLLECTIONS_PATHS)
|
|
1641
1632
|
collections_search_paths = (
|
|
1642
|
-
set(context.CLIARGS['collections_path'] or []) | default_collections_path | set(
|
|
1633
|
+
set(context.CLIARGS['collections_path'] or []) | default_collections_path | set(self.collection_paths)
|
|
1643
1634
|
)
|
|
1644
1635
|
collections_in_paths = {}
|
|
1645
1636
|
|
ansible/cli/inventory.py
CHANGED
|
@@ -44,7 +44,6 @@ class InventoryCLI(CLI):
|
|
|
44
44
|
|
|
45
45
|
def init_parser(self):
|
|
46
46
|
super(InventoryCLI, self).init_parser(
|
|
47
|
-
usage='usage: %prog [options] [group]',
|
|
48
47
|
desc='Show Ansible inventory information, by default it uses the inventory script JSON format')
|
|
49
48
|
|
|
50
49
|
opt_help.add_inventory_options(self.parser)
|
ansible/cli/playbook.py
CHANGED
|
@@ -40,7 +40,6 @@ class PlaybookCLI(CLI):
|
|
|
40
40
|
|
|
41
41
|
# create parser for CLI options
|
|
42
42
|
super(PlaybookCLI, self).init_parser(
|
|
43
|
-
usage="%prog [options] playbook.yml [playbook2 ...]",
|
|
44
43
|
desc="Runs Ansible playbooks, executing the defined tasks on the targeted hosts.")
|
|
45
44
|
|
|
46
45
|
opt_help.add_connect_options(self.parser)
|
ansible/cli/pull.py
CHANGED
|
@@ -108,7 +108,6 @@ class PullCLI(CLI):
|
|
|
108
108
|
|
|
109
109
|
# signature is different from parent as caller should not need to add usage/desc
|
|
110
110
|
super(PullCLI, self).init_parser(
|
|
111
|
-
usage='%prog -U <repository> [options] [<playbook.yml>]',
|
|
112
111
|
desc="pulls playbooks from a VCS repo and executes them on target host")
|
|
113
112
|
|
|
114
113
|
# Do not add check_options as there's a conflict with --checkout/-C
|
ansible/config/base.yml
CHANGED
|
@@ -1215,7 +1215,6 @@ DEFAULT_TRANSPORT:
|
|
|
1215
1215
|
default: ssh
|
|
1216
1216
|
description:
|
|
1217
1217
|
- Can be any connection plugin available to your ansible installation.
|
|
1218
|
-
- There is also a (DEPRECATED) special 'smart' option, that will toggle between 'ssh' and 'paramiko' depending on controller OS and ssh versions.
|
|
1219
1218
|
env: [{name: ANSIBLE_TRANSPORT}]
|
|
1220
1219
|
ini:
|
|
1221
1220
|
- {key: transport, section: defaults}
|
|
@@ -1689,12 +1688,12 @@ INTERPRETER_PYTHON:
|
|
|
1689
1688
|
INTERPRETER_PYTHON_FALLBACK:
|
|
1690
1689
|
name: Ordered list of Python interpreters to check for in discovery
|
|
1691
1690
|
default:
|
|
1691
|
+
- python3.14
|
|
1692
1692
|
- python3.13
|
|
1693
1693
|
- python3.12
|
|
1694
1694
|
- python3.11
|
|
1695
1695
|
- python3.10
|
|
1696
1696
|
- python3.9
|
|
1697
|
-
- python3.8
|
|
1698
1697
|
- /usr/bin/python3
|
|
1699
1698
|
- python3
|
|
1700
1699
|
vars:
|
|
@@ -1854,29 +1853,6 @@ PAGER:
|
|
|
1854
1853
|
- name: ANSIBLE_PAGER
|
|
1855
1854
|
version_added: '2.15'
|
|
1856
1855
|
- name: PAGER
|
|
1857
|
-
PARAMIKO_HOST_KEY_AUTO_ADD:
|
|
1858
|
-
default: False
|
|
1859
|
-
description: 'TODO: write it'
|
|
1860
|
-
env: [{name: ANSIBLE_PARAMIKO_HOST_KEY_AUTO_ADD}]
|
|
1861
|
-
ini:
|
|
1862
|
-
- {key: host_key_auto_add, section: paramiko_connection}
|
|
1863
|
-
type: boolean
|
|
1864
|
-
deprecated:
|
|
1865
|
-
why: This option was moved to the plugin itself
|
|
1866
|
-
version: "2.20"
|
|
1867
|
-
alternatives: Use the option from the plugin itself.
|
|
1868
|
-
PARAMIKO_LOOK_FOR_KEYS:
|
|
1869
|
-
name: look for keys
|
|
1870
|
-
default: True
|
|
1871
|
-
description: 'TODO: write it'
|
|
1872
|
-
env: [{name: ANSIBLE_PARAMIKO_LOOK_FOR_KEYS}]
|
|
1873
|
-
ini:
|
|
1874
|
-
- {key: look_for_keys, section: paramiko_connection}
|
|
1875
|
-
type: boolean
|
|
1876
|
-
deprecated:
|
|
1877
|
-
why: This option was moved to the plugin itself
|
|
1878
|
-
version: "2.20"
|
|
1879
|
-
alternatives: Use the option from the plugin itself.
|
|
1880
1856
|
PERSISTENT_CONTROL_PATH_DIR:
|
|
1881
1857
|
name: Persistence socket path
|
|
1882
1858
|
default: '{{ ANSIBLE_HOME ~ "/pc" }}'
|
ansible/config/manager.py
CHANGED
|
@@ -36,7 +36,6 @@ GALAXY_SERVER_DEF = [
|
|
|
36
36
|
('password', False, 'str'),
|
|
37
37
|
('token', False, 'str'),
|
|
38
38
|
('auth_url', False, 'str'),
|
|
39
|
-
('api_version', False, 'int'),
|
|
40
39
|
('validate_certs', False, 'bool'),
|
|
41
40
|
('client_id', False, 'str'),
|
|
42
41
|
('client_secret', False, 'str'),
|
|
@@ -45,7 +44,6 @@ GALAXY_SERVER_DEF = [
|
|
|
45
44
|
|
|
46
45
|
# config definition fields
|
|
47
46
|
GALAXY_SERVER_ADDITIONAL = {
|
|
48
|
-
'api_version': {'default': None, 'choices': [2, 3]},
|
|
49
47
|
'validate_certs': {'cli': [{'name': 'validate_certs'}]},
|
|
50
48
|
'timeout': {'cli': [{'name': 'timeout'}]},
|
|
51
49
|
'token': {'default': None},
|
|
@@ -174,6 +174,27 @@ class PlayIterator:
|
|
|
174
174
|
setup_task.when = self._play._included_conditional[:]
|
|
175
175
|
setup_block.block = [setup_task]
|
|
176
176
|
|
|
177
|
+
validation_task = Task.load({
|
|
178
|
+
'name': f'Validating arguments against arg spec {self._play.validate_argspec}',
|
|
179
|
+
'action': 'ansible.builtin.validate_argument_spec',
|
|
180
|
+
'args': {
|
|
181
|
+
# 'provided_arguments': {}, # allow configuration via module_defaults
|
|
182
|
+
'argument_spec': self._play.argument_spec,
|
|
183
|
+
'validate_args_context': {
|
|
184
|
+
'type': 'play',
|
|
185
|
+
'name': self._play.validate_argspec,
|
|
186
|
+
'argument_spec_name': self._play.validate_argspec,
|
|
187
|
+
'path': self._play._metadata_path,
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
'tags': ['always'],
|
|
191
|
+
}, block=setup_block)
|
|
192
|
+
|
|
193
|
+
validation_task.set_loader(self._play._loader)
|
|
194
|
+
if self._play._included_conditional is not None:
|
|
195
|
+
validation_task.when = self._play._included_conditional[:]
|
|
196
|
+
setup_block.block.append(validation_task)
|
|
197
|
+
|
|
177
198
|
setup_block = setup_block.filter_tagged_tasks(all_vars)
|
|
178
199
|
self._blocks.append(setup_block)
|
|
179
200
|
|
|
@@ -271,35 +292,36 @@ class PlayIterator:
|
|
|
271
292
|
return (state, None)
|
|
272
293
|
|
|
273
294
|
if state.run_state == IteratingStates.SETUP:
|
|
274
|
-
# First, we check to see if we
|
|
275
|
-
#
|
|
276
|
-
#
|
|
277
|
-
# the specified host.
|
|
278
|
-
|
|
279
|
-
|
|
295
|
+
# First, we check to see if we completed both setup tasks injected
|
|
296
|
+
# during play compilation in __init__ above.
|
|
297
|
+
# If not, below we will determine if we do in fact want to gather
|
|
298
|
+
# facts or validate arguments for the specified host.
|
|
299
|
+
state.pending_setup = state.cur_regular_task < len(block.block)
|
|
300
|
+
if state.pending_setup:
|
|
301
|
+
task = block.block[state.cur_regular_task]
|
|
280
302
|
|
|
281
303
|
# Gather facts if the default is 'smart' and we have not yet
|
|
282
304
|
# done it for this host; or if 'explicit' and the play sets
|
|
283
305
|
# gather_facts to True; or if 'implicit' and the play does
|
|
284
306
|
# NOT explicitly set gather_facts to False.
|
|
285
|
-
|
|
307
|
+
gather_facts = bool(state.cur_regular_task == 0)
|
|
286
308
|
gathering = C.DEFAULT_GATHERING
|
|
287
309
|
implied = self._play.gather_facts is None or boolean(self._play.gather_facts, strict=False)
|
|
288
310
|
|
|
289
|
-
if
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
else:
|
|
298
|
-
# This is the second trip through IteratingStates.SETUP, so we clear
|
|
299
|
-
# the flag and move onto the next block in the list while setting
|
|
300
|
-
# the run state to IteratingStates.TASKS
|
|
301
|
-
state.pending_setup = False
|
|
311
|
+
if gather_facts and not (
|
|
312
|
+
(gathering == 'implicit' and implied) or
|
|
313
|
+
(gathering == 'explicit' and boolean(self._play.gather_facts, strict=False)) or
|
|
314
|
+
(gathering == 'smart' and implied and not self._variable_manager._facts_gathered_for_host(host.name))
|
|
315
|
+
):
|
|
316
|
+
task = None
|
|
317
|
+
elif not gather_facts and not self._play.validate_argspec:
|
|
318
|
+
task = None
|
|
302
319
|
|
|
320
|
+
state.cur_regular_task += 1
|
|
321
|
+
else:
|
|
322
|
+
# This is the last trip through IteratingStates.SETUP, so we
|
|
323
|
+
# move onto the next block in the list while setting the run
|
|
324
|
+
# state to IteratingStates.TASKS
|
|
303
325
|
state.run_state = IteratingStates.TASKS
|
|
304
326
|
if not state.did_start_at_task:
|
|
305
327
|
state.cur_block += 1
|
|
@@ -31,7 +31,6 @@ from ansible.utils.helpers import pct_to_int
|
|
|
31
31
|
from ansible.utils.collection_loader import AnsibleCollectionConfig
|
|
32
32
|
from ansible.utils.collection_loader._collection_finder import _get_collection_name_from_path, _get_collection_playbook_path
|
|
33
33
|
from ansible.utils.path import makedirs_safe
|
|
34
|
-
from ansible.utils.ssh_functions import set_default_transport
|
|
35
34
|
from ansible.utils.display import Display
|
|
36
35
|
|
|
37
36
|
|
|
@@ -65,14 +64,6 @@ class PlaybookExecutor:
|
|
|
65
64
|
forks=context.CLIARGS.get('forks'),
|
|
66
65
|
)
|
|
67
66
|
|
|
68
|
-
# Note: We run this here to cache whether the default ansible ssh
|
|
69
|
-
# executable supports control persist. Sometime in the future we may
|
|
70
|
-
# need to enhance this to check that ansible_ssh_executable specified
|
|
71
|
-
# in inventory is also cached. We can't do this caching at the point
|
|
72
|
-
# where it is used (in task_executor) because that is post-fork and
|
|
73
|
-
# therefore would be discarded after every task.
|
|
74
|
-
set_default_transport()
|
|
75
|
-
|
|
76
67
|
def run(self):
|
|
77
68
|
"""
|
|
78
69
|
Run the given playbook, based on the settings in the play which
|
|
@@ -67,14 +67,34 @@ try {
|
|
|
67
67
|
$result.finished = $true
|
|
68
68
|
|
|
69
69
|
if ($jobAsyncResult.IsCompleted) {
|
|
70
|
-
$jobOutput = $ps.EndInvoke($jobAsyncResult)
|
|
70
|
+
$jobOutput = @($ps.EndInvoke($jobAsyncResult) | Out-String) -join "`n"
|
|
71
71
|
$jobError = $ps.Streams.Error
|
|
72
72
|
|
|
73
73
|
# write success/output/error to result object
|
|
74
|
-
|
|
75
|
-
$
|
|
74
|
+
$moduleResultJson = $jobOutput
|
|
75
|
+
$startJsonChar = $moduleResultJson.IndexOf([char]'{')
|
|
76
|
+
if ($startJsonChar -eq -1) {
|
|
77
|
+
throw "No start of json char found in module result"
|
|
78
|
+
}
|
|
79
|
+
$moduleResultJson = $moduleResultJson.Substring($startJsonChar)
|
|
80
|
+
|
|
81
|
+
$endJsonChar = $moduleResultJson.LastIndexOf([char]'}')
|
|
82
|
+
if ($endJsonChar -eq -1) {
|
|
83
|
+
throw "No end of json char found in module result"
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
$trailingJunk = $moduleResultJson.Substring($endJsonChar + 1).Trim()
|
|
87
|
+
$moduleResultJson = $moduleResultJson.Substring(0, $endJsonChar + 1)
|
|
88
|
+
$moduleResult = $moduleResultJson | ConvertFrom-Json | Convert-JsonObject
|
|
76
89
|
# TODO: check for conflicting keys
|
|
77
90
|
$result = $result + $moduleResult
|
|
91
|
+
|
|
92
|
+
if ($trailingJunk) {
|
|
93
|
+
if (-not $result.warnings) {
|
|
94
|
+
$result.warnings = @()
|
|
95
|
+
}
|
|
96
|
+
$result.warnings += "Module invocation had junk after the JSON data: $trailingJunk"
|
|
97
|
+
}
|
|
78
98
|
}
|
|
79
99
|
else {
|
|
80
100
|
# We can't call Stop() as pwsh won't respond if it is busy calling a .NET
|
|
@@ -103,7 +123,7 @@ catch {
|
|
|
103
123
|
$result.failed = $true
|
|
104
124
|
$result.msg = "failure during async watchdog: $_"
|
|
105
125
|
# return output back, if available, to Ansible to help with debugging errors
|
|
106
|
-
$result.stdout = $jobOutput
|
|
126
|
+
$result.stdout = $jobOutput
|
|
107
127
|
$result.stderr = $jobError | Out-String
|
|
108
128
|
}
|
|
109
129
|
finally {
|