Skip to content

Commit

Permalink
add multipathconf(read|check|update) el8toel9 actors
Browse files Browse the repository at this point in the history
Check config files and make necessary changes to retain RHEL-8 default
behavior. This involves adding 'enable_foreign ""' and
'allow_usb_devices yes' to the defaults section of /etc/multipath.conf
if these options don't already exist, and changing any regex values of
"*" to ".*" in any multipath config file. Reports are generated for all
changes made. In order to share code with el7toel8, move the
multipathutil library to the system_upgrade/common repo.
  • Loading branch information
bmarzins committed May 5, 2022
1 parent 6a79e0f commit 6f4ff75
Show file tree
Hide file tree
Showing 58 changed files with 14,489 additions and 0 deletions.
25 changes: 25 additions & 0 deletions repos/system_upgrade/el8toel9/actors/multipathconfcheck/actor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from leapp.actors import Actor
from leapp.libraries.actor import multipathconfcheck
from leapp.models import MultipathConfFacts8to9
from leapp.reporting import Report
from leapp.tags import ChecksPhaseTag, IPUWorkflowTag


class MultipathConfCheck8to9(Actor):
"""
Checks if changes to the multipath configuration files are necessary
for upgrading to RHEL9, and reports the results.
"""

name = 'multipath_conf_check_8to9'
consumes = (MultipathConfFacts8to9,)
produces = (Report,)
tags = (ChecksPhaseTag, IPUWorkflowTag)

def process(self):
facts = next(self.consume(MultipathConfFacts8to9), None)
if facts is None:
self.log.debug('Skipping execution. No MultipathConfFacts8to9 has '
'been produced')
return
multipathconfcheck.check_configs(facts)
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
from leapp import reporting
from leapp.reporting import create_report


def _report_foreign():
create_report([
reporting.Title(
'device-mapper-multipath now defaults to ignoring foreign devices'
),
reporting.Summary(
'In RHEL-9, the default value for the "enable_foreign" option has '
'changed to "NONE". This means that multipath will no longer list '
'devices that are not managed by device-mapper. In order to retain '
'the default RHEL-8 behavior of listing foreign multipath devices, '
'\'enable_foreign ""\' will be added to the defaults section of '
'"/etc/multipath.conf". If you wish to change to the default '
'RHEL-9 behavior, please remove this line. This option only '
'effects the devices that multipath lists. It has no impact on '
'what devices are managed.'),
reporting.Severity(reporting.Severity.INFO),
reporting.Tags([reporting.Tags.SERVICES]),
reporting.RelatedResource('package', 'device-mapper-multipath')
])


def _report_allow_usb():
create_report([
reporting.Title(
'device-mapper-multipath now defaults to ignoring USB devices'
),
reporting.Summary(
'In RHEL-9, the default multipath configuration has changed to '
'ignore USB devices. A new config option, "allow_usb_devices" has '
'been added to control this. In order to retain the RHEL-8 '
'behavior of treating USB devices like other block devices. '
'"allow_usb_devices yes" will be added to the defaults section '
'of "/etc/multipath.conf". If you wish to change to the default '
'RHEL-9 behavior, please remove this line.'),
reporting.Severity(reporting.Severity.INFO),
reporting.Tags([reporting.Tags.SERVICES]),
reporting.RelatedResource('package', 'device-mapper-multipath')
])


def _create_paths_str(paths):
if len(paths) < 2:
return paths[0]
return '{} and {}'.format(', '.join(paths[0:-1]), paths[-1])


def _report_invalid_regexes(paths):
paths_str = _create_paths_str(paths)
create_report([
reporting.Title(
'device-mapper-multipath no longer accepts "*" as a valid regular expression'
),
reporting.Summary(
'Some options in device-mapper-multipath configuration files '
'have values that are regular expressions. In RHEL-8, if such an '
'option had a value of "*", multipath would internally convert it '
'to ".*". In RHEL-9, values of "*" are no longer accepted. '
'These regular expression values have been found in {}. They '
'will be converted to ".*"'.format(paths_str)),
reporting.Severity(reporting.Severity.INFO),
reporting.Tags([reporting.Tags.SERVICES]),
reporting.RelatedResource('package', 'device-mapper-multipath')
])


def check_configs(facts):
need_foreign = not any(x for x in facts.configs if x.enable_foreign_exists)
need_allow_usb = not any(x for x in facts.configs if x.allow_usb_exists)
invalid_regexes = [x.pathname for x in facts.configs if x.invalid_regexes_exist]

if need_foreign:
_report_foreign()
if need_allow_usb:
_report_allow_usb()
if invalid_regexes:
_report_invalid_regexes(invalid_regexes)
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
from leapp.models import MultipathConfFacts8to9, MultipathConfig8to9
from leapp.reporting import Report


def _assert_foreign_report(report):
assert report['title'] == \
'device-mapper-multipath now defaults to ignoring foreign devices'
assert report['severity'] == 'info'


def _assert_allow_usb_report(report):
assert report['title'] == \
'device-mapper-multipath now defaults to ignoring USB devices'
assert report['severity'] == 'info'


def _assert_invalid_regexes_report(report, paths_str):
assert report['title'] == \
'device-mapper-multipath no longer accepts "*" as a valid regular expression'
assert report['severity'] == 'info'
assert paths_str in report['summary']


def _build_config(pathname, config_dir, enable_foreign_exists, invalid_regexes_exist, allow_usb_exists):
return MultipathConfig8to9(
pathname=pathname,
config_dir=config_dir,
enable_foreign_exists=enable_foreign_exists,
invalid_regexes_exist=invalid_regexes_exist,
allow_usb_exists=allow_usb_exists,
)


