Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions coriolis/osmorphing/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,8 @@ def set_environment(self, environment):
class BaseLinuxOSMorphingTools(BaseOSMorphingTools):

_packages = {}
_NETWORK_SCRIPTS_PATH = "etc/sysconfig/network-scripts"
_NM_CONNECTIONS_PATH = "etc/NetworkManager/system-connections"

def __init__(self, conn, os_root_dir, os_root_dev, hypervisor,
event_manager, detected_os_info, osmorphing_parameters,
Expand Down Expand Up @@ -432,6 +434,37 @@ def _read_config_file_sudo(self, chroot_path, check_exists=False):
config = utils.parse_ini_config(content)
return config

def _get_net_config_files(self, network_scripts_path):
dir_content = self._list_dir(network_scripts_path)
return [os.path.join(network_scripts_path, f) for f in
dir_content if re.match("^ifcfg-(.*)", f)]

def _get_ifcfgs_by_type(self, ifcfg_type, network_scripts_path):
ifcfgs = []
for ifcfg_file in self._get_net_config_files(network_scripts_path):
ifcfg = self._read_config_file_sudo(ifcfg_file)
detected_type = ifcfg.get('TYPE')
if not detected_type:
detected_type = "Ethernet"
ifcfg["TYPE"] = detected_type
if detected_type.lower() == ifcfg_type.lower():
ifcfgs.append((ifcfg_file, ifcfg))
return ifcfgs

def _get_nmconnection_files(self, network_scripts_path):
dir_content = self._list_dir(network_scripts_path)
return [os.path.join(network_scripts_path, f)
for f in dir_content if re.match(
r"^(.*\.nmconnection)$", f)]

def _get_keyfiles_by_type(self, nmconnection_type, network_scripts_path):
keyfiles = []
for file in self._get_nmconnection_files(network_scripts_path):
keyfile = self._read_config_file_sudo(file)
if keyfile.get("type") == nmconnection_type:
keyfiles.append((file, keyfile))
return keyfiles

def _copy_resolv_conf(self):
resolv_conf = "etc/resolv.conf"
resolv_conf_path = os.path.join(self._os_root_dir, resolv_conf)
Expand Down
39 changes: 11 additions & 28 deletions coriolis/osmorphing/netpreserver/ifcfg.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# Copyright 2025 Cloudbase Solutions Srl
# All Rights Reserved.

import os
import re

from oslo_log import log as logging
Expand All @@ -13,23 +12,24 @@

class IfcfgNetPreserver(base.BaseNetPreserver):

def __init__(self, osmorphing_tool):
super(IfcfgNetPreserver, self).__init__(osmorphing_tool)
self.network_scripts_path = "etc/sysconfig/network-scripts"

def check_net_preserver(self):
if self.osmorphing_tool._test_path(self.network_scripts_path):
ifcfg_files = self._get_net_config_files(self.network_scripts_path)
network_scripts_path = (
self.osmorphing_tool._NETWORK_SCRIPTS_PATH)
if self.osmorphing_tool._test_path(network_scripts_path):
ifcfg_files = self.osmorphing_tool._get_net_config_files(
network_scripts_path)
if ifcfg_files:
ifcfgs_ethernet = self._get_ifcfgs_by_type(
"Ethernet", self.network_scripts_path)
ifcfgs_ethernet = self.osmorphing_tool._get_ifcfgs_by_type(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a future improvement (in another PR), can you please make the netpreservers NOT read through the files twice? Maybe just re-use the object in the factory instead of the class, or return this collection of interface files instead of a bool value, and pass it down to parse_network.

"Ethernet", network_scripts_path)
if ifcfgs_ethernet:
return True
return False

def parse_network(self):
ifcfgs_ethernet = self._get_ifcfgs_by_type(
"Ethernet", self.network_scripts_path)
network_scripts_path = (
self.osmorphing_tool._NETWORK_SCRIPTS_PATH)
ifcfgs_ethernet = self.osmorphing_tool._get_ifcfgs_by_type(
"Ethernet", network_scripts_path)
if ifcfgs_ethernet:
for ifcfg_file, ifcfg in ifcfgs_ethernet:
name = ifcfg.get("DEVICE")
Expand All @@ -42,20 +42,3 @@ def parse_network(self):
"mac_address": mac_address,
"ip_addresses": [ip_address] if ip_address else []
}

def _get_net_config_files(self, network_scripts_path):
dir_content = self.osmorphing_tool._list_dir(network_scripts_path)
return [os.path.join(network_scripts_path, f) for f in
dir_content if re.match("^ifcfg-(.*)", f)]

