Skip to content

Commit 5c2b0cf

Browse files
Add suse network config support
Signed-off-by: Mihaela Balutoiu <mbalutoiu@cloudbasesolutions.com>
1 parent cb831fb commit 5c2b0cf

2 files changed

Lines changed: 300 additions & 4 deletions

File tree

coriolis/osmorphing/suse.py

Lines changed: 91 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,15 @@
1010

1111
from coriolis import exception
1212
from coriolis.osmorphing import base
13+
from coriolis.osmorphing.netpreserver import ifcfg
14+
from coriolis.osmorphing.netpreserver import nmconnection
1315
from coriolis.osmorphing.osdetect import suse as suse_detect
16+
from coriolis.osmorphing import redhat as redhat_osmorphing
1417
from coriolis import utils
1518

19+
IFCFG_TEMPLATE = redhat_osmorphing.IFCFG_TEMPLATE
20+
NMCONNECTION_TEMPLATE = redhat_osmorphing.NMCONNECTION_TEMPLATE
21+
1622
LOG = logging.getLogger(__name__)
1723

1824
DETECTED_SUSE_RELEASE_FIELD_NAME = suse_detect.DETECTED_SUSE_RELEASE_FIELD_NAME
@@ -29,6 +35,8 @@
2935

3036
class BaseSUSEMorphingTools(base.BaseLinuxOSMorphingTools):
3137

38+
_NETWORK_SCRIPTS_PATH = "etc/sysconfig/network-scripts"
39+
_NM_CONNECTIONS_PATH = "etc/NetworkManager/system-connections"
3240
BIOS_GRUB_LOCATION = "/boot/grub2"
3341
UEFI_GRUB_LOCATION = "/boot/efi/EFI/suse"
3442

@@ -61,12 +69,91 @@ def check_os_supported(cls, detected_os_info):
6169
return False
6270

6371
def disable_predictable_nic_names(self):
64-
# TODO(gsamfira): implement once we have networking support
65-
pass
72+
grub_cfg = "etc/default/grub"
73+
if not self._test_path(grub_cfg):
74+
LOG.warning(
75+
"Could not find /%s. Skipping predictable NIC names "
76+
"disabling.", grub_cfg)
77+
return
78+
contents = self._read_file_sudo(grub_cfg)
79+
cfg = utils.Grub2ConfigEditor(contents)
80+
cfg.append_to_option(
81+
"GRUB_CMDLINE_LINUX_DEFAULT",
82+
{"opt_type": "key_val", "opt_key": "net.ifnames", "opt_val": 0})
83+
cfg.append_to_option(
84+
"GRUB_CMDLINE_LINUX_DEFAULT",
85+
{"opt_type": "key_val", "opt_key": "biosdevname", "opt_val": 0})
86+
cfg.append_to_option(
87+
"GRUB_CMDLINE_LINUX",
88+
{"opt_type": "key_val", "opt_key": "net.ifnames", "opt_val": 0})
89+
cfg.append_to_option(
90+
"GRUB_CMDLINE_LINUX",
91+
{"opt_type": "key_val", "opt_key": "biosdevname", "opt_val": 0})
92+
self._write_file_sudo("etc/default/grub", cfg.dump())
93+
self._execute_update_grub()
94+
95+
def _get_nmconnection_net_preserver(self):
96+
return nmconnection.NmconnectionNetPreserver(self)
97+
98+
def _get_ifcfg_net_preserver(self):
99+
return ifcfg.IfcfgNetPreserver(self)
100+
101+
def _get_ifcfg_nm_controlled(self):
102+
if self._version_supported_util(self._version, minimum=15):
103+
return "yes"
104+
return "no"
105+
106+
def _write_nic_configs(self, nics_info):
107+
for idx, _ in enumerate(nics_info):
108+
dev_name = "eth%d" % idx
109+
cfg_path = "%s/ifcfg-%s" % (self._NETWORK_SCRIPTS_PATH, dev_name)
110+
if self._test_path(cfg_path):
111+
self._exec_cmd_chroot(
112+
"cp %s %s.bak" % (cfg_path, cfg_path)
113+
)
114+
self._write_file_sudo(
115+
cfg_path,
116+
IFCFG_TEMPLATE % {
117+
"device_name": dev_name,
118+
"nm_controlled": self._get_ifcfg_nm_controlled(),
119+
})
120+
121+
def _write_nmconnection_configs(self, nics_info):
122+
nm_net_preserver = self._get_nmconnection_net_preserver()
123+
nm_net_preserver.backup_nmconnection_files()
124+
device_names = ["eth%d" % idx for idx, _ in enumerate(nics_info)]
125+
self._get_ifcfg_net_preserver().backup_ifcfg_configs(device_names)
126+
127+
for idx, _ in enumerate(nics_info):
128+
dev_name = "eth%d" % idx
129+
cfg_path = "%s/%s.nmconnection" % (
130+
self._NM_CONNECTIONS_PATH, dev_name)
131+
self._write_file_sudo(
132+
cfg_path,
133+
NMCONNECTION_TEMPLATE % {
134+
"device_name": dev_name,
135+
"connection_uuid": str(uuid.uuid4()),
136+
})
137+
self._exec_cmd_chroot("chmod 600 /%s" % cfg_path)
138+
139+
def _write_dhcp_net_config(self, nics_info):
140+
self.disable_predictable_nic_names()
141+
ethernet_keyfiles = None
142+
if self._test_path(self._NM_CONNECTIONS_PATH):
143+
ethernet_keyfiles = (
144+
self._get_nmconnection_net_preserver().get_ethernet_keyfiles())
145+
if ethernet_keyfiles:
146+
self._write_nmconnection_configs(nics_info)
147+
else:
148+
self._write_nic_configs(nics_info)
66149