def _build_facts(confs):
return MultipathConfFacts8to9(configs=confs)


def test_need_everything(current_actor_context):
config = _build_config('need_everything.conf', None, False, True, False)
facts = _build_facts([config])
current_actor_context.feed(facts)
current_actor_context.run()
reports = list(current_actor_context.consume(Report))
assert reports and len(reports) == 3
_assert_foreign_report(reports[0].report)
_assert_allow_usb_report(reports[1].report)
_assert_invalid_regexes_report(reports[2].report, 'need_everything.conf')


def test_need_nothing(current_actor_context):
config = _build_config('need_nothing.conf', '/etc/multipath/conf.d', True, False, True)
facts = _build_facts([config])
current_actor_context.feed(facts)
current_actor_context.run()
reports = current_actor_context.consume(Report)
assert not reports


def test_need_foreign(current_actor_context):
config = _build_config('need_foreign.conf', None, False, False, True)
facts = _build_facts([config])
current_actor_context.feed(facts)
current_actor_context.run()
reports = list(current_actor_context.consume(Report))
assert reports and len(reports) == 1
_assert_foreign_report(reports[0].report)


def test_need_allos_usb(current_actor_context):
config = _build_config('need_allow_usb.conf', None, True, False, False)
facts = _build_facts([config])
current_actor_context.feed(facts)
current_actor_context.run()
reports = list(current_actor_context.consume(Report))
assert reports and len(reports) == 1
_assert_allow_usb_report(reports[0].report)


def test_invalid_regexes(current_actor_context):
config1 = _build_config('invalid_regexes1.conf', None, True, True, True)
config2 = _build_config('no_invalid_regexes.conf', None, True, False, True)
config3 = _build_config('invalid_regexes2.conf', None, True, True, True)
facts = _build_facts([config1, config2, config3])
current_actor_context.feed(facts)
current_actor_context.run()
reports = list(current_actor_context.consume(Report))
assert reports and len(reports) == 1
_assert_invalid_regexes_report(reports[0].report, 'invalid_regexes1.conf and invalid_regexes2.conf')


def test_not_in_main_conf(current_actor_context):
main_conf = _build_config('main.conf', '/etc/multipath/conf.d', False, True, False)
other_conf = _build_config('other.conf', None, True, False, True)
facts = _build_facts([main_conf, other_conf])
current_actor_context.feed(facts)
current_actor_context.run()
reports = list(current_actor_context.consume(Report))
assert reports and len(reports) == 1
_assert_invalid_regexes_report(reports[0].report, 'main.conf')


def test_in_main_conf(current_actor_context):
main_conf = _build_config('main.conf', '/etc/multipath/conf.d', True, True, True)
other_conf = _build_config('other.conf', None, False, False, False)
next_conf = _build_config('next.conf', None, False, True, False)
last_conf = _build_config('last.conf', None, False, True, False)
facts = _build_facts([main_conf, other_conf, next_conf, last_conf])
current_actor_context.feed(facts)
current_actor_context.run()
reports = list(current_actor_context.consume(Report))
assert reports and len(reports) == 1
_assert_invalid_regexes_report(reports[0].report, 'main.conf, next.conf and last.conf')


def test_in_none_conf(current_actor_context):
main_conf = _build_config('main.conf', '/etc/multipath/conf.d', False, False, False)
other_conf = _build_config('other.conf', None, False, False, False)
facts = _build_facts([main_conf, other_conf])
current_actor_context.feed(facts)
current_actor_context.run()
reports = list(current_actor_context.consume(Report))
assert reports and len(reports) == 2
_assert_foreign_report(reports[0].report)
_assert_allow_usb_report(reports[1].report)


def test_mixed_conf(current_actor_context):
main_conf = _build_config('main.conf', None, True, False, False)
next_conf = _build_config('next.conf', None, False, True, False)
last_conf = _build_config('last.conf', None, True, False, False)
facts = _build_facts([main_conf, next_conf, last_conf])
current_actor_context.feed(facts)
current_actor_context.run()
reports = list(current_actor_context.consume(Report))
assert reports and len(reports) == 2
_assert_allow_usb_report(reports[0].report)
_assert_invalid_regexes_report(reports[1].report, 'next.conf')
33 changes: 33 additions & 0 deletions repos/system_upgrade/el8toel9/actors/multipathconfread/actor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from leapp.actors import Actor
from leapp.libraries.actor import multipathconfread
from leapp.models import InstalledRedHatSignedRPM, MultipathConfFacts8to9, TargetUserSpaceUpgradeTasks
from leapp.tags import FactsPhaseTag, IPUWorkflowTag


class MultipathConfRead8to9(Actor):
"""
Read multipath configuration files and extract the necessary informaton
Related files:
- /etc/multipath.conf
- /etc/multipath/ - any files inside the directory
- /etc/xdrdevices.conf
As well, create task (msg) to copy all needed multipath files into
the target container as the files are needed to create proper initramfs.
This covers the files mentioned above.
"""

name = 'multipath_conf_read_8to9'
consumes = (InstalledRedHatSignedRPM,)
produces = (MultipathConfFacts8to9, TargetUserSpaceUpgradeTasks)
tags = (FactsPhaseTag, IPUWorkflowTag)

def process(self):
if multipathconfread.is_processable():
res = multipathconfread.get_multipath_conf_facts()
if res:
self.produce(res)
# Create task to copy multipath config files Iff facts
# are generated
multipathconfread.produce_copy_to_target_task()

0 comments on commit 6f4ff75

Please sign in to comment.