From 65a0b38a169d28d3f3c1353f526acc7b40c18a66 Mon Sep 17 00:00:00 2001 From: Mihaela Balutoiu Date: Fri, 3 Oct 2025 14:03:08 +0300 Subject: [PATCH 1/3] Fix CentOS version detection for multi-digit releases Signed-off-by: Mihaela Balutoiu --- coriolis/osmorphing/osdetect/centos.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/coriolis/osmorphing/osdetect/centos.py b/coriolis/osmorphing/osdetect/centos.py index 0fe1c5e2d..4da014d30 100644 --- a/coriolis/osmorphing/osdetect/centos.py +++ b/coriolis/osmorphing/osdetect/centos.py @@ -22,10 +22,11 @@ def detect_os(self): release_info = self._read_file( redhat_release_path).decode().splitlines() if release_info: - m = re.match(r"^(.*) release ([0-9](\.[0-9])*)( \(.*\))?.*$", - release_info[0].strip()) + m = re.match( + r"^(.*) release ([0-9]+(?:\.[0-9]+)*)( \(.*\))?.*$", + release_info[0].strip()) if m: - distro, version, _, _ = m.groups() + distro, version, _ = m.groups() if CENTOS_DISTRO_IDENTIFIER not in distro: LOG.debug( "Distro does not appear to be a CentOS: %s", From 35e4a285f1463745e30f5cf2bac1f4aa786cc462 Mon Sep 17 00:00:00 2001 From: Mihaela Balutoiu Date: Fri, 3 Oct 2025 14:11:05 +0300 Subject: [PATCH 2/3] Add `NetworkManager` support for CentOS Stream 9+ `network configuration` Use `.nmconnection` files for network configuration on CentOS Stream 9+ while maintaining `ifcfg` compatibility for older versions Signed-off-by: Mihaela Balutoiu --- coriolis/osmorphing/redhat.py | 59 ++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/coriolis/osmorphing/redhat.py b/coriolis/osmorphing/redhat.py index 22d7ebfff..2f1eb511c 100644 --- a/coriolis/osmorphing/redhat.py +++ b/coriolis/osmorphing/redhat.py @@ -38,9 +38,28 @@ NM_CONTROLLED=no """ +NMCONNECTION_TEMPLATE = """[connection] +id=%(device_name)s +uuid=%(uuid)s +type=ethernet +interface-name=%(device_name)s + +[ethernet] + +[ipv4] +method=auto + +[ipv6] +addr-gen-mode=eui64 +method=auto + +[proxy] +""" + class BaseRedHatMorphingTools(base.BaseLinuxOSMorphingTools): _NETWORK_SCRIPTS_PATH = "etc/sysconfig/network-scripts" + _NMCONNECTION_PATH = "etc/NetworkManager/system-connections" BIOS_GRUB_LOCATION = "/boot/grub2" UEFI_GRUB_LOCATION = "/boot/efi/EFI/redhat" @@ -126,6 +145,29 @@ def _write_nic_configs(self, nics_info): "device_name": dev_name, }) + def _write_nmconnection_configs(self, nics_info): + nmconn_dir = self._NMCONNECTION_PATH + self._exec_cmd_chroot("mkdir -p /%s" % nmconn_dir) + + for idx, _ in enumerate(nics_info): + dev_name = "eth%d" % idx + connection_uuid = str(uuid.uuid4()) + cfg_path = "%s/%s.nmconnection" % (nmconn_dir, dev_name) + + if self._test_path(cfg_path): + self._exec_cmd_chroot( + "cp %s %s.bak" % (cfg_path, cfg_path) + ) + + self._write_file_sudo( + cfg_path, + NMCONNECTION_TEMPLATE % { + "device_name": dev_name, + "uuid": connection_uuid, + }) + + self._exec_cmd_chroot("chmod 600 /%s" % cfg_path) + def _comment_keys_from_ifcfg_files( self, keys, interfaces=None, backup_file_suffix=".bak"): """ Comments the provided list of keys from all 'ifcfg-*' files. @@ -160,10 +202,25 @@ def _comment_keys_from_ifcfg_files( "Commented all %s references from '%s'" % ( keys, fullpath)) + def _get_os_version(self): + try: + version_str = self._detected_os_info.get('release_version', '0') + major_version = int(version_str.split('.')[0]) + return major_version + except (ValueError, AttributeError): + LOG.warning( + "Could not parse OS version from detected_os_info: %s", + self._detected_os_info) + return 0 + def set_net_config(self, nics_info, dhcp): if dhcp: self.disable_predictable_nic_names() - self._write_nic_configs(nics_info) + os_version = self._get_os_version() + if os_version < 9: + self._write_nic_configs(nics_info) + else: + self._write_nmconnection_configs(nics_info) return LOG.info("Setting static IP configuration") From 6897c2e3f65de51abdde4651f0b08c4ecc395eeb Mon Sep 17 00:00:00 2001 From: Mihaela Balutoiu Date: Fri, 3 Oct 2025 14:22:13 +0300 Subject: [PATCH 3/3] Implement `latest` flag system for `OSMorphing` tools Enables marking tools as `latest` version to skip compatibility checks while validating uniqueness per module to ensure proper tool selection. Signed-off-by: Mihaela Balutoiu --- coriolis/osmorphing/base.py | 8 ++++++++ coriolis/osmorphing/manager.py | 21 +++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/coriolis/osmorphing/base.py b/coriolis/osmorphing/base.py index b1091c4eb..bed86b5f4 100644 --- a/coriolis/osmorphing/base.py +++ b/coriolis/osmorphing/base.py @@ -127,6 +127,8 @@ class BaseLinuxOSMorphingTools(BaseOSMorphingTools): _packages = {} + latest = False + def __init__(self, conn, os_root_dir, os_root_dev, hypervisor, event_manager, detected_os_info, osmorphing_parameters, operation_timeout=None): @@ -180,6 +182,12 @@ def _version_supported_util(cls, version, minimum, maximum=None): "release: %s", version_float, minimum, version) return False + if cls.latest: + LOG.debug( + "Skipping remaining version checks for latest osmorphing tool " + "class: %s", cls.__name__) + return True + if maximum: if maximum == minimum and version_float == minimum: LOG.debug( diff --git a/coriolis/osmorphing/manager.py b/coriolis/osmorphing/manager.py index 50493b8a1..976d487b2 100644 --- a/coriolis/osmorphing/manager.py +++ b/coriolis/osmorphing/manager.py @@ -85,6 +85,27 @@ def get_osmorphing_tools_class_for_provider( "and 'osmorphing_info' %s: %s", type(provider), os_type, osmorphing_info, available_tools_cls) + module_classes = {} + for toolscls in available_tools_cls: + module_name = toolscls.__module__ + if module_name not in module_classes: + module_classes[module_name] = [] + module_classes[module_name].append(toolscls) + + for module_name, classes in module_classes.items(): + latest_flags = [getattr(cls, 'latest', False) for cls in classes] + latest_count = sum(latest_flags) + + if latest_count > 1: + latest_classes = [ + cls.__name__ for cls in classes if getattr( + cls, 'latest', False)] + raise exception.InvalidOSMorphingTools( + "Provider class '%s' returned multiple 'latest' OSMorphing " + "tools from module '%s': %s. Only one class per module " + "can be marked as 'latest'." % ( + type(provider), module_name, latest_classes)) + osmorphing_base_class = base_osmorphing.BaseOSMorphingTools for toolscls in available_tools_cls: if not issubclass(toolscls, osmorphing_base_class):