dnf to support modularity module appstream specs

Fixes #48743

Signed-off-by: Adam Miller <admiller@redhat.com>
pull/48899/head
Adam Miller 6 years ago committed by Toshio Kuratomi
parent b4542e429f
commit 4a06e95671

@ -0,0 +1,3 @@
---
minor_changes:
- "dnf properly support modularity appstream installation via overloaded group modifier syntax"

@ -254,6 +254,21 @@ EXAMPLES = '''
name: httpd name: httpd
state: absent state: absent
autoremove: no autoremove: no
- name: install a modularity appstream with defined stream and profile
dnf:
name: @postgresql:9.6/client
state: present
- name: install a modularity appstream with defined stream
dnf:
name: @postgresql:9.6
state: present
- name: install a modularity appstream with defined profile
dnf:
name: @postgresql/client
state: present
''' '''
import os import os
@ -296,6 +311,11 @@ class DnfModule(YumDnf):
self.lockfile = "/var/cache/dnf/*_lock.pid" self.lockfile = "/var/cache/dnf/*_lock.pid"
self.pkg_mgr_name = "dnf" self.pkg_mgr_name = "dnf"
try:
self.with_modules = dnf.base.WITH_MODULES
except AttributeError:
self.with_modules = False
def _sanitize_dnf_error_msg(self, spec, error): def _sanitize_dnf_error_msg(self, spec, error):
""" """
For unhandled dnf.exceptions.Error scenarios, there are certain error For unhandled dnf.exceptions.Error scenarios, there are certain error
@ -592,6 +612,7 @@ class DnfModule(YumDnf):
results=[], results=[],
rc=1 rc=1
) )
return base return base
def list_items(self, command): def list_items(self, command):
@ -720,17 +741,35 @@ class DnfModule(YumDnf):
} }
def _parse_spec_group_file(self): def _parse_spec_group_file(self):
pkg_specs, grp_specs, filenames = [], [], [] pkg_specs, grp_specs, env_specs, module_specs, filenames = [], [], [], [], []
already_loaded_comps = False # Only load this if necessary, it's slow
for name in self.names: for name in self.names:
if name.endswith(".rpm"): if name.endswith(".rpm"):
if '://' in name: if '://' in name:
name = self.fetch_rpm_from_url(name) name = self.fetch_rpm_from_url(name)
filenames.append(name) filenames.append(name)
elif name.startswith("@"): elif name.startswith("@") or ('/' in name):
grp_specs.append(name[1:]) if not already_loaded_comps:
self.base.read_comps()
already_loaded_comps = True
grp_env_mdl_candidate = name[1:].strip()
grp = self.base.comps.group_by_pattern(grp_env_mdl_candidate)
if grp:
grp_specs.append(grp.id)
env = self.base.comps.environment_by_pattern(grp_env_mdl_candidate)
if env:
env_specs.append(env.id)
mdl = self.module_base._get_modules(grp_env_mdl_candidate)
if mdl[0]:
module_specs.append(grp_env_mdl_candidate)
else: else:
pkg_specs.append(name) pkg_specs.append(name)
return pkg_specs, grp_specs, filenames return pkg_specs, grp_specs, env_specs, module_specs, filenames
def _update_only(self, pkgs): def _update_only(self, pkgs):
not_installed = [] not_installed = []
@ -784,6 +823,16 @@ class DnfModule(YumDnf):
rc=1, rc=1,
) )
def _is_module_installed(self, module_spec):
if self.with_modules:
module_spec = module_spec.strip()
module_list, nsv = self.module_base._get_modules(module_spec)
if nsv.stream in self.base._moduleContainer.getEnabledStream(nsv.name):
return True
return False # seems like a sane default
def ensure(self): def ensure(self):
allow_erasing = False allow_erasing = False
@ -816,27 +865,10 @@ class DnfModule(YumDnf):
failure_response['msg'] = "Depsolve Error occured attempting to upgrade all packages" failure_response['msg'] = "Depsolve Error occured attempting to upgrade all packages"
self.module.fail_json(**failure_response) self.module.fail_json(**failure_response)
else: else:
pkg_specs, group_specs, filenames = self._parse_spec_group_file() pkg_specs, group_specs, env_specs, module_specs, filenames = self._parse_spec_group_file()
if group_specs:
self.base.read_comps()
pkg_specs = [p.strip() for p in pkg_specs] pkg_specs = [p.strip() for p in pkg_specs]
filenames = [f.strip() for f in filenames] filenames = [f.strip() for f in filenames]
groups = []
environments = []
for group_spec in (g.strip() for g in group_specs):
group = self.base.comps.group_by_pattern(group_spec)
if group:
groups.append(group.id)
else:
environment = self.base.comps.environment_by_pattern(group_spec)
if environment:
environments.append(environment.id)
else:
self.module.fail_json(
msg="No group {0} available.".format(group_spec),
results=[],
)
if self.state in ['installed', 'present']: if self.state in ['installed', 'present']:
# Install files. # Install files.
@ -844,8 +876,25 @@ class DnfModule(YumDnf):
for filename in filenames: for filename in filenames:
response['results'].append("Installed {0}".format(filename)) response['results'].append("Installed {0}".format(filename))
# Install modules
if module_specs and self.with_modules:
for module in module_specs:
try:
if not self._is_module_installed(module):
response['results'].append("Module {0} installed.".format(module))
self.module_base.install([module])
except dnf.exceptions.MarkingErrors as e:
failure_response['failures'].append(
" ".join(
(
' '.join(module),
to_native(e)
)
)
)
# Install groups. # Install groups.
for group in groups: for group in group_specs:
try: try:
group_pkg_count_installed = self.base.group_install(group, dnf.const.GROUP_PACKAGE_TYPES) group_pkg_count_installed = self.base.group_install(group, dnf.const.GROUP_PACKAGE_TYPES)
if group_pkg_count_installed == 0: if group_pkg_count_installed == 0:
@ -861,7 +910,7 @@ class DnfModule(YumDnf):
# this but still install as much as possible. # this but still install as much as possible.
failure_response['failures'].append(" ".join((group, to_native(e)))) failure_response['failures'].append(" ".join((group, to_native(e))))
for environment in environments: for environment in env_specs:
try: try:
self.base.environment_install(environment, dnf.const.GROUP_PACKAGE_TYPES) self.base.environment_install(environment, dnf.const.GROUP_PACKAGE_TYPES)
except dnf.exceptions.DepsolveError as e: except dnf.exceptions.DepsolveError as e:
@ -870,6 +919,13 @@ class DnfModule(YumDnf):
except dnf.exceptions.Error as e: except dnf.exceptions.Error as e:
failure_response['failures'].append(" ".join((environment, to_native(e)))) failure_response['failures'].append(" ".join((environment, to_native(e))))
if module_specs and not self.with_modules:
# This means that the group or env wasn't found in comps
self.module.fail_json(
msg="No group {0} available.".format(module_specs[0]),
results=[],
)
# Install packages. # Install packages.
if self.update_only: if self.update_only:
not_installed = self._update_only(pkg_specs) not_installed = self._update_only(pkg_specs)
@ -890,7 +946,24 @@ class DnfModule(YumDnf):
for filename in filenames: for filename in filenames:
response['results'].append("Installed {0}".format(filename)) response['results'].append("Installed {0}".format(filename))
for group in groups: # Upgrade modules
if module_specs and self.with_modules:
for module in module_specs:
try:
if self._is_module_installed(module):
response['results'].append("Module {0} upgraded.".format(module))
self.module_base.upgrade([module])
except dnf.exceptions.MarkingErrors as e:
failure_response['failures'].append(
" ".join(
(
' '.join(module),
to_native(e)
)
)
)
for group in group_specs:
try: try:
try: try:
self.base.group_upgrade(group) self.base.group_upgrade(group)
@ -906,7 +979,7 @@ class DnfModule(YumDnf):
except dnf.exceptions.Error as e: except dnf.exceptions.Error as e:
failure_response['failures'].append(" ".join((group, to_native(e)))) failure_response['failures'].append(" ".join((group, to_native(e))))
for environment in environments: for environment in env_specs:
try: try:
try: try:
self.base.environment_upgrade(environment) self.base.environment_upgrade(environment)
@ -942,7 +1015,25 @@ class DnfModule(YumDnf):
results=[], results=[],
) )
for group in groups: # Remove modules
if module_specs and self.with_modules:
for module in module_specs:
try:
if self._is_module_installed(module):
response['results'].append("Module {0} removed.".format(module))
self.module_base.disable([module])
self.module_base.remove([module])
except dnf.exceptions.MarkingErrors as e:
failure_response['failures'].append(
" ".join(
(
' '.join(module),
to_native(e)
)
)
)
for group in group_specs:
try: try:
self.base.group_remove(group) self.base.group_remove(group)
except dnf.exceptions.CompsError: except dnf.exceptions.CompsError:
@ -957,7 +1048,7 @@ class DnfModule(YumDnf):
# https://bugzilla.redhat.com/show_bug.cgi?id=1620324 # https://bugzilla.redhat.com/show_bug.cgi?id=1620324
pass pass
for environment in environments: for environment in env_specs:
try: try:
self.base.environment_remove(environment) self.base.environment_remove(environment)
except dnf.exceptions.CompsError: except dnf.exceptions.CompsError:
@ -1080,6 +1171,9 @@ class DnfModule(YumDnf):
self.enablerepo, self.installroot self.enablerepo, self.installroot
) )
if self.with_modules:
self.module_base = dnf.module.module_base.ModuleBase(self.base)
self.ensure() self.ensure()

@ -38,3 +38,8 @@
when: when:
- ansible_distribution == 'Fedora' - ansible_distribution == 'Fedora'
- ansible_distribution_major_version|int >= 23 - ansible_distribution_major_version|int >= 23
- import_tasks: 'modularity.yml'
when:
- ansible_distribution == 'Fedora' and ansible_distribution_major_version|int >= 29
- ansible_distribution == 'RedHat' and ansible_distribution_major_version|int >= 8

@ -0,0 +1,48 @@
- name: install "@postgresql:9.6/client" module
dnf:
name: "@postgresql:9.6/client"
state: present
register: dnf_result
- name: verify installation of "@postgresql:9.6/client" module
assert:
that:
- "not dnf_result.failed"
- "dnf_result.changed"
- name: install "@postgresql:9.6/client" module again
dnf:
name: "@postgresql:9.6/client"
state: present
register: dnf_result
- name: verify installation of "@postgresql:9.6/client" module again
assert:
that:
- "not dnf_result.failed"
- "not dnf_result.changed"
- name: uninstall "@postgresql:9.6/client" module
dnf:
name: "@postgresql:9.6/client"
state: absent
register: dnf_result
- name: verify uninstallation of "@postgresql:9.6/client" module
assert:
that:
- "not dnf_result.failed"
- "dnf_result.changed"
- name: uninstall "@postgresql:9.6/client" module again
dnf:
name: "@postgresql:9.6/client"
state: install
register: dnf_result
- name: verify uninstallation of "@postgresql:9.6/client" module again
assert:
that:
- "not dnf_result.failed"
- "not dnf_result.changed"
Loading…
Cancel
Save