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/modules/dnf.py
CHANGED
|
@@ -213,13 +213,6 @@ options:
|
|
|
213
213
|
type: bool
|
|
214
214
|
default: "no"
|
|
215
215
|
version_added: "2.7"
|
|
216
|
-
install_repoquery:
|
|
217
|
-
description:
|
|
218
|
-
- This is effectively a no-op in DNF as it is not needed with DNF.
|
|
219
|
-
- This option is deprecated and will be removed in ansible-core 2.20.
|
|
220
|
-
type: bool
|
|
221
|
-
default: "yes"
|
|
222
|
-
version_added: "2.7"
|
|
223
216
|
download_only:
|
|
224
217
|
description:
|
|
225
218
|
- Only download the packages, do not install them.
|
|
@@ -544,6 +537,9 @@ class DnfModule(YumDnf):
|
|
|
544
537
|
conf.sslverify = sslverify
|
|
545
538
|
|
|
546
539
|
# Set installroot
|
|
540
|
+
if not os.path.isdir(installroot):
|
|
541
|
+
self.module.fail_json(msg=f"Installroot {installroot} must be a directory")
|
|
542
|
+
|
|
547
543
|
conf.installroot = installroot
|
|
548
544
|
|
|
549
545
|
# Load substitutions from the filesystem
|
ansible/modules/dnf5.py
CHANGED
|
@@ -182,12 +182,6 @@ options:
|
|
|
182
182
|
using this in combination with wildcard characters in O(name) may result in an unexpected results.
|
|
183
183
|
type: bool
|
|
184
184
|
default: "no"
|
|
185
|
-
install_repoquery:
|
|
186
|
-
description:
|
|
187
|
-
- This is effectively a no-op in DNF as it is not needed with DNF.
|
|
188
|
-
- This option is deprecated and will be removed in ansible-core 2.20.
|
|
189
|
-
type: bool
|
|
190
|
-
default: "yes"
|
|
191
185
|
download_only:
|
|
192
186
|
description:
|
|
193
187
|
- Only download the packages, do not install them.
|
|
@@ -601,6 +595,10 @@ class Dnf5Module(YumDnf):
|
|
|
601
595
|
conf.localpkg_gpgcheck = not self.disable_gpg_check
|
|
602
596
|
conf.sslverify = self.sslverify
|
|
603
597
|
conf.clean_requirements_on_remove = self.autoremove
|
|
598
|
+
|
|
599
|
+
if not os.path.isdir(self.installroot):
|
|
600
|
+
self.module.fail_json(msg=f"Installroot {self.installroot} must be a directory")
|
|
601
|
+
|
|
604
602
|
conf.installroot = self.installroot
|
|
605
603
|
conf.use_host_config = True # needed for installroot
|
|
606
604
|
conf.cacheonly = "all" if self.cacheonly else "none"
|
ansible/modules/expect.py
CHANGED
ansible/modules/find.py
CHANGED
|
@@ -291,7 +291,6 @@ import time
|
|
|
291
291
|
|
|
292
292
|
from ansible.module_utils.common.text.converters import to_text, to_native
|
|
293
293
|
from ansible.module_utils.basic import AnsibleModule
|
|
294
|
-
from ansible.module_utils.six import string_types
|
|
295
294
|
|
|
296
295
|
|
|
297
296
|
class _Object:
|
|
@@ -496,7 +495,7 @@ def main():
|
|
|
496
495
|
|
|
497
496
|
params = module.params
|
|
498
497
|
|
|
499
|
-
if params['mode'] and not isinstance(params['mode'],
|
|
498
|
+
if params['mode'] and not isinstance(params['mode'], str):
|
|
500
499
|
module.fail_json(
|
|
501
500
|
msg="argument 'mode' is not a string and conversion is not allowed, value is of type %s" % params['mode'].__class__.__name__
|
|
502
501
|
)
|
ansible/modules/get_url.py
CHANGED
|
@@ -374,9 +374,9 @@ import shutil
|
|
|
374
374
|
import tempfile
|
|
375
375
|
|
|
376
376
|
from datetime import datetime, timezone
|
|
377
|
+
from urllib.parse import urlsplit
|
|
377
378
|
|
|
378
379
|
from ansible.module_utils.basic import AnsibleModule
|
|
379
|
-
from ansible.module_utils.six.moves.urllib.parse import urlsplit
|
|
380
380
|
from ansible.module_utils.common.text.converters import to_native
|
|
381
381
|
from ansible.module_utils.urls import fetch_url, url_argument_spec
|
|
382
382
|
|
ansible/modules/git.py
CHANGED
|
@@ -343,7 +343,6 @@ from ansible.module_utils.common.text.converters import to_native, to_text
|
|
|
343
343
|
from ansible.module_utils.basic import AnsibleModule
|
|
344
344
|
from ansible.module_utils.common.locale import get_best_parsable_locale
|
|
345
345
|
from ansible.module_utils.common.process import get_bin_path
|
|
346
|
-
from ansible.module_utils.six import b, string_types
|
|
347
346
|
|
|
348
347
|
|
|
349
348
|
def relocate_repo(module, result, repo_dir, old_repo_dir, worktree_dir):
|
|
@@ -443,12 +442,12 @@ def write_ssh_wrapper(module):
|
|
|
443
442
|
fd, wrapper_path = tempfile.mkstemp()
|
|
444
443
|
|
|
445
444
|
# use existing git_ssh/ssh_command, fallback to 'ssh'
|
|
446
|
-
template =
|
|
445
|
+
template = """#!/bin/sh
|
|
447
446
|
%s $GIT_SSH_OPTS "$@"
|
|
448
|
-
""" % os.environ.get('GIT_SSH', os.environ.get('GIT_SSH_COMMAND', 'ssh'))
|
|
447
|
+
""" % os.environ.get('GIT_SSH', os.environ.get('GIT_SSH_COMMAND', 'ssh'))
|
|
449
448
|
|
|
450
449
|
# write it
|
|
451
|
-
with os.fdopen(fd, 'w
|
|
450
|
+
with os.fdopen(fd, 'w') as fh:
|
|
452
451
|
fh.write(template)
|
|
453
452
|
|
|
454
453
|
# set execute
|
|
@@ -1257,7 +1256,7 @@ def main():
|
|
|
1257
1256
|
|
|
1258
1257
|
# evaluate and set the umask before doing anything else
|
|
1259
1258
|
if umask is not None:
|
|
1260
|
-
if not isinstance(umask,
|
|
1259
|
+
if not isinstance(umask, str):
|
|
1261
1260
|
module.fail_json(msg="umask must be defined as a quoted octal integer")
|
|
1262
1261
|
try:
|
|
1263
1262
|
umask = int(umask, 8)
|
ansible/modules/include_vars.py
CHANGED
|
@@ -66,7 +66,7 @@ options:
|
|
|
66
66
|
description:
|
|
67
67
|
- Ignore unknown file extensions within the directory.
|
|
68
68
|
- This allows users to specify a directory containing vars files that are intermingled with non-vars files extension types
|
|
69
|
-
(
|
|
69
|
+
(for example, a directory with a README in it and vars files).
|
|
70
70
|
type: bool
|
|
71
71
|
default: no
|
|
72
72
|
version_added: "2.7"
|
ansible/modules/lineinfile.py
CHANGED
|
@@ -123,6 +123,13 @@ options:
|
|
|
123
123
|
type: bool
|
|
124
124
|
default: no
|
|
125
125
|
version_added: "2.5"
|
|
126
|
+
encoding:
|
|
127
|
+
description:
|
|
128
|
+
- The character set in which the target file is encoded.
|
|
129
|
+
- For a list of available built-in encodings, see U(https://docs.python.org/3/library/codecs.html#standard-encodings)
|
|
130
|
+
type: str
|
|
131
|
+
default: utf-8
|
|
132
|
+
version_added: "2.20"
|
|
126
133
|
extends_documentation_fragment:
|
|
127
134
|
- action_common_attributes
|
|
128
135
|
- action_common_attributes.files
|
|
@@ -250,11 +257,11 @@ from ansible.module_utils.basic import AnsibleModule
|
|
|
250
257
|
from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
|
|
251
258
|
|
|
252
259
|
|
|
253
|
-
def write_changes(module,
|
|
260
|
+
def write_changes(module, lines, dest, encoding=None):
|
|
254
261
|
|
|
255
262
|
tmpfd, tmpfile = tempfile.mkstemp(dir=module.tmpdir)
|
|
256
|
-
with os.fdopen(tmpfd, '
|
|
257
|
-
f.writelines(
|
|
263
|
+
with os.fdopen(tmpfd, 'w', encoding=encoding) as f:
|
|
264
|
+
f.writelines(lines)
|
|
258
265
|
|
|
259
266
|
validate = module.params.get('validate', None)
|
|
260
267
|
valid = not validate
|
|
@@ -293,6 +300,7 @@ def present(module, dest, regexp, search_string, line, insertafter, insertbefore
|
|
|
293
300
|
'before_header': '%s (content)' % dest,
|
|
294
301
|
'after_header': '%s (content)' % dest}
|
|
295
302
|
|
|
303
|
+
encoding = module.params.get('encoding', None)
|
|
296
304
|
b_dest = to_bytes(dest, errors='surrogate_or_strict')
|
|
297
305
|
if not os.path.exists(b_dest):
|
|
298
306
|
if not create:
|
|
@@ -304,30 +312,29 @@ def present(module, dest, regexp, search_string, line, insertafter, insertbefore
|
|
|
304
312
|
except Exception as e:
|
|
305
313
|
module.fail_json(msg='Error creating %s (%s)' % (to_text(b_destpath), to_text(e)))
|
|
306
314
|
|
|
307
|
-
|
|
315
|
+
lines = []
|
|
308
316
|
else:
|
|
309
|
-
with open(b_dest, '
|
|
310
|
-
|
|
317
|
+
with open(b_dest, 'r', encoding=encoding) as f:
|
|
318
|
+
lines = f.readlines()
|
|
311
319
|
|
|
312
320
|
if module._diff:
|
|
313
|
-
diff['before'] =
|
|
321
|
+
diff['before'] = ''.join(lines)
|
|
314
322
|
|
|
315
323
|
if regexp is not None:
|
|
316
|
-
|
|
324
|
+
re_m = re.compile(regexp)
|
|
317
325
|
|
|
318
326
|
if insertafter not in (None, 'BOF', 'EOF'):
|
|
319
|
-
|
|
327
|
+
re_ins = re.compile(insertafter)
|
|
320
328
|
elif insertbefore not in (None, 'BOF'):
|
|
321
|
-
|
|
329
|
+
re_ins = re.compile(insertbefore)
|
|
322
330
|
else:
|
|
323
|
-
|
|
331
|
+
re_ins = None
|
|
324
332
|
|
|
325
333
|
# index[0] is the line num where regexp has been found
|
|
326
334
|
# index[1] is the line num where insertafter/insertbefore has been found
|
|
327
335
|
index = [-1, -1]
|
|
328
336
|
match = None
|
|
329
337
|
exact_line_match = False
|
|
330
|
-
b_line = to_bytes(line, errors='surrogate_or_strict')
|
|
331
338
|
|
|
332
339
|
# The module's doc says
|
|
333
340
|
# "If regular expressions are passed to both regexp and
|
|
@@ -339,8 +346,8 @@ def present(module, dest, regexp, search_string, line, insertafter, insertbefore
|
|
|
339
346
|
# Given the above:
|
|
340
347
|
# 1. First check that there is no match for regexp:
|
|
341
348
|
if regexp is not None:
|
|
342
|
-
for lineno,
|
|
343
|
-
match_found =
|
|
349
|
+
for lineno, cur_line in enumerate(lines):
|
|
350
|
+
match_found = re_m.search(cur_line)
|
|
344
351
|
if match_found:
|
|
345
352
|
index[0] = lineno
|
|
346
353
|
match = match_found
|
|
@@ -349,8 +356,8 @@ def present(module, dest, regexp, search_string, line, insertafter, insertbefore
|
|
|
349
356
|
|
|
350
357
|
# 2. Second check that there is no match for search_string:
|
|
351
358
|
if search_string is not None:
|
|
352
|
-
for lineno,
|
|
353
|
-
match_found =
|
|
359
|
+
for lineno, cur_line in enumerate(lines):
|
|
360
|
+
match_found = search_string in cur_line
|
|
354
361
|
if match_found:
|
|
355
362
|
index[0] = lineno
|
|
356
363
|
match = match_found
|
|
@@ -360,12 +367,12 @@ def present(module, dest, regexp, search_string, line, insertafter, insertbefore
|
|
|
360
367
|
# 3. When no match found on the previous step,
|
|
361
368
|
# parse for searching insertafter/insertbefore:
|
|
362
369
|
if not match:
|
|
363
|
-
for lineno,
|
|
364
|
-
if
|
|
370
|
+
for lineno, cur_line in enumerate(lines):
|
|
371
|
+
if line == cur_line.rstrip('\r\n'):
|
|
365
372
|
index[0] = lineno
|
|
366
373
|
exact_line_match = True
|
|
367
374
|
|
|
368
|
-
elif
|
|
375
|
+
elif re_ins is not None and re_ins.search(cur_line):
|
|
369
376
|
if insertafter:
|
|
370
377
|
# + 1 for the next line
|
|
371
378
|
index[1] = lineno + 1
|
|
@@ -380,17 +387,17 @@ def present(module, dest, regexp, search_string, line, insertafter, insertbefore
|
|
|
380
387
|
|
|
381
388
|
msg = ''
|
|
382
389
|
changed = False
|
|
383
|
-
|
|
390
|
+
linesep = os.linesep
|
|
384
391
|
# Exact line or Regexp matched a line in the file
|
|
385
392
|
if index[0] != -1:
|
|
386
393
|
if backrefs and match:
|
|
387
|
-
|
|
394
|
+
new_line = match.expand(line)
|
|
388
395
|
else:
|
|
389
396
|
# Don't do backref expansion if not asked.
|
|
390
|
-
|
|
397
|
+
new_line = line
|
|
391
398
|
|
|
392
|
-
if not
|
|
393
|
-
|
|
399
|
+
if not new_line.endswith(linesep):
|
|
400
|
+
new_line += linesep
|
|
394
401
|
|
|
395
402
|
# If no regexp or search_string was given and no line match is found anywhere in the file,
|
|
396
403
|
# insert the line appropriately if using insertbefore or insertafter
|
|
@@ -400,18 +407,18 @@ def present(module, dest, regexp, search_string, line, insertafter, insertbefore
|
|
|
400
407
|
if insertafter and insertafter != 'EOF':
|
|
401
408
|
# Ensure there is a line separator after the found string
|
|
402
409
|
# at the end of the file.
|
|
403
|
-
if
|
|
404
|
-
|
|
410
|
+
if lines and not lines[-1][-1:] in ('\n', '\r'):
|
|
411
|
+
lines[-1] = lines[-1] + linesep
|
|
405
412
|
|
|
406
413
|
# If the line to insert after is at the end of the file
|
|
407
414
|
# use the appropriate index value.
|
|
408
|
-
if len(
|
|
409
|
-
if
|
|
410
|
-
|
|
415
|
+
if len(lines) == index[1]:
|
|
416
|
+
if lines[index[1] - 1].rstrip('\r\n') != line:
|
|
417
|
+
lines.append(line + linesep)
|
|
411
418
|
msg = 'line added'
|
|
412
419
|
changed = True
|
|
413
|
-
elif
|
|
414
|
-
|
|
420
|
+
elif lines[index[1]].rstrip('\r\n') != line:
|
|
421
|
+
lines.insert(index[1], line + linesep)
|
|
415
422
|
msg = 'line added'
|
|
416
423
|
changed = True
|
|
417
424
|
|
|
@@ -419,18 +426,18 @@ def present(module, dest, regexp, search_string, line, insertafter, insertbefore
|
|
|
419
426
|
# If the line to insert before is at the beginning of the file
|
|
420
427
|
# use the appropriate index value.
|
|
421
428
|
if index[1] <= 0:
|
|
422
|
-
if
|
|
423
|
-
|
|
429
|
+
if lines[index[1]].rstrip('\r\n') != line:
|
|
430
|
+
lines.insert(index[1], line + linesep)
|
|
424
431
|
msg = 'line added'
|
|
425
432
|
changed = True
|
|
426
433
|
|
|
427
|
-
elif
|
|
428
|
-
|
|
434
|
+
elif lines[index[1] - 1].rstrip('\r\n') != line:
|
|
435
|
+
lines.insert(index[1], line + linesep)
|
|
429
436
|
msg = 'line added'
|
|
430
437
|
changed = True
|
|
431
438
|
|
|
432
|
-
elif
|
|
433
|
-
|
|
439
|
+
elif lines[index[0]] != new_line:
|
|
440
|
+
lines[index[0]] = new_line
|
|
434
441
|
msg = 'line replaced'
|
|
435
442
|
changed = True
|
|
436
443
|
|
|
@@ -440,7 +447,7 @@ def present(module, dest, regexp, search_string, line, insertafter, insertbefore
|
|
|
440
447
|
pass
|
|
441
448
|
# Add it to the beginning of the file
|
|
442
449
|
elif insertbefore == 'BOF' or insertafter == 'BOF':
|
|
443
|
-
|
|
450
|
+
lines.insert(0, line + linesep)
|
|
444
451
|
msg = 'line added'
|
|
445
452
|
changed = True
|
|
446
453
|
# Add it to the end of the file if requested or
|
|
@@ -449,10 +456,10 @@ def present(module, dest, regexp, search_string, line, insertafter, insertbefore
|
|
|
449
456
|
elif insertafter == 'EOF' or index[1] == -1:
|
|
450
457
|
|
|
451
458
|
# If the file is not empty then ensure there's a newline before the added line
|
|
452
|
-
if
|
|
453
|
-
|
|
459
|
+
if lines and not lines[-1][-1:] in ('\n', '\r'):
|
|
460
|
+
lines.append(linesep)
|
|
454
461
|
|
|
455
|
-
|
|
462
|
+
lines.append(line + linesep)
|
|
456
463
|
msg = 'line added'
|
|
457
464
|
changed = True
|
|
458
465
|
|
|
@@ -460,30 +467,30 @@ def present(module, dest, regexp, search_string, line, insertafter, insertbefore
|
|
|
460
467
|
|
|
461
468
|
# Don't insert the line if it already matches at the index.
|
|
462
469
|
# If the line to insert after is at the end of the file use the appropriate index value.
|
|
463
|
-
if len(
|
|
464
|
-
if
|
|
465
|
-
|
|
470
|
+
if len(lines) == index[1]:
|
|
471
|
+
if lines[index[1] - 1].rstrip('\r\n') != line:
|
|
472
|
+
lines.append(line + linesep)
|
|
466
473
|
msg = 'line added'
|
|
467
474
|
changed = True
|
|
468
|
-
elif
|
|
469
|
-
|
|
475
|
+
elif line != lines[index[1]].rstrip('\n\r'):
|
|
476
|
+
lines.insert(index[1], line + linesep)
|
|
470
477
|
msg = 'line added'
|
|
471
478
|
changed = True
|
|
472
479
|
|
|
473
480
|
# insert matched, but not the regexp or search_string
|
|
474
481
|
else:
|
|
475
|
-
|
|
482
|
+
lines.insert(index[1], line + linesep)
|
|
476
483
|
msg = 'line added'
|
|
477
484
|
changed = True
|
|
478
485
|
|
|
479
486
|
if module._diff:
|
|
480
|
-
diff['after'] =
|
|
487
|
+
diff['after'] = ''.join(lines)
|
|
481
488
|
|
|
482
489
|
backupdest = ""
|
|
483
490
|
if changed and not module.check_mode:
|
|
484
491
|
if backup and os.path.exists(b_dest):
|
|
485
492
|
backupdest = module.backup_local(dest)
|
|
486
|
-
write_changes(module,
|
|
493
|
+
write_changes(module, lines, dest, encoding)
|
|
487
494
|
|
|
488
495
|
if module.check_mode and not os.path.exists(b_dest):
|
|
489
496
|
module.exit_json(changed=changed, msg=msg, backup=backupdest, diff=diff)
|
|
@@ -510,40 +517,40 @@ def absent(module, dest, regexp, search_string, line, backup):
|
|
|
510
517
|
'before_header': '%s (content)' % dest,
|
|
511
518
|
'after_header': '%s (content)' % dest}
|
|
512
519
|
|
|
513
|
-
|
|
514
|
-
|
|
520
|
+
encoding = module.params['encoding']
|
|
521
|
+
|
|
522
|
+
with open(b_dest, 'r', encoding=encoding) as f:
|
|
523
|
+
lines = f.readlines()
|
|
515
524
|
|
|
516
525
|
if module._diff:
|
|
517
|
-
diff['before'] =
|
|
526
|
+
diff['before'] = ''.join(lines)
|
|
518
527
|
|
|
519
528
|
if regexp is not None:
|
|
520
|
-
|
|
529
|
+
re_c = re.compile(regexp)
|
|
521
530
|
found = []
|
|
522
531
|
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
def matcher(b_cur_line):
|
|
532
|
+
def matcher(cur_line):
|
|
526
533
|
if regexp is not None:
|
|
527
|
-
match_found =
|
|
534
|
+
match_found = re_c.search(cur_line)
|
|
528
535
|
elif search_string is not None:
|
|
529
|
-
match_found =
|
|
536
|
+
match_found = search_string in cur_line
|
|
530
537
|
else:
|
|
531
|
-
match_found =
|
|
538
|
+
match_found = line == cur_line.rstrip('\r\n')
|
|
532
539
|
if match_found:
|
|
533
|
-
found.append(
|
|
540
|
+
found.append(cur_line)
|
|
534
541
|
return not match_found
|
|
535
542
|
|
|
536
|
-
|
|
543
|
+
lines = [l for l in lines if matcher(l)]
|
|
537
544
|
changed = len(found) > 0
|
|
538
545
|
|
|
539
546
|
if module._diff:
|
|
540
|
-
diff['after'] =
|
|
547
|
+
diff['after'] = ''.join(lines)
|
|
541
548
|
|
|
542
549
|
backupdest = ""
|
|
543
550
|
if changed and not module.check_mode:
|
|
544
551
|
if backup:
|
|
545
552
|
backupdest = module.backup_local(dest)
|
|
546
|
-
write_changes(module,
|
|
553
|
+
write_changes(module, lines, dest, encoding)
|
|
547
554
|
|
|
548
555
|
if changed:
|
|
549
556
|
msg = "%s line(s) removed" % len(found)
|
|
@@ -567,6 +574,7 @@ def main():
|
|
|
567
574
|
regexp=dict(type='str', aliases=['regex']),
|
|
568
575
|
search_string=dict(type='str'),
|
|
569
576
|
line=dict(type='str', aliases=['value']),
|
|
577
|
+
encoding=dict(type='str', default='utf-8'),
|
|
570
578
|
insertafter=dict(type='str'),
|
|
571
579
|
insertbefore=dict(type='str'),
|
|
572
580
|
backrefs=dict(type='bool', default=False),
|
ansible/modules/package_facts.py
CHANGED
|
@@ -18,7 +18,7 @@ options:
|
|
|
18
18
|
This is a list and can support multiple package managers per system, since version 2.8.
|
|
19
19
|
- The V(portage) and V(pkg) options were added in version 2.8.
|
|
20
20
|
- The V(apk) option was added in version 2.11.
|
|
21
|
-
- The V(pkg_info)
|
|
21
|
+
- The V(pkg_info) option was added in version 2.13.
|
|
22
22
|
- Aliases were added in 2.18, to support using C(manager={{ansible_facts['pkg_mgr']}})
|
|
23
23
|
default: ['auto']
|
|
24
24
|
choices:
|
ansible/modules/pip.py
CHANGED
|
@@ -608,7 +608,7 @@ def setup_virtualenv(module, env, chdir, out, err):
|
|
|
608
608
|
err += err_venv
|
|
609
609
|
if rc != 0:
|
|
610
610
|
_fail(module, cmd, out, err)
|
|
611
|
-
return out, err
|
|
611
|
+
return out, err, cmd
|
|
612
612
|
|
|
613
613
|
|
|
614
614
|
class Package:
|
|
@@ -741,11 +741,12 @@ def main():
|
|
|
741
741
|
|
|
742
742
|
err = ''
|
|
743
743
|
out = ''
|
|
744
|
+
venv_cmd = ''
|
|
744
745
|
|
|
745
746
|
if env:
|
|
746
747
|
if not os.path.exists(os.path.join(env, 'bin', 'activate')):
|
|
747
748
|
venv_created = True
|
|
748
|
-
out, err = setup_virtualenv(module, env, chdir, out, err)
|
|
749
|
+
out, err, venv_cmd = setup_virtualenv(module, env, chdir, out, err)
|
|
749
750
|
py_bin = os.path.join(env, 'bin', 'python')
|
|
750
751
|
else:
|
|
751
752
|
py_bin = module.params['executable'] or sys.executable
|
|
@@ -811,6 +812,11 @@ def main():
|
|
|
811
812
|
cmd.extend(to_native(p) for p in packages)
|
|
812
813
|
elif requirements:
|
|
813
814
|
cmd.extend(['-r', requirements])
|
|
815
|
+
elif venv_created and not name and not requirements:
|
|
816
|
+
# ONLY creating an empty venv
|
|
817
|
+
module.exit_json(changed=venv_created, cmd=venv_cmd, name=name, version=version,
|
|
818
|
+
state=state, requirements=requirements, virtualenv=env,
|
|
819
|
+
stdout=out, stderr=err)
|
|
814
820
|
else:
|
|
815
821
|
module.warn("No valid name or requirements file found.")
|
|
816
822
|
module.exit_json(changed=False)
|
ansible/modules/replace.py
CHANGED
|
@@ -183,14 +183,14 @@ import os
|
|
|
183
183
|
import re
|
|
184
184
|
import tempfile
|
|
185
185
|
|
|
186
|
-
from ansible.module_utils.common.text.converters import to_text
|
|
186
|
+
from ansible.module_utils.common.text.converters import to_text
|
|
187
187
|
from ansible.module_utils.basic import AnsibleModule
|
|
188
188
|
|
|
189
189
|
|
|
190
|
-
def write_changes(module, contents, path):
|
|
190
|
+
def write_changes(module, contents, path, encoding='utf-8'):
|
|
191
191
|
|
|
192
192
|
tmpfd, tmpfile = tempfile.mkstemp(dir=module.tmpdir)
|
|
193
|
-
with os.fdopen(tmpfd, '
|
|
193
|
+
with os.fdopen(tmpfd, 'w', encoding=encoding) as f:
|
|
194
194
|
f.write(contents)
|
|
195
195
|
|
|
196
196
|
validate = module.params.get('validate', None)
|
|
@@ -254,8 +254,8 @@ def main():
|
|
|
254
254
|
module.fail_json(rc=257, msg='Path %s does not exist !' % path)
|
|
255
255
|
else:
|
|
256
256
|
try:
|
|
257
|
-
with open(path, '
|
|
258
|
-
contents =
|
|
257
|
+
with open(path, 'r', encoding=encoding) as f:
|
|
258
|
+
contents = f.read()
|
|
259
259
|
except OSError as ex:
|
|
260
260
|
raise Exception(f"Unable to read the contents of {path!r}.") from ex
|
|
261
261
|
|
|
@@ -307,7 +307,7 @@ def main():
|
|
|
307
307
|
res_args['backup_file'] = module.backup_local(path)
|
|
308
308
|
# We should always follow symlinks so that we change the real file
|
|
309
309
|
path = os.path.realpath(path)
|
|
310
|
-
write_changes(module,
|
|
310
|
+
write_changes(module, result[0], path, encoding=encoding)
|
|
311
311
|
|
|
312
312
|
res_args['msg'], res_args['changed'] = check_file_attrs(module, changed, msg)
|
|
313
313
|
module.exit_json(**res_args)
|
ansible/modules/service.py
CHANGED
|
@@ -180,7 +180,6 @@ from ansible.module_utils.basic import AnsibleModule
|
|
|
180
180
|
from ansible.module_utils.common.locale import get_best_parsable_locale
|
|
181
181
|
from ansible.module_utils.common.sys_info import get_platform_subclass
|
|
182
182
|
from ansible.module_utils.service import fail_if_missing, is_systemd_managed
|
|
183
|
-
from ansible.module_utils.six import b
|
|
184
183
|
|
|
185
184
|
|
|
186
185
|
class Service(object):
|
|
@@ -292,8 +291,8 @@ class Service(object):
|
|
|
292
291
|
# chkconfig localizes messages and we're screen scraping so make
|
|
293
292
|
# sure we use the C locale
|
|
294
293
|
p = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=lang_env, preexec_fn=lambda: os.close(pipe[1]))
|
|
295
|
-
stdout = b
|
|
296
|
-
stderr = b
|
|
294
|
+
stdout = b""
|
|
295
|
+
stderr = b""
|
|
297
296
|
fds = [p.stdout, p.stderr]
|
|
298
297
|
# Wait for all output, or until the main process is dead and its output is done.
|
|
299
298
|
while fds:
|
|
@@ -322,7 +321,7 @@ class Service(object):
|
|
|
322
321
|
os.close(pipe[1])
|
|
323
322
|
os.waitpid(pid, 0)
|
|
324
323
|
# Wait for data from daemon process and process it.
|
|
325
|
-
data = b
|
|
324
|
+
data = b""
|
|
326
325
|
while True:
|
|
327
326
|
rfd, wfd, efd = select.select([pipe[0]], [], [pipe[0]])
|
|
328
327
|
if pipe[0] in rfd:
|
ansible/modules/stat.py
CHANGED
|
@@ -44,6 +44,14 @@ options:
|
|
|
44
44
|
version_added: "2.3"
|
|
45
45
|
get_checksum:
|
|
46
46
|
version_added: "1.8"
|
|
47
|
+
get_selinux_context:
|
|
48
|
+
description:
|
|
49
|
+
- Get file SELinux context in a list V([user, role, type, range]),
|
|
50
|
+
and will get V([None, None, None, None]) if it is not possible to retrieve the context,
|
|
51
|
+
either because it does not exist or some other issue.
|
|
52
|
+
type: bool
|
|
53
|
+
default: no
|
|
54
|
+
version_added: '2.20'
|
|
47
55
|
extends_documentation_fragment:
|
|
48
56
|
- action_common_attributes
|
|
49
57
|
- checksum_common
|
|
@@ -346,6 +354,12 @@ stat:
|
|
|
346
354
|
type: list
|
|
347
355
|
sample: [ immutable, extent ]
|
|
348
356
|
version_added: 2.3
|
|
357
|
+
selinux_context:
|
|
358
|
+
description: The SELinux context of a path
|
|
359
|
+
returned: success, path exists and user can execute the path
|
|
360
|
+
type: list
|
|
361
|
+
sample: [ user, role, type, range ]
|
|
362
|
+
version_added: '2.20'
|
|
349
363
|
version:
|
|
350
364
|
description: The version/generation attribute of a file according to the filesystem
|
|
351
365
|
returned: success, path exists, user can execute the path, lsattr is available and filesystem supports
|
|
@@ -434,6 +448,7 @@ def main():
|
|
|
434
448
|
get_checksum=dict(type='bool', default=True),
|
|
435
449
|
get_mime=dict(type='bool', default=True, aliases=['mime', 'mime_type', 'mime-type']),
|
|
436
450
|
get_attributes=dict(type='bool', default=True, aliases=['attr', 'attributes']),
|
|
451
|
+
get_selinux_context=dict(type='bool', default=False),
|
|
437
452
|
checksum_algorithm=dict(type='str', default='sha1',
|
|
438
453
|
choices=['md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512'],
|
|
439
454
|
aliases=['checksum', 'checksum_algo']),
|
|
@@ -448,6 +463,7 @@ def main():
|
|
|
448
463
|
get_attr = module.params.get('get_attributes')
|
|
449
464
|
get_checksum = module.params.get('get_checksum')
|
|
450
465
|
checksum_algorithm = module.params.get('checksum_algorithm')
|
|
466
|
+
get_selinux_context = module.params.get('get_selinux_context')
|
|
451
467
|
|
|
452
468
|
# main stat data
|
|
453
469
|
try:
|
|
@@ -515,6 +531,10 @@ def main():
|
|
|
515
531
|
if x in out:
|
|
516
532
|
output[x] = out[x]
|
|
517
533
|
|
|
534
|
+
# try to get SELinux context
|
|
535
|
+
if get_selinux_context:
|
|
536
|
+
output['selinux_context'] = module.selinux_context(b_path)
|
|
537
|
+
|
|
518
538
|
module.exit_json(changed=False, stat=output)
|
|
519
539
|
|
|
520
540
|
|