From cce48a6c1ad138b3217939ccfdb0f271a8492890 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Mon, 26 Sep 2022 10:57:59 +0200 Subject: [PATCH 66/75] Add IfCfgScanner actor This scans the legacy network configuration in /etc/sysconfig/network-scripts and produces an IfCfg for each ifcfg-* file encountered (along with associated keys-, rules-, routes-, etc. files). --- .../el8toel9/actors/ifcfgscanner/actor.py | 18 +++ .../ifcfgscanner/libraries/ifcfgscanner.py | 67 ++++++++++ .../tests/unit_test_ifcfgscanner.py | 123 ++++++++++++++++++ 3 files changed, 208 insertions(+) create mode 100644 repos/system_upgrade/el8toel9/actors/ifcfgscanner/actor.py create mode 100644 repos/system_upgrade/el8toel9/actors/ifcfgscanner/libraries/ifcfgscanner.py create mode 100644 repos/system_upgrade/el8toel9/actors/ifcfgscanner/tests/unit_test_ifcfgscanner.py diff --git a/repos/system_upgrade/el8toel9/actors/ifcfgscanner/actor.py b/repos/system_upgrade/el8toel9/actors/ifcfgscanner/actor.py new file mode 100644 index 00000000..dd94986b --- /dev/null +++ b/repos/system_upgrade/el8toel9/actors/ifcfgscanner/actor.py @@ -0,0 +1,18 @@ +from leapp.actors import Actor +from leapp.libraries.actor import ifcfgscanner +from leapp.models import IfCfg +from leapp.tags import FactsPhaseTag, IPUWorkflowTag + + +class IfCfgScanner(Actor): + """ + Scan ifcfg files with legacy network configuration + """ + + name = "ifcfg_scanner" + consumes = () + produces = (IfCfg,) + tags = (IPUWorkflowTag, FactsPhaseTag,) + + def process(self): + ifcfgscanner.process() diff --git a/repos/system_upgrade/el8toel9/actors/ifcfgscanner/libraries/ifcfgscanner.py b/repos/system_upgrade/el8toel9/actors/ifcfgscanner/libraries/ifcfgscanner.py new file mode 100644 index 00000000..cfc385dc --- /dev/null +++ b/repos/system_upgrade/el8toel9/actors/ifcfgscanner/libraries/ifcfgscanner.py @@ -0,0 +1,67 @@ +import errno +from os import listdir, path + +from leapp.libraries.stdlib import api +from leapp.models import IfCfg, IfCfgProperty + +SYSCONFIG_DIR = "/etc/sysconfig/network-scripts" + + +def aux_file(prefix, filename): + directory = path.dirname(filename) + keys_base = path.basename(filename).replace("ifcfg-", prefix) + return path.join(directory, keys_base) + + +def process_ifcfg(filename, secrets=False): + if not path.exists(filename): + return None + + properties = [] + for line in open(filename).readlines(): + try: + (name, value) = line.split("#")[0].strip().split("=") + if secrets: + value = None + except ValueError: + # We're not interested in lines that are not + # simple assignments. Play it safe. + continue + + properties.append(IfCfgProperty(name=name, value=value)) + return properties + + +def process_plain(filename): + if not path.exists(filename): + return None + return open(filename).readlines() + + +def process_file(filename): + api.produce(IfCfg( + filename=filename, + properties=process_ifcfg(filename), + secrets=process_ifcfg(aux_file("keys-", filename), secrets=True), + rules=process_plain(aux_file("rule-", filename)), + rules6=process_plain(aux_file("rule6-", filename)), + routes=process_plain(aux_file("route-", filename)), + routes6=process_plain(aux_file("route6-", filename)), + )) + + +def process_dir(directory): + try: + keyfiles = listdir(directory) + except OSError as e: + if e.errno == errno.ENOENT: + return + raise + + for f in keyfiles: + if f.startswith("ifcfg-"): + process_file(path.join(directory, f)) + + +def process(): + process_dir(SYSCONFIG_DIR) diff --git a/repos/system_upgrade/el8toel9/actors/ifcfgscanner/tests/unit_test_ifcfgscanner.py b/repos/system_upgrade/el8toel9/actors/ifcfgscanner/tests/unit_test_ifcfgscanner.py new file mode 100644 index 00000000..f5e3056a --- /dev/null +++ b/repos/system_upgrade/el8toel9/actors/ifcfgscanner/tests/unit_test_ifcfgscanner.py @@ -0,0 +1,123 @@ +import errno +import textwrap +from os.path import basename + +import mock +import six + +from leapp.libraries.actor import ifcfgscanner +from leapp.libraries.common.testutils import make_OSError, produce_mocked +from leapp.libraries.stdlib import api +from leapp.models import IfCfg + +_builtins_open = "builtins.open" if six.PY3 else "__builtin__.open" + + +def _listdir_ifcfg(path): + if path == ifcfgscanner.SYSCONFIG_DIR: + return ["ifcfg-net0"] + raise make_OSError(errno.ENOENT) + + +def _listdir_ifcfg2(path): + if path == ifcfgscanner.SYSCONFIG_DIR: + return ["ifcfg-net0", "ifcfg-net1"] + raise make_OSError(errno.ENOENT) + + +def _exists_ifcfg(filename): + return basename(filename).startswith("ifcfg-") + + +def _exists_keys(filename): + if _exists_ifcfg(filename): + return True + return basename(filename).startswith("keys-") + + +def test_no_conf(monkeypatch): + """ + No report if there are no ifcfg files. + """ + + monkeypatch.setattr(ifcfgscanner, "listdir", lambda _: ()) + monkeypatch.setattr(api, "produce", produce_mocked()) + ifcfgscanner.process() + assert not api.produce.called + + +def test_ifcfg1(monkeypatch): + """ + Parse a single ifcfg file. + """ + + ifcfg_file = textwrap.dedent(""" + TYPE=Wireless # Some comment + # Another comment + ESSID=wep1 + NAME=wep1 + MODE=Managed + WEP_KEY_FLAGS=ask + SECURITYMODE=open + DEFAULTKEY=1 + KEY_TYPE=key + """) + + mock_config = mock.mock_open(read_data=ifcfg_file) + with mock.patch(_builtins_open, mock_config): + monkeypatch.setattr(ifcfgscanner, "listdir", _listdir_ifcfg) + monkeypatch.setattr(ifcfgscanner.path, "exists", _exists_ifcfg) + monkeypatch.setattr(api, "produce", produce_mocked()) + ifcfgscanner.process() + + assert api.produce.called == 1 + assert len(api.produce.model_instances) == 1 + ifcfg = api.produce.model_instances[0] + assert isinstance(ifcfg, IfCfg) + assert ifcfg.filename == "/etc/sysconfig/network-scripts/ifcfg-net0" + assert ifcfg.secrets is None + assert len(ifcfg.properties) == 8 + assert ifcfg.properties[0].name == "TYPE" + assert ifcfg.properties[0].value == "Wireless" + assert ifcfg.properties[1].name == "ESSID" + assert ifcfg.properties[1].value == "wep1" + + +def test_ifcfg2(monkeypatch): + """ + Parse two ifcfg files. + """ + + mock_config = mock.mock_open(read_data="TYPE=Ethernet") + with mock.patch(_builtins_open, mock_config): + monkeypatch.setattr(ifcfgscanner, "listdir", _listdir_ifcfg2) + monkeypatch.setattr(ifcfgscanner.path, "exists", _exists_ifcfg) + monkeypatch.setattr(api, "produce", produce_mocked()) + ifcfgscanner.process() + + assert api.produce.called == 2 + assert len(api.produce.model_instances) == 2 + ifcfg = api.produce.model_instances[0] + assert isinstance(ifcfg, IfCfg) + + +def test_ifcfg_key(monkeypatch): + """ + Report ifcfg secrets from keys- file. + """ + + mock_config = mock.mock_open(read_data="KEY_PASSPHRASE1=Hell0") + with mock.patch(_builtins_open, mock_config): + monkeypatch.setattr(ifcfgscanner, "listdir", _listdir_ifcfg) + monkeypatch.setattr(ifcfgscanner.path, "exists", _exists_keys) + monkeypatch.setattr(api, "produce", produce_mocked()) + ifcfgscanner.process() + + assert api.produce.called == 1 + assert len(api.produce.model_instances) == 1 + ifcfg = api.produce.model_instances[0] + assert isinstance(ifcfg, IfCfg) + assert ifcfg.filename == "/etc/sysconfig/network-scripts/ifcfg-net0" + assert len(ifcfg.secrets) == 1 + assert ifcfg.secrets[0].name == "KEY_PASSPHRASE1" + assert ifcfg.secrets[0].value is None -- 2.39.0