From a5cb6c787cd9329aa4d4825c350a45785859a01f Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Tue, 20 Feb 2024 19:38:47 +0300 Subject: [PATCH 1/2] Activate all wired network devices with link in up state when running interactive installation --- anaconda.py | 3 + pyanaconda/modules/network/initialization.py | 44 +++++++++ pyanaconda/modules/network/network.py | 15 ++- .../modules/network/network_interface.py | 9 ++ pyanaconda/network.py | 91 +++++++++++++++++++ 5 files changed, 161 insertions(+), 1 deletion(-) diff --git a/anaconda.py b/anaconda.py index bbf801b..ae282e3 100755 --- a/anaconda.py +++ b/anaconda.py @@ -516,6 +516,9 @@ if __name__ == "__main__": # Initialize the network now, in case the display needs it from pyanaconda.network import initialize_network, wait_for_connecting_NM_thread, wait_for_connected_NM + import pyanaconda.network # @UnusedImport + pyanaconda.network.interactive_mode = anaconda.interactive_mode + initialize_network() # If required by user, wait for connection before starting the installation. if opts.waitfornet: diff --git a/pyanaconda/modules/network/initialization.py b/pyanaconda/modules/network/initialization.py index f61b88f..dc8035a 100644 --- a/pyanaconda/modules/network/initialization.py +++ b/pyanaconda/modules/network/initialization.py @@ -32,6 +32,7 @@ from pyanaconda.modules.network.ifcfg import get_ifcfg_file_of_device, find_ifcf from pyanaconda.modules.network.device_configuration import supported_wired_device_types, \ virtual_device_types from pyanaconda.modules.network.utils import guard_by_system_configuration +from pyanaconda.network import switch_all_network_devices log = get_module_logger(__name__) @@ -40,6 +41,49 @@ gi.require_version("NM", "1.0") from gi.repository import NM +class SwitchNetworkDevicesTask(Task): + """Task for switching all network devices ON""" + + def __init__(self, network_data, supported_devices, bootif, ifname_option_values): + """Create a new task. + + :param network_data: kickstart network data to be applied + :type: list(NetworkData) + :param supported_devices: list of names of supported network devices + :type supported_devices: list(str) + :param bootif: MAC addres of device to be used for --device=bootif specification + :type bootif: str + :param ifname_option_values: list of ifname boot option values + :type ifname_option_values: list(str) + """ + super().__init__() + self._network_data = network_data + self._supported_devices = supported_devices + self._bootif = bootif + self._ifname_option_values = ifname_option_values + + @property + def name(self): + return "Switch network devices on" + + def for_publication(self): + """Return a DBus representation.""" + return NetworkInitializationTaskInterface(self) + + @guard_by_system_configuration(return_value=[]) + def run(self): + """Run the switching network devices on. + + :returns: names of devices + :rtype: list(str) + """ + with nm_client_in_thread() as nm_client: + return self._run(nm_client) + + def _run(self, nm_client): + return switch_all_network_devices(nm_client) + + class ApplyKickstartTask(Task): """Task for application of kickstart network configuration.""" diff --git a/pyanaconda/modules/network/network.py b/pyanaconda/modules/network/network.py index 1e194c3..d436190 100644 --- a/pyanaconda/modules/network/network.py +++ b/pyanaconda/modules/network/network.py @@ -39,7 +39,7 @@ from pyanaconda.modules.network.ifcfg import get_kickstart_network_data, \ get_ifcfg_file, get_ifcfg_files_content from pyanaconda.modules.network.installation import NetworkInstallationTask, \ ConfigureActivationOnBootTask, HostnameConfigurationTask -from pyanaconda.modules.network.initialization import ApplyKickstartTask, \ +from pyanaconda.modules.network.initialization import ApplyKickstartTask, SwitchNetworkDevicesTask, \ ConsolidateInitramfsConnectionsTask, SetRealOnbootValuesFromKickstartTask, \ DumpMissingIfcfgFilesTask from pyanaconda.modules.network.utils import get_default_route_iface @@ -550,6 +550,19 @@ class NetworkService(KickstartService): self._ifname_option_values = values log.debug("ifname boot option values are set to %s", values) + def switch_network_devices_with_task(self): + """Switch all network devices ON + + :returns: a task switching all network devices ON + """ + supported_devices = [dev_info.device_name for dev_info in self.get_supported_devices()] + task = SwitchNetworkDevicesTask(self._original_network_data, + supported_devices, + self.bootif, + self.ifname_option_values) + task.succeeded_signal.connect(lambda: self.log_task_result(task, check_result=True)) + return task + def apply_kickstart_with_task(self): """Apply kickstart configuration which has not already been applied. diff --git a/pyanaconda/modules/network/network_interface.py b/pyanaconda/modules/network/network_interface.py index 6e904ba..27e6e3d 100644 --- a/pyanaconda/modules/network/network_interface.py +++ b/pyanaconda/modules/network/network_interface.py @@ -213,6 +213,15 @@ class NetworkInterface(KickstartModuleInterface): self.implementation.consolidate_initramfs_connections_with_task() ) + def SwitchNetworkDevicesWithTask(self) -> ObjPath: + """Switch network devices ON + + :returns: DBus path of the task switching network devices ON + """ + return TaskContainer.to_object_path( + self.implementation.switch_network_devices_with_task() + ) + def ApplyKickstartWithTask(self) -> ObjPath: """Apply kickstart configuration which has not already been applied. diff --git a/pyanaconda/network.py b/pyanaconda/network.py index d757934..f7fa48c 100644 --- a/pyanaconda/network.py +++ b/pyanaconda/network.py @@ -50,6 +50,8 @@ log = get_module_logger(__name__) DEFAULT_HOSTNAME = "localhost.localdomain" +interactive_mode = False + network_connected = None network_connected_condition = threading.Condition() @@ -272,6 +274,91 @@ def run_network_initialization_task(task_path): log.debug(msg) +def switch_all_network_devices(client, activate=True, check_link=True, wait=True): + """ + Switch ON (or OFF) all wired network devices with link in up state. + Return list of device names switched ON + :param client: Network Manager client object + :param activate: True = activate (ON), False = deactivate network devices + :param check_link: True = activate network devices with link in up state only + :param wait: True = wait for network devices to become active before exiting the function + :rtype list(str) + """ + action = "on" if activate else "off" + log.debug("start switching all devices %s", action) + if not client: + log.debug("NM client not available") + return [] + devices = client.get_devices() + if not devices: + log.debug("No devices found") + return [] + activated_ifaces = get_activated_devices(client) + active_connections = client.get_active_connections() + active_uuids = set([con.get_uuid() for con in active_connections]) + changed = False + ifaces = set() + for device in devices: + if not device: + continue + switched = False + iface = device.get_iface() + if not device_type_is_supported_wired(device.get_device_type()): + log.debug("device %s is not wired, skipping", iface) + continue + if activate: + if iface in activated_ifaces: + log.debug("device %s activated already, skipping", iface) + continue + elif iface not in activated_ifaces: + log.debug("device %s not activated already, skipping", iface) + continue + # check if the link for the device is up + if check_link and activate: + try: + carrier = device.get_carrier() + except AttributeError: + carrier = None + if carrier: + log.debug("device %s link is up", iface) + else: + log.debug("device %s link is down", iface) + # skip activating device if link is down + continue + connections = device.get_available_connections() + if not connections: + log.debug("No available connections for device %s", iface) + continue + for con in connections: + if not con: + continue + uuid = con.get_uuid() + if activate: + if uuid in active_uuids: + log.debug("connection %s for device %s is active already, skipping", uuid, iface) + continue + elif uuid not in active_uuids: + log.debug("connection %s for device %s is not active already, skipping", uuid, iface) + continue + if activate: + client.activate_connection_async(con, device, None, None) + ifaces.add(iface) + else: + device.disconnect(None) + switched = changed = True + if switched: + log.info("device %s switched %s", iface, action) + if not changed: + log.debug("No devices switched %s", action) + if wait and ifaces: + if wait_for_network_devices(ifaces): + log.debug("waiting succeeded: devices are active") + else: + log.debug("waiting timeout has expired") + log.debug("finish switching all devices %s", action) + return list(ifaces) + + def initialize_network(): """Initialize networking.""" if not conf.system.can_configure_network: @@ -290,6 +377,10 @@ def initialize_network(): run_network_initialization_task(network_proxy.ApplyKickstartWithTask()) run_network_initialization_task(network_proxy.DumpMissingIfcfgFilesWithTask()) run_network_initialization_task(network_proxy.SetRealOnbootValuesFromKickstartWithTask()) + if interactive_mode: + run_network_initialization_task(network_proxy.SwitchNetworkDevicesWithTask()) + else: + log.debug("Not activating all wired network devices in non-interactive mode") if network_proxy.Hostname == DEFAULT_HOSTNAME: bootopts_hostname = hostname_from_cmdline(kernel_arguments) -- 2.39.3