You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

127 lines
4.3 KiB

9 months ago
from leapp.exceptions import StopActorExecutionError
from leapp.libraries.common.config.version import get_source_major_version
from leapp.libraries.stdlib import api, CalledProcessError
def get_leapp_packages():
"""
Return the list of leapp and leapp-repository rpms that should be preserved
during the upgrade.
It's list of packages that should be preserved, not what is really
installed.
The snactor RPM doesn't have to be installed, but if so, we have to take
care about that too as well to prevent broken dnf transaction.
"""
# TODO: should we set the seatbelt and exclude leapp RPMs from the target
# system too?
generic = ['leapp', 'snactor']
if get_source_major_version() == '7':
return generic + ['python2-leapp', 'leapp-upgrade-el7toel8']
return generic + ['python3-leapp', 'leapp-upgrade-el8toel9']
def _strip_split(data, sep, maxsplit=-1):
"""
Just like str.split(), but remove ambient whitespaces from all items
"""
return [item.strip() for item in data.split(sep, maxsplit)]
def _get_main_dump(context, disable_plugins):
"""
Return the dnf configuration dump of main options for the given context.
Returns the list of lines after the line with "[main]" section
"""
cmd = ['dnf', 'config-manager', '--dump']
if disable_plugins:
for plugin in disable_plugins:
cmd += ['--disableplugin', plugin]
try:
data = context.call(cmd, split=True)['stdout']
except CalledProcessError as e:
api.current_logger().error('Cannot obtain the dnf configuration')
raise StopActorExecutionError(
message='Cannot obtain data about the DNF configuration',
details={'stdout': e.stdout, 'stderr': e.stderr}
)
try:
# return index of the first item in the main section
main_start = data.index('[main]') + 1
except ValueError:
raise StopActorExecutionError(
message='Invalid DNF configuration data (missing [main])',
details=data,
)
output_data = {}
for line in data[main_start:]:
try:
key, val = _strip_split(line, '=', 1)
except ValueError:
# This is not expected to happen, but call it a seatbelt in case
# the dnf dump implementation will change and we will miss it
# This is not such a hard error as the one above, as it means
# some values could be incomplete, however we are still able
# to continue.
api.current_logger().warning(
'Cannot parse the dnf dump correctly, line: {}'.format(line))
pass
output_data[key] = val
return output_data
def _get_excluded_pkgs(context, disable_plugins):
"""
Return the list of excluded packages for DNF in the given context.
It shouldn't be used on the source system. It is expected this functions
is called only in the target userspace container or on the target system.
"""
pkgs = _strip_split(_get_main_dump(context, disable_plugins).get('exclude', ''), ',')
return [i for i in pkgs if i]
def _set_excluded_pkgs(context, pkglist, disable_plugins):
"""
Configure DNF to exclude packages in the given list
Raise the CalledProcessError on error.
"""
exclude = 'exclude={}'.format(','.join(pkglist))
cmd = ['dnf', 'config-manager', '--save', '--setopt', exclude]
if disable_plugins:
for plugin in disable_plugins:
cmd += ['--disableplugin', plugin]
try:
context.call(cmd)
except CalledProcessError:
api.current_logger().error('Cannot set the dnf configuration')
raise
api.current_logger().debug('The DNF configuration has been updated to exclude leapp packages.')
def exclude_leapp_rpms(context, disable_plugins):
"""
Ensure the leapp RPMs are excluded from any DNF transaction.
This has to be called several times to ensure that our RPMs are not removed
or updated (replaced) during the IPU. The action should happen inside
- the target userspace container
- on the host system
So user will have to drop these packages from the exclude after the
upgrade.
"""
to_exclude = list(set(_get_excluded_pkgs(context, disable_plugins) + get_leapp_packages()))
_set_excluded_pkgs(context, to_exclude, disable_plugins)