def _get_ifcfgs_by_type(self, ifcfg_type, network_scripts_path):
ifcfgs = []
for ifcfg_file in self._get_net_config_files(network_scripts_path):
ifcfg = self.osmorphing_tool._read_config_file_sudo(ifcfg_file)
detected_type = ifcfg.get('TYPE')
if not detected_type:
detected_type = "Ethernet"
ifcfg["TYPE"] = detected_type
if detected_type.lower() == ifcfg_type.lower():
ifcfgs.append((ifcfg_file, ifcfg))
return ifcfgs
36 changes: 10 additions & 26 deletions coriolis/osmorphing/netpreserver/nmconnection.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# Copyright 2025 Cloudbase Solutions Srl
# All Rights Reserved.

import os
import re

from oslo_log import log as logging
Expand All @@ -13,24 +12,23 @@

class NmconnectionNetPreserver(base.BaseNetPreserver):

def __init__(self, osmorphing_tool):
super(NmconnectionNetPreserver, self).__init__(osmorphing_tool)
self.nmconnection_file = "etc/NetworkManager/system-connections"

def check_net_preserver(self):
if self.osmorphing_tool._test_path(self.nmconnection_file):
nmconnection_files = self._get_nmconnection_files(
self.nmconnection_file)
nmconnection_path = self.osmorphing_tool._NM_CONNECTIONS_PATH
if self.osmorphing_tool._test_path(nmconnection_path):
nmconnection_files = self.osmorphing_tool._get_nmconnection_files(
nmconnection_path)
if nmconnection_files:
nmconnection_ethernet = self._get_keyfiles_by_type(
"ethernet", self.nmconnection_file)
nmconnection_ethernet = (
self.osmorphing_tool._get_keyfiles_by_type(
"ethernet", nmconnection_path))
if nmconnection_ethernet:
return True
return False

def parse_network(self):
nmconnection_ethernet = self._get_keyfiles_by_type(
"ethernet", self.nmconnection_file)
nmconnection_path = self.osmorphing_tool._NM_CONNECTIONS_PATH
nmconnection_ethernet = self.osmorphing_tool._get_keyfiles_by_type(
"ethernet", nmconnection_path)
if nmconnection_ethernet:
for nmconn_file, nmconn in nmconnection_ethernet:
name = nmconn.get("interface-name", nmconn.get("id"))
Expand All @@ -56,17 +54,3 @@ def parse_network(self):
"Could not find MAC address or IP addresses for "
"interface '%s' in nmconnection configuration "
"'%s'", name, nmconn_file)

def _get_nmconnection_files(self, network_scripts_path):
dir_content = self.osmorphing_tool._list_dir(network_scripts_path)
return [os.path.join(network_scripts_path, f)
for f in dir_content if re.match(
r"^(.*\.nmconnection)$", f)]

def _get_keyfiles_by_type(self, nmconnection_type, network_scripts_path):
keyfiles = []
for file in self._get_nmconnection_files(network_scripts_path):
keyfile = self.osmorphing_tool._read_config_file_sudo(file)
if keyfile.get("type") == nmconnection_type:
keyfiles.append((file, keyfile))
return keyfiles
127 changes: 103 additions & 24 deletions coriolis/osmorphing/redhat.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,29 @@
NAME=%(device_name)s
DEVICE=%(device_name)s
ONBOOT=yes
NM_CONTROLLED=no
NM_CONTROLLED=%(nm_controlled)s
"""

NMCONNECTION_TEMPLATE = """[connection]
id=%(device_name)s
uuid=%(connection_uuid)s
type=ethernet
interface-name=%(device_name)s
autoconnect=true

[ethernet]

[ipv4]
method=auto
may-fail=false

