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
95 changes: 91 additions & 4 deletions coriolis/osmorphing/suse.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,15 @@

from coriolis import exception
from coriolis.osmorphing import base
from coriolis.osmorphing.netpreserver import ifcfg
from coriolis.osmorphing.netpreserver import nmconnection
from coriolis.osmorphing.osdetect import suse as suse_detect
from coriolis.osmorphing import redhat as redhat_osmorphing
from coriolis import utils

IFCFG_TEMPLATE = redhat_osmorphing.IFCFG_TEMPLATE
NMCONNECTION_TEMPLATE = redhat_osmorphing.NMCONNECTION_TEMPLATE

LOG = logging.getLogger(__name__)

DETECTED_SUSE_RELEASE_FIELD_NAME = suse_detect.DETECTED_SUSE_RELEASE_FIELD_NAME
Expand All @@ -29,6 +35,8 @@

class BaseSUSEMorphingTools(base.BaseLinuxOSMorphingTools):

_NETWORK_SCRIPTS_PATH = "etc/sysconfig/network-scripts"
_NM_CONNECTIONS_PATH = "etc/NetworkManager/system-connections"
BIOS_GRUB_LOCATION = "/boot/grub2"
UEFI_GRUB_LOCATION = "/boot/efi/EFI/suse"

Expand Down Expand Up @@ -61,12 +69,91 @@ def check_os_supported(cls, detected_os_info):
return False

def disable_predictable_nic_names(self):
# TODO(gsamfira): implement once we have networking support
pass
grub_cfg = "etc/default/grub"
if not self._test_path(grub_cfg):
LOG.warning(
"Could not find /%s. Skipping predictable NIC names "
"disabling.", grub_cfg)
return
contents = self._read_file_sudo(grub_cfg)
cfg = utils.Grub2ConfigEditor(contents)
cfg.append_to_option(
"GRUB_CMDLINE_LINUX_DEFAULT",
{"opt_type": "key_val", "opt_key": "net.ifnames", "opt_val": 0})
cfg.append_to_option(
"GRUB_CMDLINE_LINUX_DEFAULT",
{"opt_type": "key_val", "opt_key": "biosdevname", "opt_val": 0})
cfg.append_to_option(
"GRUB_CMDLINE_LINUX",
{"opt_type": "key_val", "opt_key": "net.ifnames", "opt_val": 0})
cfg.append_to_option(
"GRUB_CMDLINE_LINUX",
{"opt_type": "key_val", "opt_key": "biosdevname", "opt_val": 0})
self._write_file_sudo("etc/default/grub", cfg.dump())
self._execute_update_grub()

def _get_nmconnection_net_preserver(self):
return nmconnection.NmconnectionNetPreserver(self)

def _get_ifcfg_net_preserver(self):
return ifcfg.IfcfgNetPreserver(self)

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

def _write_nic_configs(self, nics_info):
for idx, _ in enumerate(nics_info):
dev_name = "eth%d" % idx
cfg_path = "%s/ifcfg-%s" % (self._NETWORK_SCRIPTS_PATH, 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,
IFCFG_TEMPLATE % {
"device_name": dev_name,
"nm_controlled": self._get_ifcfg_nm_controlled(),
})

def _write_nmconnection_configs(self, nics_info):
nm_net_preserver = self._get_nmconnection_net_preserver()
nm_net_preserver.backup_nmconnection_files()
device_names = ["eth%d" % idx for idx, _ in enumerate(nics_info)]
self._get_ifcfg_net_preserver().backup_ifcfg_configs(device_names)

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 _write_dhcp_net_config(self, nics_info):
self.disable_predictable_nic_names()
ethernet_keyfiles = None
if self._test_path(self._NM_CONNECTIONS_PATH):
ethernet_keyfiles = (
self._get_nmconnection_net_preserver().get_ethernet_keyfiles())
if ethernet_keyfiles:
self._write_nmconnection_configs(nics_info)
else:
self._write_nic_configs(nics_info)

def set_net_config(self, nics_info, dhcp):
# TODO(alexpilotti): add networking support
pass
if dhcp:
self._write_dhcp_net_config(nics_info)
return

LOG.info("Setting static IP configuration")
self._setup_network_preservation(nics_info)

def get_installed_packages(self):
cmd = 'rpm -qa --qf "%{NAME}\\n"'
Expand Down
209 changes: 209 additions & 0 deletions coriolis/tests/osmorphing/test_suse.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

from coriolis import exception
from coriolis.osmorphing import base
from coriolis.osmorphing.netpreserver import ifcfg
from coriolis.osmorphing.netpreserver import nmconnection
from coriolis.osmorphing import suse
from coriolis.tests import test_base

