ansible-core 2.16.5rc1__py3-none-any.whl → 2.16.7__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/cli/config.py +5 -3
- ansible/cli/inventory.py +1 -21
- ansible/config/manager.py +12 -11
- ansible/constants.py +40 -0
- ansible/executor/play_iterator.py +1 -1
- ansible/executor/task_executor.py +8 -2
- ansible/galaxy/role.py +10 -12
- ansible/module_utils/ansible_release.py +1 -1
- ansible/module_utils/facts/virtual/linux.py +1 -1
- ansible/modules/blockinfile.py +1 -1
- ansible/modules/dnf.py +25 -143
- ansible/modules/dnf5.py +42 -21
- ansible/modules/find.py +7 -4
- ansible/modules/unarchive.py +1 -1
- ansible/modules/uri.py +8 -7
- ansible/modules/user.py +0 -6
- ansible/parsing/mod_args.py +8 -4
- ansible/playbook/role/__init__.py +1 -1
- ansible/plugins/action/__init__.py +1 -1
- ansible/plugins/action/fetch.py +4 -0
- ansible/plugins/cache/__init__.py +1 -0
- ansible/plugins/connection/psrp.py +1 -1
- ansible/plugins/connection/winrm.py +14 -2
- ansible/plugins/lookup/url.py +9 -1
- ansible/plugins/strategy/free.py +1 -1
- ansible/plugins/strategy/linear.py +1 -1
- ansible/release.py +1 -1
- ansible/template/__init__.py +10 -10
- ansible/vars/hostvars.py +6 -22
- {ansible_core-2.16.5rc1.dist-info → ansible_core-2.16.7.dist-info}/METADATA +1 -1
- {ansible_core-2.16.5rc1.dist-info → ansible_core-2.16.7.dist-info}/RECORD +40 -40
- ansible_test/_data/requirements/constraints.txt +1 -0
- ansible_test/_internal/commands/sanity/ansible_doc.py +1 -1
- ansible_test/_internal/pypi_proxy.py +8 -1
- ansible_test/_util/target/setup/bootstrap.sh +9 -0
- {ansible_core-2.16.5rc1.data → ansible_core-2.16.7.data}/scripts/ansible-test +0 -0
- {ansible_core-2.16.5rc1.dist-info → ansible_core-2.16.7.dist-info}/COPYING +0 -0
- {ansible_core-2.16.5rc1.dist-info → ansible_core-2.16.7.dist-info}/WHEEL +0 -0
- {ansible_core-2.16.5rc1.dist-info → ansible_core-2.16.7.dist-info}/entry_points.txt +0 -0
- {ansible_core-2.16.5rc1.dist-info → ansible_core-2.16.7.dist-info}/top_level.txt +0 -0
ansible/cli/config.py
CHANGED
|
@@ -270,7 +270,7 @@ class ConfigCLI(CLI):
|
|
|
270
270
|
if not settings[setting].get('description'):
|
|
271
271
|
continue
|
|
272
272
|
|
|
273
|
-
default = settings[setting].get('default', '')
|
|
273
|
+
default = self.config.template_default(settings[setting].get('default', ''), get_constants())
|
|
274
274
|
if subkey == 'env':
|
|
275
275
|
stype = settings[setting].get('type', '')
|
|
276
276
|
if stype == 'boolean':
|
|
@@ -352,7 +352,7 @@ class ConfigCLI(CLI):
|
|
|
352
352
|
if entry['key'] not in seen[entry['section']]:
|
|
353
353
|
seen[entry['section']].append(entry['key'])
|
|
354
354
|
|
|
355
|
-
default = opt.get('default', '')
|
|
355
|
+
default = self.config.template_default(opt.get('default', ''), get_constants())
|
|
356
356
|
if opt.get('type', '') == 'list' and not isinstance(default, string_types):
|
|
357
357
|
# python lists are not valid ini ones
|
|
358
358
|
default = ', '.join(default)
|
|
@@ -414,14 +414,16 @@ class ConfigCLI(CLI):
|
|
|
414
414
|
if context.CLIARGS['format'] == 'display':
|
|
415
415
|
if isinstance(config[setting], Setting):
|
|
416
416
|
# proceed normally
|
|
417
|
+
value = config[setting].value
|
|
417
418
|
if config[setting].origin == 'default':
|
|
418
419
|
color = 'green'
|
|
420
|
+
value = self.config.template_default(value, get_constants())
|
|
419
421
|
elif config[setting].origin == 'REQUIRED':
|
|
420
422
|
# should include '_terms', '_input', etc
|
|
421
423
|
color = 'red'
|
|
422
424
|
else:
|
|
423
425
|
color = 'yellow'
|
|
424
|
-
msg = "%s(%s) = %s" % (setting, config[setting].origin,
|
|
426
|
+
msg = "%s(%s) = %s" % (setting, config[setting].origin, value)
|
|
425
427
|
else:
|
|
426
428
|
color = 'green'
|
|
427
429
|
msg = "%s(%s) = %s" % (setting, 'default', config[setting].get('default'))
|
ansible/cli/inventory.py
CHANGED
|
@@ -25,26 +25,6 @@ from ansible.vars.plugins import get_vars_from_inventory_sources, get_vars_from_
|
|
|
25
25
|
|
|
26
26
|
display = Display()
|
|
27
27
|
|
|
28
|
-
INTERNAL_VARS = frozenset(['ansible_diff_mode',
|
|
29
|
-
'ansible_config_file',
|
|
30
|
-
'ansible_facts',
|
|
31
|
-
'ansible_forks',
|
|
32
|
-
'ansible_inventory_sources',
|
|
33
|
-
'ansible_limit',
|
|
34
|
-
'ansible_playbook_python',
|
|
35
|
-
'ansible_run_tags',
|
|
36
|
-
'ansible_skip_tags',
|
|
37
|
-
'ansible_verbosity',
|
|
38
|
-
'ansible_version',
|
|
39
|
-
'inventory_dir',
|
|
40
|
-
'inventory_file',
|
|
41
|
-
'inventory_hostname',
|
|
42
|
-
'inventory_hostname_short',
|
|
43
|
-
'groups',
|
|
44
|
-
'group_names',
|
|
45
|
-
'omit',
|
|
46
|
-
'playbook_dir', ])
|
|
47
|
-
|
|
48
28
|
|
|
49
29
|
class InventoryCLI(CLI):
|
|
50
30
|
''' used to display or dump the configured inventory as Ansible sees it '''
|
|
@@ -247,7 +227,7 @@ class InventoryCLI(CLI):
|
|
|
247
227
|
@staticmethod
|
|
248
228
|
def _remove_internal(dump):
|
|
249
229
|
|
|
250
|
-
for internal in
|
|
230
|
+
for internal in C.INTERNAL_STATIC_VARS:
|
|
251
231
|
if internal in dump:
|
|
252
232
|
del dump[internal]
|
|
253
233
|
|
ansible/config/manager.py
CHANGED
|
@@ -305,6 +305,17 @@ class ConfigManager(object):
|
|
|
305
305
|
# ensure we always have config def entry
|
|
306
306
|
self._base_defs['CONFIG_FILE'] = {'default': None, 'type': 'path'}
|
|
307
307
|
|
|
308
|
+
def template_default(self, value, variables):
|
|
309
|
+
if isinstance(value, string_types) and (value.startswith('{{') and value.endswith('}}')) and variables is not None:
|
|
310
|
+
# template default values if possible
|
|
311
|
+
# NOTE: cannot use is_template due to circular dep
|
|
312
|
+
try:
|
|
313
|
+
t = NativeEnvironment().from_string(value)
|
|
314
|
+
value = t.render(variables)
|
|
315
|
+
except Exception:
|
|
316
|
+
pass # not templatable
|
|
317
|
+
return value
|
|
318
|
+
|
|
308
319
|
def _read_config_yaml_file(self, yml_file):
|
|
309
320
|
# TODO: handle relative paths as relative to the directory containing the current playbook instead of CWD
|
|
310
321
|
# Currently this is only used with absolute paths to the `ansible/config` directory
|
|
@@ -548,17 +559,7 @@ class ConfigManager(object):
|
|
|
548
559
|
to_native(_get_entry(plugin_type, plugin_name, config)))
|
|
549
560
|
else:
|
|
550
561
|
origin = 'default'
|
|
551
|
-
value = defs[config].get('default')
|
|
552
|
-
if isinstance(value, string_types) and (value.startswith('{{') and value.endswith('}}')) and variables is not None:
|
|
553
|
-
# template default values if possible
|
|
554
|
-
# NOTE: cannot use is_template due to circular dep
|
|
555
|
-
try:
|
|
556
|
-
t = NativeEnvironment().from_string(value)
|
|
557
|
-
value = t.render(variables)
|
|
558
|
-
except Exception:
|
|
559
|
-
pass # not templatable
|
|
560
|
-
|
|
561
|
-
# ensure correct type, can raise exceptions on mismatched types
|
|
562
|
+
value = self.template_default(defs[config].get('default'), variables)
|
|
562
563
|
try:
|
|
563
564
|
value = ensure_type(value, defs[config].get('type'), origin=origin)
|
|
564
565
|
except ValueError as e:
|
ansible/constants.py
CHANGED
|
@@ -112,6 +112,46 @@ CONFIGURABLE_PLUGINS = ('become', 'cache', 'callback', 'cliconf', 'connection',
|
|
|
112
112
|
DOCUMENTABLE_PLUGINS = CONFIGURABLE_PLUGINS + ('module', 'strategy', 'test', 'filter')
|
|
113
113
|
IGNORE_FILES = ("COPYING", "CONTRIBUTING", "LICENSE", "README", "VERSION", "GUIDELINES", "MANIFEST", "Makefile") # ignore during module search
|
|
114
114
|
INTERNAL_RESULT_KEYS = ('add_host', 'add_group')
|
|
115
|
+
INTERNAL_STATIC_VARS = frozenset(
|
|
116
|
+
[
|
|
117
|
+
"ansible_async_path",
|
|
118
|
+
"ansible_collection_name",
|
|
119
|
+
"ansible_config_file",
|
|
120
|
+
"ansible_dependent_role_names",
|
|
121
|
+
"ansible_diff_mode",
|
|
122
|
+
"ansible_config_file",
|
|
123
|
+
"ansible_facts",
|
|
124
|
+
"ansible_forks",
|
|
125
|
+
"ansible_inventory_sources",
|
|
126
|
+
"ansible_limit",
|
|
127
|
+
"ansible_play_batch",
|
|
128
|
+
"ansible_play_hosts",
|
|
129
|
+
"ansible_play_hosts_all",
|
|
130
|
+
"ansible_play_role_names",
|
|
131
|
+
"ansible_playbook_python",
|
|
132
|
+
"ansible_role_name",
|
|
133
|
+
"ansible_role_names",
|
|
134
|
+
"ansible_run_tags",
|
|
135
|
+
"ansible_skip_tags",
|
|
136
|
+
"ansible_verbosity",
|
|
137
|
+
"ansible_version",
|
|
138
|
+
"inventory_dir",
|
|
139
|
+
"inventory_file",
|
|
140
|
+
"inventory_hostname",
|
|
141
|
+
"inventory_hostname_short",
|
|
142
|
+
"groups",
|
|
143
|
+
"group_names",
|
|
144
|
+
"omit",
|
|
145
|
+
"hostvars",
|
|
146
|
+
"playbook_dir",
|
|
147
|
+
"play_hosts",
|
|
148
|
+
"role_name",
|
|
149
|
+
"role_names",
|
|
150
|
+
"role_path",
|
|
151
|
+
"role_uuid",
|
|
152
|
+
"role_names",
|
|
153
|
+
]
|
|
154
|
+
)
|
|
115
155
|
LOCALHOST = ('127.0.0.1', 'localhost', '::1')
|
|
116
156
|
MODULE_REQUIRE_ARGS = tuple(add_internal_fqcns(('command', 'win_command', 'ansible.windows.win_command', 'shell', 'win_shell',
|
|
117
157
|
'ansible.windows.win_shell', 'raw', 'script')))
|
|
@@ -429,13 +429,13 @@ class PlayIterator:
|
|
|
429
429
|
# might be there from previous flush
|
|
430
430
|
state.handlers = self.handlers[:]
|
|
431
431
|
state.update_handlers = False
|
|
432
|
-
state.cur_handlers_task = 0
|
|
433
432
|
|
|
434
433
|
while True:
|
|
435
434
|
try:
|
|
436
435
|
task = state.handlers[state.cur_handlers_task]
|
|
437
436
|
except IndexError:
|
|
438
437
|
task = None
|
|
438
|
+
state.cur_handlers_task = 0
|
|
439
439
|
state.run_state = state.pre_flushing_run_state
|
|
440
440
|
state.update_handlers = True
|
|
441
441
|
break
|
|
@@ -841,7 +841,12 @@ class TaskExecutor:
|
|
|
841
841
|
# that (with a sleep for "poll" seconds between each retry) until the
|
|
842
842
|
# async time limit is exceeded.
|
|
843
843
|
|
|
844
|
-
async_task = Task.load(dict(
|
|
844
|
+
async_task = Task.load(dict(
|
|
845
|
+
action='async_status',
|
|
846
|
+
args={'jid': async_jid},
|
|
847
|
+
check_mode=self._task.check_mode,
|
|
848
|
+
environment=self._task.environment,
|
|
849
|
+
))
|
|
845
850
|
|
|
846
851
|
# FIXME: this is no longer the case, normal takes care of all, see if this can just be generalized
|
|
847
852
|
# Because this is an async task, the action handler is async. However,
|
|
@@ -913,6 +918,7 @@ class TaskExecutor:
|
|
|
913
918
|
'jid': async_jid,
|
|
914
919
|
'mode': 'cleanup',
|
|
915
920
|
},
|
|
921
|
+
'check_mode': self._task.check_mode,
|
|
916
922
|
'environment': self._task.environment,
|
|
917
923
|
}
|
|
918
924
|
)
|
|
@@ -1086,7 +1092,7 @@ class TaskExecutor:
|
|
|
1086
1092
|
|
|
1087
1093
|
# deals with networking sub_plugins (network_cli/httpapi/netconf)
|
|
1088
1094
|
sub = getattr(self._connection, '_sub_plugin', None)
|
|
1089
|
-
if sub
|
|
1095
|
+
if sub and sub.get('type') != 'external':
|
|
1090
1096
|
plugin_type = get_plugin_class(sub.get("obj"))
|
|
1091
1097
|
varnames.extend(self._set_plugin_options(plugin_type, variables, templar, task_keys))
|
|
1092
1098
|
sub_conn = getattr(self._connection, 'ssh_type_conn', None)
|
ansible/galaxy/role.py
CHANGED
|
@@ -387,6 +387,8 @@ class GalaxyRole(object):
|
|
|
387
387
|
else:
|
|
388
388
|
os.makedirs(self.path)
|
|
389
389
|
|
|
390
|
+
resolved_archive = unfrackpath(archive_parent_dir, follow=False)
|
|
391
|
+
|
|
390
392
|
# We strip off any higher-level directories for all of the files
|
|
391
393
|
# contained within the tar file here. The default is 'github_repo-target'.
|
|
392
394
|
# Gerrit instances, on the other hand, does not have a parent directory at all.
|
|
@@ -401,33 +403,29 @@ class GalaxyRole(object):
|
|
|
401
403
|
if not (attr_value := getattr(member, attr, None)):
|
|
402
404
|
continue
|
|
403
405
|
|
|
404
|
-
if attr_value.startswith(os.sep) and not is_subpath(attr_value, archive_parent_dir):
|
|
405
|
-
err = f"Invalid {attr} for tarfile member: path {attr_value} is not a subpath of the role {archive_parent_dir}"
|
|
406
|
-
raise AnsibleError(err)
|
|
407
|
-
|
|
408
406
|
if attr == 'linkname':
|
|
409
407
|
# Symlinks are relative to the link
|
|
410
|
-
|
|
411
|
-
archive_dir_path = os.path.join(archive_parent_dir, relative_to_archive_dir, attr_value)
|
|
408
|
+
relative_to = os.path.dirname(getattr(member, 'name', ''))
|
|
412
409
|
else:
|
|
413
410
|
# Normalize paths that start with the archive dir
|
|
414
411
|
attr_value = attr_value.replace(archive_parent_dir, "", 1)
|
|
415
412
|
attr_value = os.path.join(*attr_value.split(os.sep)) # remove leading os.sep
|
|
416
|
-
|
|
413
|
+
relative_to = ''
|
|
417
414
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
err = f"Invalid {attr} for tarfile member: path {resolved_path} is not a subpath of the role {resolved_archive}"
|
|
415
|
+
full_path = os.path.join(resolved_archive, relative_to, attr_value)
|
|
416
|
+
if not is_subpath(full_path, resolved_archive, real=True):
|
|
417
|
+
err = f"Invalid {attr} for tarfile member: path {full_path} is not a subpath of the role {resolved_archive}"
|
|
422
418
|
raise AnsibleError(err)
|
|
423
419
|
|
|
424
|
-
|
|
420
|
+
relative_path_dir = os.path.join(resolved_archive, relative_to)
|
|
421
|
+
relative_path = os.path.join(*full_path.replace(relative_path_dir, "", 1).split(os.sep))
|
|
425
422
|
setattr(member, attr, relative_path)
|
|
426
423
|
|
|
427
424
|
if _check_working_data_filter():
|
|
428
425
|
# deprecated: description='extract fallback without filter' python_version='3.11'
|
|
429
426
|
role_tar_file.extract(member, to_native(self.path), filter='data') # type: ignore[call-arg]
|
|
430
427
|
else:
|
|
428
|
+
# Remove along with manual path filter once Python 3.12 is minimum supported version
|
|
431
429
|
role_tar_file.extract(member, to_native(self.path))
|
|
432
430
|
|
|
433
431
|
# write out the install info file for later use
|
|
@@ -176,7 +176,7 @@ class LinuxVirtual(Virtual):
|
|
|
176
176
|
virtual_facts['virtualization_type'] = 'RHEV'
|
|
177
177
|
found_virt = True
|
|
178
178
|
|
|
179
|
-
if product_name
|
|
179
|
+
if product_name and product_name.startswith(("VMware",)):
|
|
180
180
|
guest_tech.add('VMware')
|
|
181
181
|
if not found_virt:
|
|
182
182
|
virtual_facts['virtualization_type'] = 'VMware'
|
ansible/modules/blockinfile.py
CHANGED
|
@@ -269,7 +269,7 @@ def main():
|
|
|
269
269
|
module.fail_json(rc=257,
|
|
270
270
|
msg='Path %s does not exist !' % path)
|
|
271
271
|
destpath = os.path.dirname(path)
|
|
272
|
-
if not os.path.exists(destpath) and not module.check_mode:
|
|
272
|
+
if destpath and not os.path.exists(destpath) and not module.check_mode:
|
|
273
273
|
try:
|
|
274
274
|
os.makedirs(destpath)
|
|
275
275
|
except OSError as e:
|
ansible/modules/dnf.py
CHANGED
|
@@ -380,7 +380,6 @@ EXAMPLES = '''
|
|
|
380
380
|
'''
|
|
381
381
|
|
|
382
382
|
import os
|
|
383
|
-
import re
|
|
384
383
|
import sys
|
|
385
384
|
|
|
386
385
|
from ansible.module_utils.common.text.converters import to_native, to_text
|
|
@@ -482,94 +481,6 @@ class DnfModule(YumDnf):
|
|
|
482
481
|
|
|
483
482
|
return result
|
|
484
483
|
|
|
485
|
-
def _split_package_arch(self, packagename):
|
|
486
|
-
# This list was auto generated on a Fedora 28 system with the following one-liner
|
|
487
|
-
# printf '[ '; for arch in $(ls /usr/lib/rpm/platform); do printf '"%s", ' ${arch%-linux}; done; printf ']\n'
|
|
488
|
-
redhat_rpm_arches = [
|
|
489
|
-
"aarch64", "alphaev56", "alphaev5", "alphaev67", "alphaev6", "alpha",
|
|
490
|
-
"alphapca56", "amd64", "armv3l", "armv4b", "armv4l", "armv5tejl", "armv5tel",
|
|
491
|
-
"armv5tl", "armv6hl", "armv6l", "armv7hl", "armv7hnl", "armv7l", "athlon",
|
|
492
|
-
"geode", "i386", "i486", "i586", "i686", "ia32e", "ia64", "m68k", "mips64el",
|
|
493
|
-
"mips64", "mips64r6el", "mips64r6", "mipsel", "mips", "mipsr6el", "mipsr6",
|
|
494
|
-
"noarch", "pentium3", "pentium4", "ppc32dy4", "ppc64iseries", "ppc64le", "ppc64",
|
|
495
|
-
"ppc64p7", "ppc64pseries", "ppc8260", "ppc8560", "ppciseries", "ppc", "ppcpseries",
|
|
496
|
-
"riscv64", "s390", "s390x", "sh3", "sh4a", "sh4", "sh", "sparc64", "sparc64v",
|
|
497
|
-
"sparc", "sparcv8", "sparcv9", "sparcv9v", "x86_64"
|
|
498
|
-
]
|
|
499
|
-
|
|
500
|
-
name, delimiter, arch = packagename.rpartition('.')
|
|
501
|
-
if name and arch and arch in redhat_rpm_arches:
|
|
502
|
-
return name, arch
|
|
503
|
-
return packagename, None
|
|
504
|
-
|
|
505
|
-
def _packagename_dict(self, packagename):
|
|
506
|
-
"""
|
|
507
|
-
Return a dictionary of information for a package name string or None
|
|
508
|
-
if the package name doesn't contain at least all NVR elements
|
|
509
|
-
"""
|
|
510
|
-
|
|
511
|
-
if packagename[-4:] == '.rpm':
|
|
512
|
-
packagename = packagename[:-4]
|
|
513
|
-
|
|
514
|
-
rpm_nevr_re = re.compile(r'(\S+)-(?:(\d*):)?(.*)-(~?\w+[\w.+]*)')
|
|
515
|
-
try:
|
|
516
|
-
arch = None
|
|
517
|
-
nevr, arch = self._split_package_arch(packagename)
|
|
518
|
-
if arch:
|
|
519
|
-
packagename = nevr
|
|
520
|
-
rpm_nevr_match = rpm_nevr_re.match(packagename)
|
|
521
|
-
if rpm_nevr_match:
|
|
522
|
-
name, epoch, version, release = rpm_nevr_re.match(packagename).groups()
|
|
523
|
-
if not version or not version.split('.')[0].isdigit():
|
|
524
|
-
return None
|
|
525
|
-
else:
|
|
526
|
-
return None
|
|
527
|
-
except AttributeError as e:
|
|
528
|
-
self.module.fail_json(
|
|
529
|
-
msg='Error attempting to parse package: %s, %s' % (packagename, to_native(e)),
|
|
530
|
-
rc=1,
|
|
531
|
-
results=[]
|
|
532
|
-
)
|
|
533
|
-
|
|
534
|
-
if not epoch:
|
|
535
|
-
epoch = "0"
|
|
536
|
-
|
|
537
|
-
if ':' in name:
|
|
538
|
-
epoch_name = name.split(":")
|
|
539
|
-
|
|
540
|
-
epoch = epoch_name[0]
|
|
541
|
-
name = ''.join(epoch_name[1:])
|
|
542
|
-
|
|
543
|
-
result = {
|
|
544
|
-
'name': name,
|
|
545
|
-
'epoch': epoch,
|
|
546
|
-
'release': release,
|
|
547
|
-
'version': version,
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
return result
|
|
551
|
-
|
|
552
|
-
# Original implementation from yum.rpmUtils.miscutils (GPLv2+)
|
|
553
|
-
# http://yum.baseurl.org/gitweb?p=yum.git;a=blob;f=rpmUtils/miscutils.py
|
|
554
|
-
def _compare_evr(self, e1, v1, r1, e2, v2, r2):
|
|
555
|
-
# return 1: a is newer than b
|
|
556
|
-
# 0: a and b are the same version
|
|
557
|
-
# -1: b is newer than a
|
|
558
|
-
if e1 is None:
|
|
559
|
-
e1 = '0'
|
|
560
|
-
else:
|
|
561
|
-
e1 = str(e1)
|
|
562
|
-
v1 = str(v1)
|
|
563
|
-
r1 = str(r1)
|
|
564
|
-
if e2 is None:
|
|
565
|
-
e2 = '0'
|
|
566
|
-
else:
|
|
567
|
-
e2 = str(e2)
|
|
568
|
-
v2 = str(v2)
|
|
569
|
-
r2 = str(r2)
|
|
570
|
-
rc = dnf.rpm.rpm.labelCompare((e1, v1, r1), (e2, v2, r2))
|
|
571
|
-
return rc
|
|
572
|
-
|
|
573
484
|
def _ensure_dnf(self):
|
|
574
485
|
locale = get_best_parsable_locale(self.module)
|
|
575
486
|
os.environ['LC_ALL'] = os.environ['LC_MESSAGES'] = locale
|
|
@@ -578,7 +489,6 @@ class DnfModule(YumDnf):
|
|
|
578
489
|
global dnf
|
|
579
490
|
try:
|
|
580
491
|
import dnf
|
|
581
|
-
import dnf.cli
|
|
582
492
|
import dnf.const
|
|
583
493
|
import dnf.exceptions
|
|
584
494
|
import dnf.package
|
|
@@ -809,43 +719,22 @@ class DnfModule(YumDnf):
|
|
|
809
719
|
self.module.exit_json(msg="", results=results)
|
|
810
720
|
|
|
811
721
|
def _is_installed(self, pkg):
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
name, arch = self._split_package_arch(pkg)
|
|
816
|
-
if arch:
|
|
817
|
-
package_spec['arch'] = arch
|
|
818
|
-
|
|
819
|
-
package_details = self._packagename_dict(pkg)
|
|
820
|
-
if package_details:
|
|
821
|
-
package_details['epoch'] = int(package_details['epoch'])
|
|
822
|
-
package_spec.update(package_details)
|
|
823
|
-
else:
|
|
824
|
-
package_spec['name'] = name
|
|
825
|
-
|
|
826
|
-
return bool(installed.filter(**package_spec))
|
|
722
|
+
return bool(
|
|
723
|
+
dnf.subject.Subject(pkg).get_best_query(sack=self.base.sack).installed().run()
|
|
724
|
+
)
|
|
827
725
|
|
|
828
726
|
def _is_newer_version_installed(self, pkg_name):
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
installed_pkg = installed_pkg[0]
|
|
839
|
-
|
|
840
|
-
# this looks weird but one is a dict and the other is a dnf.Package
|
|
841
|
-
evr_cmp = self._compare_evr(
|
|
842
|
-
installed_pkg.epoch, installed_pkg.version, installed_pkg.release,
|
|
843
|
-
candidate_pkg['epoch'], candidate_pkg['version'], candidate_pkg['release'],
|
|
844
|
-
)
|
|
845
|
-
|
|
846
|
-
return evr_cmp == 1
|
|
847
|
-
else:
|
|
727
|
+
try:
|
|
728
|
+
if isinstance(pkg_name, dnf.package.Package):
|
|
729
|
+
available = pkg_name
|
|
730
|
+
else:
|
|
731
|
+
available = sorted(
|
|
732
|
+
dnf.subject.Subject(pkg_name).get_best_query(sack=self.base.sack).available().run()
|
|
733
|
+
)[-1]
|
|
734
|
+
installed = sorted(self.base.sack.query().installed().filter(name=available.name).run())[-1]
|
|
735
|
+
except IndexError:
|
|
848
736
|
return False
|
|
737
|
+
return installed > available
|
|
849
738
|
|
|
850
739
|
def _mark_package_install(self, pkg_spec, upgrade=False):
|
|
851
740
|
"""Mark the package for install."""
|
|
@@ -917,17 +806,6 @@ class DnfModule(YumDnf):
|
|
|
917
806
|
"results": []
|
|
918
807
|
}
|
|
919
808
|
|
|
920
|
-
def _whatprovides(self, filepath):
|
|
921
|
-
self.base.read_all_repos()
|
|
922
|
-
available = self.base.sack.query().available()
|
|
923
|
-
# Search in file
|
|
924
|
-
files_filter = available.filter(file=filepath)
|
|
925
|
-
# And Search in provides
|
|
926
|
-
pkg_spec = files_filter.union(available.filter(provides=filepath)).run()
|
|
927
|
-
|
|
928
|
-
if pkg_spec:
|
|
929
|
-
return pkg_spec[0].name
|
|
930
|
-
|
|
931
809
|
def _parse_spec_group_file(self):
|
|
932
810
|
pkg_specs, grp_specs, module_specs, filenames = [], [], [], []
|
|
933
811
|
already_loaded_comps = False # Only load this if necessary, it's slow
|
|
@@ -939,11 +817,13 @@ class DnfModule(YumDnf):
|
|
|
939
817
|
elif name.endswith(".rpm"):
|
|
940
818
|
filenames.append(name)
|
|
941
819
|
elif name.startswith('/'):
|
|
942
|
-
#
|
|
943
|
-
|
|
944
|
-
if
|
|
945
|
-
pkg_specs.append(
|
|
946
|
-
|
|
820
|
+
# dnf install /usr/bin/vi
|
|
821
|
+
installed = self.base.sack.query().filter(provides=name, file=name).installed().run()
|
|
822
|
+
if installed:
|
|
823
|
+
pkg_specs.append(installed[0].name) # should be only one?
|
|
824
|
+
elif not self.update_only:
|
|
825
|
+
# not installed, pass the filename for dnf to process
|
|
826
|
+
pkg_specs.append(name)
|
|
947
827
|
elif name.startswith("@") or ('/' in name):
|
|
948
828
|
if not already_loaded_comps:
|
|
949
829
|
self.base.read_comps()
|
|
@@ -1005,7 +885,7 @@ class DnfModule(YumDnf):
|
|
|
1005
885
|
else:
|
|
1006
886
|
for pkg in pkgs:
|
|
1007
887
|
try:
|
|
1008
|
-
if self._is_newer_version_installed(
|
|
888
|
+
if self._is_newer_version_installed(pkg):
|
|
1009
889
|
if self.allow_downgrade:
|
|
1010
890
|
self.base.package_install(pkg, strict=self.base.conf.strict)
|
|
1011
891
|
else:
|
|
@@ -1441,8 +1321,10 @@ class DnfModule(YumDnf):
|
|
|
1441
1321
|
|
|
1442
1322
|
if self.with_modules:
|
|
1443
1323
|
self.module_base = dnf.module.module_base.ModuleBase(self.base)
|
|
1444
|
-
|
|
1445
|
-
|
|
1324
|
+
try:
|
|
1325
|
+
self.ensure()
|
|
1326
|
+
finally:
|
|
1327
|
+
self.base.close()
|
|
1446
1328
|
|
|
1447
1329
|
|
|
1448
1330
|
def main():
|
ansible/modules/dnf5.py
CHANGED
|
@@ -361,19 +361,37 @@ def is_newer_version_installed(base, spec):
|
|
|
361
361
|
spec_nevra = next(iter(libdnf5.rpm.Nevra.parse(spec)))
|
|
362
362
|
except RuntimeError:
|
|
363
363
|
return False
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
if not v or not r:
|
|
364
|
+
|
|
365
|
+
spec_version = spec_nevra.get_version()
|
|
366
|
+
if not spec_version:
|
|
368
367
|
return False
|
|
369
|
-
spec_evr = "{}:{}-{}".format(spec_nevra.get_epoch() or "0", v, r)
|
|
370
368
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
369
|
+
installed = libdnf5.rpm.PackageQuery(base)
|
|
370
|
+
installed.filter_installed()
|
|
371
|
+
installed.filter_name([spec_nevra.get_name()])
|
|
372
|
+
installed.filter_latest_evr()
|
|
373
|
+
try:
|
|
374
|
+
installed_package = list(installed)[-1]
|
|
375
|
+
except IndexError:
|
|
376
|
+
return False
|
|
375
377
|
|
|
376
|
-
|
|
378
|
+
target = libdnf5.rpm.PackageQuery(base)
|
|
379
|
+
target.filter_name([spec_nevra.get_name()])
|
|
380
|
+
target.filter_version([spec_version])
|
|
381
|
+
spec_release = spec_nevra.get_release()
|
|
382
|
+
if spec_release:
|
|
383
|
+
target.filter_release([spec_release])
|
|
384
|
+
spec_epoch = spec_nevra.get_epoch()
|
|
385
|
+
if spec_epoch:
|
|
386
|
+
target.filter_epoch([spec_epoch])
|
|
387
|
+
target.filter_latest_evr()
|
|
388
|
+
try:
|
|
389
|
+
target_package = list(target)[-1]
|
|
390
|
+
except IndexError:
|
|
391
|
+
return False
|
|
392
|
+
|
|
393
|
+
# FIXME https://github.com/rpm-software-management/dnf5/issues/1104
|
|
394
|
+
return libdnf5.rpm.rpmvercmp(installed_package.get_evr(), target_package.get_evr()) == 1
|
|
377
395
|
|
|
378
396
|
|
|
379
397
|
def package_to_dict(package):
|
|
@@ -484,7 +502,7 @@ class Dnf5Module(YumDnf):
|
|
|
484
502
|
conf.config_file_path = self.conf_file
|
|
485
503
|
|
|
486
504
|
try:
|
|
487
|
-
base.
|
|
505
|
+
base.load_config()
|
|
488
506
|
except RuntimeError as e:
|
|
489
507
|
self.module.fail_json(
|
|
490
508
|
msg=str(e),
|
|
@@ -520,7 +538,8 @@ class Dnf5Module(YumDnf):
|
|
|
520
538
|
log_router = base.get_logger()
|
|
521
539
|
global_logger = libdnf5.logger.GlobalLogger()
|
|
522
540
|
global_logger.set(log_router.get(), libdnf5.logger.Logger.Level_DEBUG)
|
|
523
|
-
|
|
541
|
+
# FIXME hardcoding the filename does not seem right, should libdnf5 expose the default file name?
|
|
542
|
+
logger = libdnf5.logger.create_file_logger(base, "dnf5.log")
|
|
524
543
|
log_router.add_logger(logger)
|
|
525
544
|
|
|
526
545
|
if self.update_cache:
|
|
@@ -545,7 +564,11 @@ class Dnf5Module(YumDnf):
|
|
|
545
564
|
for repo in repo_query:
|
|
546
565
|
repo.enable()
|
|
547
566
|
|
|
548
|
-
|
|
567
|
+
try:
|
|
568
|
+
sack.load_repos()
|
|
569
|
+
except AttributeError:
|
|
570
|
+
# dnf5 < 5.2.0.0
|
|
571
|
+
sack.update_and_load_enabled_repos(True)
|
|
549
572
|
|
|
550
573
|
if self.update_cache and not self.names and not self.list:
|
|
551
574
|
self.module.exit_json(
|
|
@@ -577,7 +600,11 @@ class Dnf5Module(YumDnf):
|
|
|
577
600
|
self.module.exit_json(msg="", results=results, rc=0)
|
|
578
601
|
|
|
579
602
|
settings = libdnf5.base.GoalJobSettings()
|
|
580
|
-
|
|
603
|
+
try:
|
|
604
|
+
settings.set_group_with_name(True)
|
|
605
|
+
except AttributeError:
|
|
606
|
+
# dnf5 < 5.2.0.0
|
|
607
|
+
settings.group_with_name = True
|
|
581
608
|
if self.bugfix or self.security:
|
|
582
609
|
advisory_query = libdnf5.advisory.AdvisoryQuery(base)
|
|
583
610
|
types = []
|
|
@@ -597,13 +624,7 @@ class Dnf5Module(YumDnf):
|
|
|
597
624
|
for spec in self.names:
|
|
598
625
|
if is_newer_version_installed(base, spec):
|
|
599
626
|
if self.allow_downgrade:
|
|
600
|
-
|
|
601
|
-
if is_installed(base, spec):
|
|
602
|
-
goal.add_upgrade(spec, settings)
|
|
603
|
-
else:
|
|
604
|
-
goal.add_install(spec, settings)
|
|
605
|
-
else:
|
|
606
|
-
goal.add_install(spec, settings)
|
|
627
|
+
goal.add_install(spec, settings)
|
|
607
628
|
elif is_installed(base, spec):
|
|
608
629
|
if upgrade:
|
|
609
630
|
goal.add_upgrade(spec, settings)
|
ansible/modules/find.py
CHANGED
|
@@ -258,6 +258,7 @@ skipped_paths:
|
|
|
258
258
|
version_added: '2.12'
|
|
259
259
|
'''
|
|
260
260
|
|
|
261
|
+
import errno
|
|
261
262
|
import fnmatch
|
|
262
263
|
import grp
|
|
263
264
|
import os
|
|
@@ -434,10 +435,6 @@ def statinfo(st):
|
|
|
434
435
|
}
|
|
435
436
|
|
|
436
437
|
|
|
437
|
-
def handle_walk_errors(e):
|
|
438
|
-
raise e
|
|
439
|
-
|
|
440
|
-
|
|
441
438
|
def main():
|
|
442
439
|
module = AnsibleModule(
|
|
443
440
|
argument_spec=dict(
|
|
@@ -482,6 +479,12 @@ def main():
|
|
|
482
479
|
filelist = []
|
|
483
480
|
skipped = {}
|
|
484
481
|
|
|
482
|
+
def handle_walk_errors(e):
|
|
483
|
+
if e.errno in (errno.EPERM, errno.EACCES):
|
|
484
|
+
skipped[e.filename] = to_text(e)
|
|
485
|
+
return
|
|
486
|
+
raise e
|
|
487
|
+
|
|
485
488
|
if params['age'] is None:
|
|
486
489
|
age = None
|
|
487
490
|
else:
|
ansible/modules/unarchive.py
CHANGED
|
@@ -969,7 +969,7 @@ class TarZstdArchive(TgzArchive):
|
|
|
969
969
|
class ZipZArchive(ZipArchive):
|
|
970
970
|
def __init__(self, src, b_dest, file_args, module):
|
|
971
971
|
super(ZipZArchive, self).__init__(src, b_dest, file_args, module)
|
|
972
|
-
self.zipinfoflag = '-
|
|
972
|
+
self.zipinfoflag = '-Zl'
|
|
973
973
|
self.binaries = (
|
|
974
974
|
('unzip', 'cmd_path'),
|
|
975
975
|
('unzip', 'zipinfo_cmd_path'),
|