[ipv6]
method=auto
addr-gen-mode=default
"""


class BaseRedHatMorphingTools(base.BaseLinuxOSMorphingTools):
_NETWORK_SCRIPTS_PATH = "etc/sysconfig/network-scripts"
BIOS_GRUB_LOCATION = "/boot/grub2"
UEFI_GRUB_LOCATION = "/boot/efi/EFI/redhat"

Expand Down Expand Up @@ -95,22 +112,27 @@ def _has_systemd(self):
except Exception:
return False

def _get_ifcfg_nm_controlled(self):
if self._version_supported_util(self._version, minimum=8):
return "yes"
return "no"

def _set_dhcp_net_config(self, ifcfgs_ethernet):
for ifcfg_file, ifcfg in ifcfgs_ethernet:
if ifcfg.get("BOOTPROTO") == "none":
ifcfg["BOOTPROTO"] = "dhcp"
ifcfg["UUID"] = str(uuid.uuid4())

if 'IPADDR' in ifcfg:
del ifcfg['IPADDR']
if 'GATEWAY' in ifcfg:
del ifcfg['GATEWAY']
if 'NETMASK' in ifcfg:
del ifcfg['NETMASK']
if 'NETWORK' in ifcfg:
del ifcfg['NETWORK']

self._write_config_file(ifcfg_file, ifcfg)
for ifcfg_file, iface_cfg in ifcfgs_ethernet:
if iface_cfg.get("BOOTPROTO") == "none":
iface_cfg["BOOTPROTO"] = "dhcp"
iface_cfg["UUID"] = str(uuid.uuid4())

if 'IPADDR' in iface_cfg:
del iface_cfg['IPADDR']
if 'GATEWAY' in iface_cfg:
del iface_cfg['GATEWAY']
if 'NETMASK' in iface_cfg:
del iface_cfg['NETMASK']
if 'NETWORK' in iface_cfg:
del iface_cfg['NETWORK']

self._write_config_file(ifcfg_file, iface_cfg)

network_cfg_file = "etc/sysconfig/network"
network_cfg = self._read_config_file(network_cfg_file,
Expand All @@ -119,19 +141,67 @@ def _set_dhcp_net_config(self, ifcfgs_ethernet):
del network_cfg["GATEWAY"]
self._write_config_file(network_cfg_file, network_cfg)

def _get_existing_ethernet_nmconnection_files(self):
if not self._test_path(self._NM_CONNECTIONS_PATH):
return []
return [cfg_path for cfg_path, _ in self._get_keyfiles_by_type(
"ethernet", self._NM_CONNECTIONS_PATH)]

def _backup_nmconnection_files(self, nmconnection_files=None,
backup_file_suffix=".bak"):
if nmconnection_files is None:
nmconnection_files = (
self._get_existing_ethernet_nmconnection_files())
for cfg_path in nmconnection_files:
self._exec_cmd_chroot(
'mv "%s" "%s%s"' % (cfg_path, cfg_path, backup_file_suffix))
LOG.debug("Backed up nmconnection profile '%s'", cfg_path)

def _backup_all_ifcfg_configs(self, backup_file_suffix=".bak"):
if not self._test_path(self._NETWORK_SCRIPTS_PATH):
return
for cfg_path, _ in self._get_ifcfgs_by_type(
"Ethernet", self._NETWORK_SCRIPTS_PATH):
if os.path.basename(cfg_path) == "ifcfg-lo":
continue
self._exec_cmd_chroot(
'mv "%s" "%s%s"' % (cfg_path, cfg_path, backup_file_suffix))
LOG.debug("Backed up ifcfg profile '%s'", cfg_path)

def _write_nic_configs(self, nics_info):
self._backup_all_ifcfg_configs()
for idx, _ in enumerate(nics_info or []):
dev_name = "eth%d" % idx
cfg_path = "etc/sysconfig/network-scripts/ifcfg-%s" % dev_name
if self._test_path(cfg_path):
self._exec_cmd_chroot(
"cp %s %s.bak" % (cfg_path, cfg_path)
)
cfg_path = "%s/ifcfg-%s" % (self._NETWORK_SCRIPTS_PATH, dev_name)
self._write_file_sudo(
cfg_path,
IFCFG_TEMPLATE % {
"device_name": dev_name,
"nm_controlled": self._get_ifcfg_nm_controlled(),
})

def _write_nmconnection_configs(self, nics_info, nmconnection_files):
nics_info = nics_info or []
if not nics_info:
return

# Red Hat-based systems may have both nmconnection keyfiles and legacy
# ifcfg profiles; back up Ethernet profiles from both so stale source
# configs cannot override the freshly written DHCP profiles.
self._backup_nmconnection_files(nmconnection_files)
Comment thread
fabi200123 marked this conversation as resolved.
self._backup_all_ifcfg_configs()

for idx, _ in enumerate(nics_info):
dev_name = "eth%d" % idx
cfg_path = "%s/%s.nmconnection" % (
self._NM_CONNECTIONS_PATH, dev_name)
self._write_file_sudo(
cfg_path,
NMCONNECTION_TEMPLATE % {
"device_name": dev_name,
"connection_uuid": str(uuid.uuid4()),
})
self._exec_cmd_chroot("chmod 600 /%s" % cfg_path)

def _comment_keys_from_ifcfg_files(
self, keys, interfaces=None, backup_file_suffix=".bak"):
Expand All @@ -141,7 +211,7 @@ def _comment_keys_from_ifcfg_files(
if not interfaces:
interfaces = []
scripts_dir = os.path.join(
self._os_root_dir, "etc/sysconfig/network-scripts")
self._os_root_dir, self._NETWORK_SCRIPTS_PATH)
all_ifcfg_files = utils.list_ssh_dir(self._ssh, scripts_dir)
regex = "^(ifcfg-[a-z0-9]+)$"

Expand Down Expand Up @@ -169,8 +239,17 @@ def _comment_keys_from_ifcfg_files(

def set_net_config(self, nics_info, dhcp):
if dhcp:
nics_info = nics_info or []
if not nics_info:
return
self.disable_predictable_nic_names()
self._write_nic_configs(nics_info)
nmconnection_files = (
self._get_existing_ethernet_nmconnection_files())
if nmconnection_files:
self._write_nmconnection_configs(
nics_info, nmconnection_files)
else:
self._write_nic_configs(nics_info)
return

LOG.info("Setting static IP configuration")
Expand Down
Loading
Loading