Expand Down Expand Up @@ -434,3 +436,210 @@ def test_pre_packages_install_no_packages(
mock_super_pre.assert_called_once_with([])
mock_enable_sles_module.assert_not_called()
mock_add_cloud_tools_repo.assert_not_called()

def test__get_nmconnection_net_preserver(self):
result = self.morphing_tools._get_nmconnection_net_preserver()

self.assertIsInstance(result, nmconnection.NmconnectionNetPreserver)

def test__get_ifcfg_net_preserver(self):
result = self.morphing_tools._get_ifcfg_net_preserver()

self.assertIsInstance(result, ifcfg.IfcfgNetPreserver)

def test__get_ifcfg_nm_controlled_old_version(self):
result = self.morphing_tools._get_ifcfg_nm_controlled()

self.assertEqual("no", result)

def test__get_ifcfg_nm_controlled_sles15(self):
self.morphing_tools._version = "15"

result = self.morphing_tools._get_ifcfg_nm_controlled()

self.assertEqual("yes", result)

@mock.patch.object(base.BaseLinuxOSMorphingTools, '_write_file_sudo')
@mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
@mock.patch.object(base.BaseLinuxOSMorphingTools, '_test_path')
def test__write_nic_configs_with_existing_file(
self, mock_test_path, mock_exec_cmd_chroot, mock_write_file_sudo):
nics_info = [{'name': 'eth0'}, {'name': 'eth1'}]
mock_test_path.return_value = True

self.morphing_tools._write_nic_configs(nics_info)

mock_exec_cmd_chroot.assert_has_calls([
mock.call("cp etc/sysconfig/network-scripts/ifcfg-eth0 "
"etc/sysconfig/network-scripts/ifcfg-eth0.bak"),
mock.call("cp etc/sysconfig/network-scripts/ifcfg-eth1 "
"etc/sysconfig/network-scripts/ifcfg-eth1.bak"),
])
mock_write_file_sudo.assert_has_calls([
mock.call(
"etc/sysconfig/network-scripts/ifcfg-eth0",
suse.IFCFG_TEMPLATE % {
"device_name": "eth0",
"nm_controlled": "no",
},
),
mock.call(
"etc/sysconfig/network-scripts/ifcfg-eth1",
suse.IFCFG_TEMPLATE % {
"device_name": "eth1",
"nm_controlled": "no",
},
),
])

@mock.patch.object(base.BaseLinuxOSMorphingTools, '_write_file_sudo')
@mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
@mock.patch.object(base.BaseLinuxOSMorphingTools, '_test_path')
def test__write_nic_configs_sles15_no_existing_file(
self, mock_test_path, mock_exec_cmd_chroot, mock_write_file_sudo):
self.morphing_tools._version = "15"
nics_info = [{'name': 'eth0'}]
mock_test_path.return_value = False

self.morphing_tools._write_nic_configs(nics_info)

mock_exec_cmd_chroot.assert_not_called()
mock_write_file_sudo.assert_called_once_with(
"etc/sysconfig/network-scripts/ifcfg-eth0",
suse.IFCFG_TEMPLATE % {
"device_name": "eth0",
"nm_controlled": "yes",
},
)

@mock.patch.object(
ifcfg.IfcfgNetPreserver, 'backup_ifcfg_configs'
)
@mock.patch.object(
nmconnection.NmconnectionNetPreserver, 'backup_nmconnection_files'
)
@mock.patch.object(
suse.BaseSUSEMorphingTools, '_get_ifcfg_net_preserver'
)
@mock.patch.object(
suse.BaseSUSEMorphingTools, '_get_nmconnection_net_preserver'
)
@mock.patch.object(base.BaseLinuxOSMorphingTools, '_write_file_sudo')
@mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
def test__write_nmconnection_configs(
self, mock_exec_cmd_chroot, mock_write_file_sudo,
mock_get_nmconnection_net_preserver,
mock_get_ifcfg_net_preserver,
mock_backup_nmconnection_files,
mock_backup_ifcfg_configs):
mock_nm_preserver = mock_get_nmconnection_net_preserver.return_value
mock_ifcfg_preserver = mock_get_ifcfg_net_preserver.return_value
nics_info = [{'name': 'eth0'}]

self.morphing_tools._write_nmconnection_configs(nics_info)

mock_nm_preserver.backup_nmconnection_files.assert_called_once_with()
mock_ifcfg_preserver.backup_ifcfg_configs.assert_called_once_with(
['eth0'])
mock_write_file_sudo.assert_called_once()
args, _ = mock_write_file_sudo.call_args
self.assertEqual(
args[0],
"etc/NetworkManager/system-connections/eth0.nmconnection")
self.assertIn("[connection]", args[1])
self.assertIn("interface-name=eth0", args[1])
self.assertIn("method=auto", args[1])
self.assertIn("may-fail=false", args[1])
mock_exec_cmd_chroot.assert_called_once_with(
"chmod 600 /etc/NetworkManager/system-connections/"
"eth0.nmconnection")

@mock.patch.object(suse.BaseSUSEMorphingTools, '_write_nic_configs')
@mock.patch.object(
suse.BaseSUSEMorphingTools, '_write_nmconnection_configs')
@mock.patch.object(
suse.BaseSUSEMorphingTools, '_get_nmconnection_net_preserver')
@mock.patch.object(
suse.BaseSUSEMorphingTools, 'disable_predictable_nic_names')
@mock.patch.object(base.BaseLinuxOSMorphingTools, '_test_path')
def test__write_dhcp_net_config_no_nm_path(
self, mock_test_path, mock_disable_predictable_nic_names,
mock_get_nm_preserver, mock_write_nmconnection_configs,
mock_write_nic_configs):
mock_test_path.return_value = False
nics_info = [{'name': 'eth0'}]

self.morphing_tools._write_dhcp_net_config(nics_info)

mock_disable_predictable_nic_names.assert_called_once()
mock_test_path.assert_called_once_with(
"etc/NetworkManager/system-connections")
mock_get_nm_preserver.assert_not_called()
mock_write_nic_configs.assert_called_once_with(nics_info)
mock_write_nmconnection_configs.assert_not_called()

@mock.patch.object(suse.BaseSUSEMorphingTools, '_write_nic_configs')
@mock.patch.object(
suse.BaseSUSEMorphingTools, '_write_nmconnection_configs')
@mock.patch.object(
suse.BaseSUSEMorphingTools, '_get_nmconnection_net_preserver')
@mock.patch.object(
suse.BaseSUSEMorphingTools, 'disable_predictable_nic_names')
@mock.patch.object(base.BaseLinuxOSMorphingTools, '_test_path')
def test__write_dhcp_net_config_no_ethernet_keyfiles(
self, mock_test_path, mock_disable_predictable_nic_names,
mock_get_nm_preserver, mock_write_nmconnection_configs,
mock_write_nic_configs):
mock_test_path.return_value = True
mock_nm_preserver = mock_get_nm_preserver.return_value
mock_nm_preserver.get_ethernet_keyfiles.return_value = []
nics_info = [{'name': 'eth0'}]

self.morphing_tools._write_dhcp_net_config(nics_info)

mock_disable_predictable_nic_names.assert_called_once()
mock_nm_preserver.get_ethernet_keyfiles.assert_called_once_with()
mock_write_nic_configs.assert_called_once_with(nics_info)
mock_write_nmconnection_configs.assert_not_called()

@mock.patch.object(suse.BaseSUSEMorphingTools, '_write_nic_configs')
@mock.patch.object(
suse.BaseSUSEMorphingTools, '_write_nmconnection_configs')
@mock.patch.object(
suse.BaseSUSEMorphingTools, '_get_nmconnection_net_preserver')
@mock.patch.object(
suse.BaseSUSEMorphingTools, 'disable_predictable_nic_names')
@mock.patch.object(base.BaseLinuxOSMorphingTools, '_test_path')
def test__write_dhcp_net_config_with_ethernet_keyfiles(
self, mock_test_path, mock_disable_predictable_nic_names,
mock_get_nm_preserver, mock_write_nmconnection_configs,
mock_write_nic_configs):
mock_test_path.return_value = True
mock_nm_preserver = mock_get_nm_preserver.return_value
mock_nm_preserver.get_ethernet_keyfiles.return_value = [
('etc/NetworkManager/system-connections/eth0.nmconnection', {})]
nics_info = [{'name': 'eth0'}]

self.morphing_tools._write_dhcp_net_config(nics_info)

mock_disable_predictable_nic_names.assert_called_once()
mock_nm_preserver.get_ethernet_keyfiles.assert_called_once_with()
mock_write_nmconnection_configs.assert_called_once_with(nics_info)
mock_write_nic_configs.assert_not_called()

@mock.patch.object(suse.BaseSUSEMorphingTools, '_write_dhcp_net_config')
def test_set_net_config_dhcp(self, mock_write_dhcp_net_config):
nics_info = [{'name': 'eth0'}]

self.morphing_tools.set_net_config(nics_info, dhcp=True)

mock_write_dhcp_net_config.assert_called_once_with(nics_info)

@mock.patch.object(
base.BaseLinuxOSMorphingTools, '_setup_network_preservation')
def test_set_net_config_static(self, mock_setup_network_preservation):
nics_info = [{'name': 'eth0'}]

self.morphing_tools.set_net_config(nics_info, dhcp=False)

mock_setup_network_preservation.assert_called_once_with(nics_info)
Loading