From d30e6d7af910c44452568fb7f4ddaf9c64793ac0 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Tue, 21 Jan 2025 01:44:00 +0100 Subject: [PATCH] CheckNetworkDeprecations9to10: Check for ifcfg artifacts Ifcfg support is going away in RHEL 10. Try to be a little more helpful, either suggesting how to migrate automatically, deliver bad news about migrating legacy policy routing rules manually, or report garbage after what looks like bad migration (better sad than sorry). JIRA: https://issues.redhat.com/browse/RHEL-58972 --- .../actors/networkdeprecations/actor.py | 104 +++++++++++++++++- .../unit_test_networkdeprecations_9to10.py | 72 +++++++++++- 2 files changed, 173 insertions(+), 3 deletions(-) diff --git a/repos/system_upgrade/el9toel10/actors/networkdeprecations/actor.py b/repos/system_upgrade/el9toel10/actors/networkdeprecations/actor.py index d1d58511a3..4f6fb71084 100644 --- a/repos/system_upgrade/el9toel10/actors/networkdeprecations/actor.py +++ b/repos/system_upgrade/el9toel10/actors/networkdeprecations/actor.py @@ -1,6 +1,8 @@ +import os + from leapp import reporting from leapp.actors import Actor -from leapp.models import NetworkManagerConfig, Report +from leapp.models import IfCfg, NetworkManagerConfig, Report from leapp.tags import ChecksPhaseTag, IPUWorkflowTag @@ -15,7 +17,7 @@ class CheckNetworkDeprecations9to10(Actor): """ name = "network_deprecations" - consumes = (NetworkManagerConfig,) + consumes = (NetworkManagerConfig, IfCfg,) produces = (Report,) tags = (ChecksPhaseTag, IPUWorkflowTag,) @@ -39,8 +41,106 @@ def report_dhclient(): reporting.RelatedResource('package', 'NetworkManager'), ]) + @staticmethod + def report_ifcfg_rules(conn): + reporting.create_report([ + reporting.Title('Legacy network configuration with policy routing rules found'), + reporting.Summary('Network configuration files in "ifcfg" format is present accompanied' + ' by legacy routing rules. In Red Hat Enterprise Linux 10, support' + ' for these files is no longer enabled and the configuration will be' + ' ignored. Legacy routing rules are not supported by NetworkManager' + ' natively and therefore can not be migrated automatically.'), + reporting.Remediation(hint='Replace the routing rules with equivalent' + ' "ipv4.routing-rules" or "ipv6.routing-rules" properties,' + ' then migrate the connection with "nmcli conn migrate"'), + reporting.ExternalLink( + url='https://access.redhat.com/solutions/7083803', + title='How to migrate the connection from ifcfg to NetworkManager keyfile plugin?'), + reporting.ExternalLink( + url='https://networkmanager.dev/docs/api/latest/nmcli.html', + title='nmcli(1) manual, describes "connection migrate" sub-command.'), + reporting.ExternalLink( + url='https://networkmanager.dev/docs/api/latest/nm-settings-ifcfg-rh.html', + title='nm-settings-ifcfg-rh(5), description of the "ifcfg" format'), + reporting.ExternalLink( + url='https://networkmanager.dev/docs/api/latest/nm-settings-keyfile.html', + title='nm-settings-keyfile(5), description of the "keyfile" format'), + reporting.Severity(reporting.Severity.HIGH), + reporting.Groups([reporting.Groups.NETWORK, reporting.Groups.SERVICES]), + reporting.Groups([reporting.Groups.INHIBITOR]), + reporting.RelatedResource('package', 'NetworkManager'), + reporting.RelatedResource('package', 'NetworkManager-dispatcher-routing-rules'), + ] + [reporting.RelatedResource('file', file) for file in conn.values()]) + pass + + @staticmethod + def report_ifcfg_leftover(conn): + reporting.create_report([ + reporting.Title('Unused legacy network configuration found'), + reporting.Summary('Files that used to accompany legacy network configuration in "ifcfg"' + ' format are present, even though the configuration itself is not' + ' longer there. These files will be ignored.'), + reporting.Remediation(hint='Verify that the files were not left behind by incomplete' + ' migration, fix up configuration if necessary, and remove' + ' them.'), + reporting.ExternalLink( + url='https://access.redhat.com/solutions/7083803', + title='How to migrate the connection from ifcfg to NetworkManager keyfile plugin?'), + reporting.Severity(reporting.Severity.HIGH), + reporting.Groups([reporting.Groups.NETWORK, reporting.Groups.SERVICES]), + reporting.Groups([reporting.Groups.INHIBITOR]), + ] + [reporting.RelatedResource('file', file) for file in conn.values()]) + + @staticmethod + def report_ifcfg(conn): + reporting.create_report([ + reporting.Title('Legacy network configuration found'), + reporting.Summary('Network configuration file in legacy "ifcfg" format is present.' + ' In Red Hat Enterprise Linux 10, support for these files is no longer' + ' enabled and the configuration will be ignored.'), + reporting.Remediation( + hint='Convert the configuration into NetworkManager native "keyfile" format.', + commands=[['nmcli', 'connection', 'migrate', conn['ifcfg']]]), + reporting.ExternalLink( + url='https://access.redhat.com/solutions/7083803', + title='How to migrate the connection from ifcfg to NetworkManager keyfile plugin?'), + reporting.ExternalLink( + url='https://networkmanager.dev/docs/api/latest/nmcli.html', + title='nmcli(1) manual, describes "connection migrate" sub-command.'), + reporting.ExternalLink( + url='https://networkmanager.dev/docs/api/latest/nm-settings-ifcfg-rh.html', + title='nm-settings-ifcfg-rh(5), description of the "ifcfg" format'), + reporting.ExternalLink( + url='https://networkmanager.dev/docs/api/latest/nm-settings-keyfile.html', + title='nm-settings-keyfile(5), description of the "keyfile" format'), + reporting.Severity(reporting.Severity.HIGH), + reporting.Groups([reporting.Groups.NETWORK, reporting.Groups.SERVICES]), + reporting.Groups([reporting.Groups.INHIBITOR]), + reporting.RelatedResource('package', 'NetworkManager'), + ] + [reporting.RelatedResource('file', file) for file in conn.values()]) + def process(self): for nm_config in self.consume(NetworkManagerConfig): self.log.info('Consuming dhcp={}'.format(nm_config.dhcp)) if nm_config.dhcp == 'dhclient': CheckNetworkDeprecations9to10.report_dhclient() + + conns = {} + + for ifcfg in self.consume(IfCfg): + self.log.info('Consuming ifcfg={}'.format(ifcfg.filename)) + rule_basename = os.path.basename(ifcfg.filename) + (kind, name) = rule_basename.split('-', 1) + if name not in conns: + conns[name] = {} + conns[name][kind] = ifcfg.filename + + for name in conns: + conn = conns[name] + if 'ifcfg' in conn: + if 'rule' in conn or 'rule6' in conn: + CheckNetworkDeprecations9to10.report_ifcfg_rules(conn) + else: + CheckNetworkDeprecations9to10.report_ifcfg(conn) + else: + CheckNetworkDeprecations9to10.report_ifcfg_leftover(conn) diff --git a/repos/system_upgrade/el9toel10/actors/networkdeprecations/tests/unit_test_networkdeprecations_9to10.py b/repos/system_upgrade/el9toel10/actors/networkdeprecations/tests/unit_test_networkdeprecations_9to10.py index 57c09b018c..5f048b4b1f 100644 --- a/repos/system_upgrade/el9toel10/actors/networkdeprecations/tests/unit_test_networkdeprecations_9to10.py +++ b/repos/system_upgrade/el9toel10/actors/networkdeprecations/tests/unit_test_networkdeprecations_9to10.py @@ -1,5 +1,7 @@ -from leapp.models import NetworkManagerConfig, Report +import pytest +from leapp.models import IfCfg, NetworkManagerConfig, Report +from leapp.utils.report import is_inhibitor def test_dhcp_dhclient(current_actor_context): current_actor_context.feed(NetworkManagerConfig(dhcp='dhclient')) @@ -23,3 +25,71 @@ def test_dhcp_default(current_actor_context): current_actor_context.run() reports = list(current_actor_context.consume(Report)) assert not reports + +def test_ifcfg(current_actor_context): + """ + Report when a file ready for migration is present. + """ + + current_actor_context.feed(IfCfg(filename='/NM/ifcfg-eth-dev')) + current_actor_context.run() + reports = current_actor_context.consume(Report) + assert len(reports) == 1 + report_fields = reports[0].report + assert is_inhibitor(report_fields) + assert report_fields['title'] == 'Legacy network configuration found' + resources = report_fields['detail']['related_resources'] + assert len(resources) == 2 + assert resources[0]['scheme'] == 'package' + assert resources[0]['title'] == 'NetworkManager' + assert resources[1]['scheme'] == 'file' + assert resources[1]['title'] == '/NM/ifcfg-eth-dev' + +@pytest.mark.parametrize('files', + [('/NM/rule-lost',), + ('/NM/route6-eth-dev', '/NM/rule-eth-dev')]) +def test_leftovers(current_actor_context, files): + """ + Report when what appears like artifacts from unsuccessful migration are present. + """ + + for file in files: + current_actor_context.feed(IfCfg(filename=file)) + current_actor_context.run() + reports = current_actor_context.consume(Report) + assert len(reports) == 1 + report_fields = reports[0].report + assert is_inhibitor(report_fields) + assert report_fields['title'] == 'Unused legacy network configuration found' + resources = report_fields['detail']['related_resources'] + assert len(resources) == len(files) + for i in range(len(files)): + assert resources[i]['scheme'] == 'file' + assert resources[i]['title'] == files[i] + +@pytest.mark.parametrize('files', + [('/NM/ifcfg-old', '/NM/rule-old'), + ('/NM/ifcfg-old', '/NM/rule6-old'), + ('/NM/ifcfg-old', '/NM/rule6-old', '/NM/rule-old')]) +def test_rules(current_actor_context, files): + """ + Report when configuration that requires manual migration is present. + """ + + for file in files: + current_actor_context.feed(IfCfg(filename=file)) + current_actor_context.run() + reports = current_actor_context.consume(Report) + assert len(reports) == 1 + report_fields = reports[0].report + assert is_inhibitor(report_fields) + assert report_fields['title'] == 'Legacy network configuration with policy routing rules found' + resources = report_fields['detail']['related_resources'] + assert len(resources) == 2 + len(files) + assert resources[0]['scheme'] == 'package' + assert resources[0]['title'] == 'NetworkManager' + assert resources[1]['scheme'] == 'package' + assert resources[1]['title'] == 'NetworkManager-dispatcher-routing-rules' + for i in range(len(files)): + assert resources[2 + i]['scheme'] == 'file' + assert resources[2 + i]['title'] == files[i]