67150
def set_net_config(self, nics_info, dhcp):
68-
# TODO(alexpilotti): add networking support
69-
pass
151+
if dhcp:
152+
self._write_dhcp_net_config(nics_info)
153+
return
154+
155+
LOG.info("Setting static IP configuration")
156+
self._setup_network_preservation(nics_info)
70157

71158
def get_installed_packages(self):
72159
cmd = 'rpm -qa --qf "%{NAME}\\n"'

coriolis/tests/osmorphing/test_suse.py

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
from coriolis import exception
88
from coriolis.osmorphing import base
9+
from coriolis.osmorphing.netpreserver import ifcfg
10+
from coriolis.osmorphing.netpreserver import nmconnection
911
from coriolis.osmorphing import suse
1012
from coriolis.tests import test_base
1113

@@ -434,3 +436,210 @@ def test_pre_packages_install_no_packages(
434436
mock_super_pre.assert_called_once_with([])
435437
mock_enable_sles_module.assert_not_called()
436438
mock_add_cloud_tools_repo.assert_not_called()
439+
440+
def test__get_nmconnection_net_preserver(self):
441+
result = self.morphing_tools._get_nmconnection_net_preserver()
442+
443+
self.assertIsInstance(result, nmconnection.NmconnectionNetPreserver)
444+
445+
def test__get_ifcfg_net_preserver(self):
446+
result = self.morphing_tools._get_ifcfg_net_preserver()
447+
448+
self.assertIsInstance(result, ifcfg.IfcfgNetPreserver)
449+
450+
def test__get_ifcfg_nm_controlled_old_version(self):
451+
result = self.morphing_tools._get_ifcfg_nm_controlled()
452+
453+
self.assertEqual("no", result)
454+
455+
def test__get_ifcfg_nm_controlled_sles15(self):
456+
self.morphing_tools._version = "15"
457+
458+
result = self.morphing_tools._get_ifcfg_nm_controlled()
459+
460+
self.assertEqual("yes", result)
461+
462+
@mock.patch.object(base.BaseLinuxOSMorphingTools, '_write_file_sudo')
463+
@mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
464+
@mock.patch.object(base.BaseLinuxOSMorphingTools, '_test_path')
465+
def test__write_nic_configs_with_existing_file(
466+
self, mock_test_path, mock_exec_cmd_chroot, mock_write_file_sudo):
467+
nics_info = [{'name': 'eth0'}, {'name': 'eth1'}]
468+
mock_test_path.return_value = True
469+
470+
self.morphing_tools._write_nic_configs(nics_info)
471+
472+
mock_exec_cmd_chroot.assert_has_calls([
473+
mock.call("cp etc/sysconfig/network-scripts/ifcfg-eth0 "
474+
"etc/sysconfig/network-scripts/ifcfg-eth0.bak"),
475+
mock.call("cp etc/sysconfig/network-scripts/ifcfg-eth1 "
476+
"etc/sysconfig/network-scripts/ifcfg-eth1.bak"),
477+
])
478+
mock_write_file_sudo.assert_has_calls([
479+
mock.call(
480+
"etc/sysconfig/network-scripts/ifcfg-eth0",
481+
suse.IFCFG_TEMPLATE % {
482+
"device_name": "eth0",
483+
"nm_controlled": "no",
484+
},
485+
),
486+
mock.call(
487+
"etc/sysconfig/network-scripts/ifcfg-eth1",
488+
suse.IFCFG_TEMPLATE % {
489+
"device_name": "eth1",
490+
"nm_controlled": "no",
491+
},
492+
),
493+
])
494+
495+
@mock.patch.object(base.BaseLinuxOSMorphingTools, '_write_file_sudo')
496+
@mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
497+
@mock.patch.object(base.BaseLinuxOSMorphingTools, '_test_path')
498+
def test__write_nic_configs_sles15_no_existing_file(
499+
self, mock_test_path, mock_exec_cmd_chroot, mock_write_file_sudo):
500+
self.morphing_tools._version = "15"
501+
nics_info = [{'name': 'eth0'}]
502+
mock_test_path.return_value = False
503+
504+
self.morphing_tools._write_nic_configs(nics_info)
505+
506+
mock_exec_cmd_chroot.assert_not_called()
507+
mock_write_file_sudo.assert_called_once_with(
508+
"etc/sysconfig/network-scripts/ifcfg-eth0",
509+
suse.IFCFG_TEMPLATE % {
510+
"device_name": "eth0",
511+
"nm_controlled": "yes",
512+
},
513+
)
514+
515+
@mock.patch.object(
516+
ifcfg.IfcfgNetPreserver, 'backup_ifcfg_configs'
517+
)
518+
@mock.patch.object(
519+
nmconnection.NmconnectionNetPreserver, 'backup_nmconnection_files'
520+
)
521+
@mock.patch.object(
522+
suse.BaseSUSEMorphingTools, '_get_ifcfg_net_preserver'
523+
)
524+
@mock.patch.object(
525+
suse.BaseSUSEMorphingTools, '_get_nmconnection_net_preserver'
526+
)
527+
@mock.patch.object(base.BaseLinuxOSMorphingTools, '_write_file_sudo')
528+
@mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
529+
def test__write_nmconnection_configs(
530+
self, mock_exec_cmd_chroot, mock_write_file_sudo,
531+
mock_get_nmconnection_net_preserver,
532+
mock_get_ifcfg_net_preserver,
533+
mock_backup_nmconnection_files,
534+
mock_backup_ifcfg_configs):
535+
mock_nm_preserver = mock_get_nmconnection_net_preserver.return_value
536+
mock_ifcfg_preserver = mock_get_ifcfg_net_preserver.return_value
537+
nics_info = [{'name': 'eth0'}]
538+
539+
self.morphing_tools._write_nmconnection_configs(nics_info)
540+
541+
mock_nm_preserver.backup_nmconnection_files.assert_called_once_with()
542+
mock_ifcfg_preserver.backup_ifcfg_configs.assert_called_once_with(
543+
['eth0'])
544+
mock_write_file_sudo.assert_called_once()
545+
args, _ = mock_write_file_sudo.call_args
546+
self.assertEqual(
547+
args[0],
548+
"etc/NetworkManager/system-connections/eth0.nmconnection")
549+
self.assertIn("[connection]", args[1])
550+
self.assertIn("interface-name=eth0", args[1])
551+
self.assertIn("method=auto", args[1])
552+
self.assertIn("may-fail=false", args[1])
553+
mock_exec_cmd_chroot.assert_called_once_with(
554+
"chmod 600 /etc/NetworkManager/system-connections/"
555+
"eth0.nmconnection")
556+
557+
@mock.patch.object(suse.BaseSUSEMorphingTools, '_write_nic_configs')
558+
@mock.patch.object(
559+
suse.BaseSUSEMorphingTools, '_write_nmconnection_configs')
560+
@mock.patch.object(
561+
suse.BaseSUSEMorphingTools, '_get_nmconnection_net_preserver')
562+
@mock.patch.object(
563+
suse.BaseSUSEMorphingTools, 'disable_predictable_nic_names')
564+
@mock.patch.object(base.BaseLinuxOSMorphingTools, '_test_path')
565+
def test__write_dhcp_net_config_no_nm_path(
566+
self, mock_test_path, mock_disable_predictable_nic_names,
567+
mock_get_nm_preserver, mock_write_nmconnection_configs,
568+
mock_write_nic_configs):
569+
mock_test_path.return_value = False
570+
nics_info = [{'name': 'eth0'}]
571+
572+
self.morphing_tools._write_dhcp_net_config(nics_info)
573+
574+
mock_disable_predictable_nic_names.assert_called_once()
575+
mock_test_path.assert_called_once_with(
576+
"etc/NetworkManager/system-connections")
577+
mock_get_nm_preserver.assert_not_called()
578+
mock_write_nic_configs.assert_called_once_with(nics_info)
579+
mock_write_nmconnection_configs.assert_not_called()
580+
581+
@mock.patch.object(suse.BaseSUSEMorphingTools, '_write_nic_configs')
582+
@mock.patch.object(
583+
suse.BaseSUSEMorphingTools, '_write_nmconnection_configs')
584+
@mock.patch.object(
585+
suse.BaseSUSEMorphingTools, '_get_nmconnection_net_preserver')
586+
@mock.patch.object(
587+
suse.BaseSUSEMorphingTools, 'disable_predictable_nic_names')
588+
@mock.patch.object(base.BaseLinuxOSMorphingTools, '_test_path')
589+
def test__write_dhcp_net_config_no_ethernet_keyfiles(
590+
self, mock_test_path, mock_disable_predictable_nic_names,
591+
mock_get_nm_preserver, mock_write_nmconnection_configs,
592+
mock_write_nic_configs):
593+
mock_test_path.return_value = True
594+
mock_nm_preserver = mock_get_nm_preserver.return_value
595+
mock_nm_preserver.get_ethernet_keyfiles.return_value = []
596+
nics_info = [{'name': 'eth0'}]
597+
598+
self.morphing_tools._write_dhcp_net_config(nics_info)
599+
600+
mock_disable_predictable_nic_names.assert_called_once()
601+
mock_nm_preserver.get_ethernet_keyfiles.assert_called_once_with()
602+
mock_write_nic_configs.assert_called_once_with(nics_info)
603+
mock_write_nmconnection_configs.assert_not_called()
604+
605+
@mock.patch.object(suse.BaseSUSEMorphingTools, '_write_nic_configs')
606+
@mock.patch.object(
607+
suse.BaseSUSEMorphingTools, '_write_nmconnection_configs')
608+
@mock.patch.object(
609+
suse.BaseSUSEMorphingTools, '_get_nmconnection_net_preserver')
610+
@mock.patch.object(
611+
suse.BaseSUSEMorphingTools, 'disable_predictable_nic_names')
612+
@mock.patch.object(base.BaseLinuxOSMorphingTools, '_test_path')
613+
def test__write_dhcp_net_config_with_ethernet_keyfiles(
614+
self, mock_test_path, mock_disable_predictable_nic_names,
615+
mock_get_nm_preserver, mock_write_nmconnection_configs,
616+
mock_write_nic_configs):
617+
mock_test_path.return_value = True
618+
mock_nm_preserver = mock_get_nm_preserver.return_value
619+
mock_nm_preserver.get_ethernet_keyfiles.return_value = [
620+
('etc/NetworkManager/system-connections/eth0.nmconnection', {})]
621+
nics_info = [{'name': 'eth0'}]
622+
623+
self.morphing_tools._write_dhcp_net_config(nics_info)
624+
625+
mock_disable_predictable_nic_names.assert_called_once()
626+
mock_nm_preserver.get_ethernet_keyfiles.assert_called_once_with()
627+
mock_write_nmconnection_configs.assert_called_once_with(nics_info)
628+
mock_write_nic_configs.assert_not_called()
629+
630+
@mock.patch.object(suse.BaseSUSEMorphingTools, '_write_dhcp_net_config')
631+
def test_set_net_config_dhcp(self, mock_write_dhcp_net_config):
632+
nics_info = [{'name': 'eth0'}]
633+
634+
self.morphing_tools.set_net_config(nics_info, dhcp=True)
635+
636+
mock_write_dhcp_net_config.assert_called_once_with(nics_info)
637+
638+
@mock.patch.object(
639+
base.BaseLinuxOSMorphingTools, '_setup_network_preservation')
640+
def test_set_net_config_static(self, mock_setup_network_preservation):
641+
nics_info = [{'name': 'eth0'}]
642+
643+
self.morphing_tools.set_net_config(nics_info, dhcp=False)
644+
645+
mock_setup_network_preservation.assert_called_once_with(nics_info)

0 commit comments

Comments
 (0)