diff --git a/anaconda.spec.in b/anaconda.spec.in index c87f5437899..62045938434 100644 --- a/anaconda.spec.in +++ b/anaconda.spec.in @@ -27,7 +27,7 @@ Source0: https://github.com/rhinstaller/%{name}/releases/download/%{name}-%{vers %define dasbusver 1.3 %define dbusver 1.2.3 %define dnfver 3.6.0 -%define dracutver 034-7 +%define dracutver 102-3 %define fcoeutilsver 1.0.12-3.20100323git %define gettextver 0.19.8 %define gtk3ver 3.22.17 @@ -46,6 +46,7 @@ Source0: https://github.com/rhinstaller/%{name}/releases/download/%{name}-%{vers %define subscriptionmanagerver 1.26 %define utillinuxver 2.15.1 %define rpmostreever 2023.2 +%define s390utilscorever 2.31.0 BuildRequires: libtool BuildRequires: gettext-devel >= %{gettextver} @@ -125,6 +126,8 @@ Requires: NetworkManager-team %endif %ifarch s390 s390x Requires: openssh +Requires: s390utils-core >= %{s390utilscorever} +Requires: dracut-network >= %{dracutver} %endif Requires: NetworkManager >= %{nmver} Requires: NetworkManager-libnm >= %{nmver} diff --git a/data/systemd/Makefile.am b/data/systemd/Makefile.am index 8c30b393ccd..023a644be32 100644 --- a/data/systemd/Makefile.am +++ b/data/systemd/Makefile.am @@ -29,6 +29,7 @@ dist_systemd_DATA = anaconda.service \ anaconda-nm-config.service \ anaconda-nm-disable-autocons.service \ anaconda-pre.service \ + anaconda-s390-device-config-import.service \ anaconda-fips.service dist_generator_SCRIPTS = anaconda-generator diff --git a/data/systemd/anaconda-s390-device-config-import.service b/data/systemd/anaconda-s390-device-config-import.service new file mode 100644 index 00000000000..d3234767bea --- /dev/null +++ b/data/systemd/anaconda-s390-device-config-import.service @@ -0,0 +1,21 @@ +[Unit] +# This service is to be run before anaconda starts and log data before anaconda changes them +# Import persistent config of any s390 devices (dasd, zfcp, znet) from +# initrd to retain user choices made with rd.dasd, rd.zfcp, rd.znet. +Description=pre-anaconda s390 device persistent config import +ConditionArchitecture=s390x +Requires=basic.target +After=basic.target +Before=anaconda.target +Wants=rsyslog.service +Wants=systemd-udev-settle.service +Wants=plymouth-quit.service plymouth-quit-wait.service +Wants=systemd-logind.service + +[Service] +Type=oneshot +ExecStart=-/sbin/chzdev --import /run/zdev.initrd.config --persistent --yes --no-root-update --force --verbose +StandardInput=tty +StandardOutput=journal+console +StandardError=journal+console +TimeoutSec=0 diff --git a/data/systemd/anaconda.target b/data/systemd/anaconda.target index 07d3e16e08a..4fb0d11573f 100644 --- a/data/systemd/anaconda.target +++ b/data/systemd/anaconda.target @@ -12,6 +12,7 @@ Wants=anaconda-direct.service anaconda.service Wants=anaconda-sshd.service Wants=anaconda-pre.service Wants=anaconda-fips.service +Wants=anaconda-s390-device-config-import.service Wants=systemd-logind.service Wants=brltty.service Wants=rhsm.service diff --git a/dracut/Makefile.am b/dracut/Makefile.am index dc303b5240d..b3e2a9d760d 100644 --- a/dracut/Makefile.am +++ b/dracut/Makefile.am @@ -37,7 +37,6 @@ dist_dracut_SCRIPTS = module-setup.sh \ anaconda-copy-cmdline.sh \ anaconda-copy-dhclient.sh \ anaconda-copy-prefixdevname.sh \ - anaconda-copy-s390ccwconf.sh \ anaconda-ifcfg.sh \ anaconda-set-kernel-hung-timeout.sh \ anaconda-error-reporting.sh \ diff --git a/dracut/anaconda-copy-s390ccwconf.sh b/dracut/anaconda-copy-s390ccwconf.sh deleted file mode 100755 index 0074720d576..00000000000 --- a/dracut/anaconda-copy-s390ccwconf.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -# Copy over s390 config /etc/ccw.conf for anaconda before pivot -[ -e /etc/ccw.conf ] && cp /etc/ccw.conf /run/install diff --git a/dracut/module-setup.sh b/dracut/module-setup.sh index b1972e9b316..fff1288254a 100755 --- a/dracut/module-setup.sh +++ b/dracut/module-setup.sh @@ -48,7 +48,6 @@ install() { inst "$moddir/anaconda-diskroot" "/sbin/anaconda-diskroot" inst_hook pre-pivot 50 "$moddir/anaconda-copy-ks.sh" inst_hook pre-pivot 50 "$moddir/anaconda-copy-cmdline.sh" - inst_hook pre-pivot 50 "$moddir/anaconda-copy-s390ccwconf.sh" inst_hook pre-pivot 90 "$moddir/anaconda-copy-dhclient.sh" inst_hook pre-pivot 91 "$moddir/anaconda-copy-prefixdevname.sh" inst_hook pre-pivot 95 "$moddir/anaconda-set-kernel-hung-timeout.sh" diff --git a/pyanaconda/modules/network/nm_client.py b/pyanaconda/modules/network/nm_client.py index 446a56ca775..5c2012ee7ce 100644 --- a/pyanaconda/modules/network/nm_client.py +++ b/pyanaconda/modules/network/nm_client.py @@ -35,6 +35,7 @@ from pyanaconda.modules.network.utils import get_s390_settings, netmask2prefix, prefix2netmask from pyanaconda.modules.network.config_file import is_config_file_for_system from pyanaconda.core.dbus import SystemBus +from pyanaconda.core import util from pyanaconda.anaconda_loggers import get_module_logger log = get_module_logger(__name__) @@ -437,12 +438,6 @@ def _update_wired_connection_with_s390_settings(connection, s390cfg): if s390cfg['SUBCHANNELS']: subchannels = s390cfg['SUBCHANNELS'].split(",") s_wired.props.s390_subchannels = subchannels - if s390cfg['NETTYPE']: - s_wired.props.s390_nettype = s390cfg['NETTYPE'] - if s390cfg['OPTIONS']: - opts = s390cfg['OPTIONS'].split(" ") - opts_dict = {k: v for k, v in (o.split("=") for o in opts)} - s_wired.props.s390_options = opts_dict def _create_new_connection(network_data, device_name): @@ -1379,15 +1374,10 @@ def _get_dracut_znet_argument_from_connection(connection): argument = "" wired_setting = connection.get_setting_wired() if wired_setting and is_s390(): - nettype = wired_setting.get_s390_nettype() - # get_s390_subchannels() returns a list of subchannels - subchannels = wired_setting.get_s390_subchannels() - if nettype and subchannels: - argument = "rd.znet={},{}".format(nettype, ",".join(subchannels)) - options = wired_setting.get_property(NM.SETTING_WIRED_S390_OPTIONS) - if options: - options_string = ','.join("{}={}".format(key, val) for key, val in options.items()) - argument += ",{}".format(options_string) + devspec = util.execWithCapture("/lib/s390-tools/zdev-to-rd.znet", + ["persistent", + connection.get_interface_name()]).strip() + argument = "rd.znet={}".format(devspec) return argument diff --git a/pyanaconda/modules/network/utils.py b/pyanaconda/modules/network/utils.py index 6d113a7a88d..445c05ef33b 100644 --- a/pyanaconda/modules/network/utils.py +++ b/pyanaconda/modules/network/utils.py @@ -35,8 +35,6 @@ def get_s390_settings(devname): cfg = { 'SUBCHANNELS': '', - 'NETTYPE': '', - 'OPTIONS': '' } subchannels = [] @@ -46,23 +44,6 @@ def get_s390_settings(devname): return cfg cfg['SUBCHANNELS'] = ','.join(subchannels) - # Example of the ccw.conf file content: - # qeth,0.0.0900,0.0.0901,0.0.0902,layer2=0,portname=FOOBAR,portno=0 - # - # SUBCHANNELS="0.0.0900,0.0.0901,0.0.0902" - # NETTYPE="qeth" - # OPTIONS="layer2=1 portname=FOOBAR portno=0" - if not os.path.exists('/run/install/ccw.conf'): - return cfg - with open('/run/install/ccw.conf') as f: - # pylint: disable=redefined-outer-name - for line in f: - if cfg['SUBCHANNELS'] in line: - items = line.strip().split(',') - cfg['NETTYPE'] = items[0] - cfg['OPTIONS'] = " ".join(i for i in items[1:] if '=' in i) - break - return cfg diff --git a/pyanaconda/modules/storage/dasd/discover.py b/pyanaconda/modules/storage/dasd/discover.py index 392aeecef66..e848da99bc0 100644 --- a/pyanaconda/modules/storage/dasd/discover.py +++ b/pyanaconda/modules/storage/dasd/discover.py @@ -22,6 +22,7 @@ from pyanaconda.core.regexes import DASD_DEVICE_NUMBER from pyanaconda.modules.common.task import Task from pyanaconda.modules.common.errors.configuration import StorageDiscoveryError +from pyanaconda.core.util import execWithRedirect class DASDDiscoverTask(Task): @@ -58,6 +59,12 @@ def _discover_device(self): """Discover the device.""" # pylint: disable=try-except-raise try: - blockdev.s390.dasd_online(self._device_number) - except blockdev.S390Error as e: + rc = execWithRedirect("chzdev", + ["--enable", "dasd", self._device_number, + "--active", "--persistent", + "--yes", "--no-root-update", "--force"]) + except RuntimeError as e: raise StorageDiscoveryError(str(e)) from e + if rc != 0: + raise StorageDiscoveryError( + "Could not set the device online. It might not exist.") diff --git a/pyanaconda/modules/storage/installation.py b/pyanaconda/modules/storage/installation.py index d3a90d26d6c..9889589e7a8 100644 --- a/pyanaconda/modules/storage/installation.py +++ b/pyanaconda/modules/storage/installation.py @@ -32,10 +32,10 @@ from pyanaconda.anaconda_loggers import get_module_logger from pyanaconda.core.i18n import _ -from pyanaconda.core.util import join_paths +from pyanaconda.core.util import join_paths, execWithRedirect from pyanaconda.core.path import make_directories from pyanaconda.core.configuration.anaconda import conf -from pyanaconda.modules.common.constants.objects import ISCSI, FCOE, ZFCP, NVME +from pyanaconda.modules.common.constants.objects import ISCSI, FCOE, NVME from pyanaconda.modules.common.constants.services import STORAGE from pyanaconda.modules.common.errors.installation import StorageInstallationError from pyanaconda.modules.common.task import Task @@ -290,13 +290,10 @@ def _write_storage_configuration(self, storage, sysroot=None): fcoe_proxy = STORAGE.get_proxy(FCOE) fcoe_proxy.WriteConfiguration() - zfcp_proxy = STORAGE.get_proxy(ZFCP) - zfcp_proxy.WriteConfiguration() - nvme_proxy = STORAGE.get_proxy(NVME) nvme_proxy.WriteConfiguration() - self._write_dasd_conf(storage, sysroot) + self._write_s390_device_config(sysroot) def _write_escrow_packets(self, storage, sysroot): """Write the escrow packets. @@ -361,46 +358,21 @@ def _write_lvm_devices_file(storage, sysroot): make_directories(os.path.dirname(out_filename)) shutil.copyfile(in_filename, out_filename) - def _write_dasd_conf(self, storage, sysroot): - """Write DASD configuration to sysroot. + def _write_s390_device_config(self, sysroot): + """Copy entire persistent config of any s390 devices to sysroot. - Write /etc/dasd.conf to target system for all DASD devices - configured during installation. + This includes config imported from initrd as well as anything the user + configured via the installer user interface. - :param storage: the storage object :param sysroot: a path to the target OS installation """ - dasds = [d for d in storage.devices if d.type == "dasd"] - dasds.sort(key=lambda d: d.name) - if not (arch.is_s390() and dasds): - return - - with open(os.path.realpath(sysroot + "/etc/dasd.conf"), "w") as f: - for dasd in dasds: - fields = [dasd.busid] + dasd.get_opts() - f.write("%s\n" % " ".join(fields),) - - # check for hyper PAV aliases; they need to get added to dasd.conf as well - sysfs = "/sys/bus/ccw/drivers/dasd-eckd" - - # in the case that someone is installing with *only* FBA DASDs,the above - # sysfs path will not exist; so check for it and just bail out of here if - # that's the case - if not os.path.exists(sysfs): - return - - # this does catch every DASD, even non-aliases, but we're only going to be - # checking for a very specific flag, so there won't be any duplicate entries - # in dasd.conf - devs = [d for d in os.listdir(sysfs) if d.startswith("0.0")] - with open(os.path.realpath(sysroot + "/etc/dasd.conf"), "a") as f: - for d in devs: - aliasfile = "%s/%s/alias" % (sysfs, d) - with open(aliasfile, "r") as falias: - alias = falias.read().strip() - - # if alias == 1, then the device is an alias; otherwise it is a - # normal dasd (alias == 0) and we can skip it, since it will have - # been added to dasd.conf in the above block of code - if alias == "1": - f.write("%s\n" % d) + if arch.is_s390(): + execWithRedirect("chzdev", + ["--export", "/tmp/zdev.config", + "--all", "--type", "--persistent", + "--verbose"]) + execWithRedirect("chzdev", + ["--import", "/tmp/zdev.config", + "--persistent", + "--yes", "--no-root-update", "--force", "--verbose", + "--base", "/etc=%s/etc" % sysroot]) diff --git a/tests/unit_tests/pyanaconda_tests/modules/network/test_module_network_nm_client.py b/tests/unit_tests/pyanaconda_tests/modules/network/test_module_network_nm_client.py index 71abfbe747d..76762d36f8d 100644 --- a/tests/unit_tests/pyanaconda_tests/modules/network/test_module_network_nm_client.py +++ b/tests/unit_tests/pyanaconda_tests/modules/network/test_module_network_nm_client.py @@ -116,17 +116,20 @@ def test_get_ports_from_connections(self, get_iface_from_connection): assert get_ports_from_connections(nm_client, "team", [TEAM1_UUID]) == \ set([("ens11", "ens11", ENS11_UUID)]) + @patch("pyanaconda.modules.network.nm_client._get_dracut_znet_argument_from_connection") @patch("pyanaconda.modules.network.nm_client.get_connections_available_for_iface") @patch("pyanaconda.modules.network.nm_client.get_ports_from_connections") @patch("pyanaconda.modules.network.nm_client.is_s390") def test_get_dracut_arguments_from_connection(self, is_s390, get_ports_from_connections_mock, - get_connections_available_for_iface): + get_connections_available_for_iface, + _get_dracut_znet_argument_from_connection): nm_client = Mock() CON_UUID = "44755f4c-ee12-45b4-ba5e-e10f83de51af" # IPv4 config auto, IPv6 config auto, mac address specified is_s390.return_value = False + _get_dracut_znet_argument_from_connection.return_value = "" ip4_config_attrs = { "get_method.return_value": NM.SETTING_IP4_CONFIG_METHOD_AUTO, } @@ -162,6 +165,8 @@ def test_get_dracut_arguments_from_connection(self, is_s390, get_ports_from_conn # IPv4 config static, mac address not specified, s390 is_s390.return_value = True + _get_dracut_znet_argument_from_connection.return_value = \ + "rd.znet=qeth,0.0.0900,0.0.0901,0.0.0902,layer2=1,portname=FOOBAR,portno=0" address_attrs = { "get_address.return_value": "10.34.39.44", "get_prefix.return_value": 24, @@ -176,11 +181,6 @@ def test_get_dracut_arguments_from_connection(self, is_s390, get_ports_from_conn ip4_config = self._get_mock_objects_from_attrs([ip4_config_attrs])[0] wired_setting_attrs = { "get_mac_address.return_value": None, - "get_s390_nettype.return_value": "qeth", - "get_s390_subchannels.return_value": ["0.0.0900", "0.0.0901", "0.0.0902"], - "get_property.return_value": {"layer2": "1", - "portname": "FOOBAR", - "portno": "0"}, } wired_setting = self._get_mock_objects_from_attrs([wired_setting_attrs])[0] cons_attrs = [ @@ -199,6 +199,7 @@ def test_get_dracut_arguments_from_connection(self, is_s390, get_ports_from_conn # IPv6 config dhcp is_s390.return_value = False + _get_dracut_znet_argument_from_connection.return_value = "" ip6_config_attrs = { "get_method.return_value": NM.SETTING_IP6_CONFIG_METHOD_DHCP, } @@ -222,6 +223,7 @@ def test_get_dracut_arguments_from_connection(self, is_s390, get_ports_from_conn # IPv6 config manual is_s390.return_value = False + _get_dracut_znet_argument_from_connection.return_value = "" address_attrs = { "get_address.return_value": "2001::5", "get_prefix.return_value": 64, @@ -253,6 +255,7 @@ def test_get_dracut_arguments_from_connection(self, is_s390, get_ports_from_conn # IPv4 config auto, team is_s390.return_value = False + _get_dracut_znet_argument_from_connection.return_value = "" ip4_config_attrs = { "get_method.return_value": NM.SETTING_IP4_CONFIG_METHOD_AUTO, } @@ -279,6 +282,8 @@ def test_get_dracut_arguments_from_connection(self, is_s390, get_ports_from_conn # IPv4 config auto, vlan, s390, parent specified by interface name is_s390.return_value = True + _get_dracut_znet_argument_from_connection.return_value = \ + "rd.znet=qeth,0.0.0900,0.0.0901,0.0.0902,layer2=1,portname=FOOBAR,portno=0" ip4_config_attrs = { "get_method.return_value": NM.SETTING_IP4_CONFIG_METHOD_AUTO, } @@ -300,11 +305,6 @@ def test_get_dracut_arguments_from_connection(self, is_s390, get_ports_from_conn # Mock parent connection wired_setting_attrs = { "get_mac_address.return_value": None, - "get_s390_nettype.return_value": "qeth", - "get_s390_subchannels.return_value": ["0.0.0900", "0.0.0901", "0.0.0902"], - "get_property.return_value": {"layer2": "1", - "portname": "FOOBAR", - "portno": "0"}, } wired_setting = self._get_mock_objects_from_attrs([wired_setting_attrs])[0] parent_cons_attrs = [ @@ -326,6 +326,7 @@ def test_get_dracut_arguments_from_connection(self, is_s390, get_ports_from_conn # IPv4 config auto, vlan, parent specified by connection uuid VLAN_PARENT_UUID = "5e6ead30-d133-4c8c-ba59-818c5ced6a7c" is_s390.return_value = False + _get_dracut_znet_argument_from_connection.return_value = "" ip4_config_attrs = { "get_method.return_value": NM.SETTING_IP4_CONFIG_METHOD_AUTO, } @@ -361,6 +362,8 @@ def test_get_dracut_arguments_from_connection(self, is_s390, get_ports_from_conn # IPv4 config auto, vlan, parent specified by connection uuid, s390 (we # need the parent connection in s390 case, not only parent iface) is_s390.return_value = True + _get_dracut_znet_argument_from_connection.return_value = \ + "rd.znet=qeth,0.0.0900,0.0.0901,0.0.0902,layer2=1,portname=FOOBAR,portno=0" ip4_config_attrs = { "get_method.return_value": NM.SETTING_IP4_CONFIG_METHOD_AUTO, } @@ -382,11 +385,6 @@ def test_get_dracut_arguments_from_connection(self, is_s390, get_ports_from_conn # Mock parent connection wired_setting_attrs = { "get_mac_address.return_value": None, - "get_s390_nettype.return_value": "qeth", - "get_s390_subchannels.return_value": ["0.0.0900", "0.0.0901", "0.0.0902"], - "get_property.return_value": {"layer2": "1", - "portname": "FOOBAR", - "portno": "0"}, } wired_setting = self._get_mock_objects_from_attrs([wired_setting_attrs])[0] parent_cons_attrs = [ diff --git a/tests/unit_tests/pyanaconda_tests/modules/storage/test_module_dasd.py b/tests/unit_tests/pyanaconda_tests/modules/storage/test_module_dasd.py index ed5c40ce9e9..c6ff5ca7911 100644 --- a/tests/unit_tests/pyanaconda_tests/modules/storage/test_module_dasd.py +++ b/tests/unit_tests/pyanaconda_tests/modules/storage/test_module_dasd.py @@ -83,8 +83,7 @@ def test_find_formattable(self, blockdev): "dev1", fmt=get_format("ext4"), size=Size("10 GiB"), - busid="0.0.0201", - opts={} + busid="0.0.0201" ) ) @@ -123,14 +122,15 @@ def test_discovery_fails(self): with pytest.raises(StorageDiscoveryError): DASDDiscoverTask("x.y.z").run() + @patch('pyanaconda.modules.storage.dasd.discover.execWithRedirect') @patch('pyanaconda.modules.storage.dasd.discover.blockdev') - def test_discovery(self, blockdev): + def test_discovery(self, blockdev, execWithRedirect): """Test the discovery task.""" + execWithRedirect.return_value = 0 DASDDiscoverTask("0.0.A100").run() blockdev.s390.sanitize_dev_input.assert_called_once_with("0.0.A100") - sanitized_input = blockdev.s390.sanitize_dev_input.return_value - blockdev.s390.dasd_online.assert_called_once_with(sanitized_input) + execWithRedirect.assert_called_once() @patch('pyanaconda.modules.storage.dasd.format.blockdev') def test_format(self, blockdev): diff --git a/tests/unit_tests/pyanaconda_tests/modules/storage/test_module_device_tree.py b/tests/unit_tests/pyanaconda_tests/modules/storage/test_module_device_tree.py index 4d8bf0d7a60..5858a5b4a4f 100644 --- a/tests/unit_tests/pyanaconda_tests/modules/storage/test_module_device_tree.py +++ b/tests/unit_tests/pyanaconda_tests/modules/storage/test_module_device_tree.py @@ -220,8 +220,7 @@ def test_get_dasd_device_data(self): "dev1", fmt=get_format("ext4"), size=Size("10 GiB"), - busid="0.0.0201", - opts={} + busid="0.0.0201" )) data = self.interface.GetDeviceData("dev1")