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
127 lines
4.3 KiB
10 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)
|