curtin-dev team mailing list archive
-
curtin-dev team
-
Mailing list archive
-
Message #02650
[Merge] ~alexsander-souza/curtin:sles_support into curtin:master
Alexsander de Souza has proposed merging ~alexsander-souza/curtin:sles_support into curtin:master.
Commit message:
add SLES support
Requested reviews:
curtin developers (curtin-dev)
For more details, see:
https://code.launchpad.net/~alexsander-souza/curtin/+git/curtin/+merge/435914
--
Your team curtin developers is requested to review the proposed merge of ~alexsander-souza/curtin:sles_support into curtin:master.
diff --git a/curtin/block/deps.py b/curtin/block/deps.py
index 5cb23e0..ec5fe83 100644
--- a/curtin/block/deps.py
+++ b/curtin/block/deps.py
@@ -94,6 +94,25 @@ def detect_required_packages_mapping(osfamily=DISTROS.debian):
'zfs': [],
'zpool': [],
},
+ DISTROS.suse: {
+ 'bcache': ['bcache-tools'],
+ 'btrfs': ['btrfsprogs'],
+ 'dm_crypt': ['cryptsetup'],
+ 'ext2': ['e2fsprogs'],
+ 'ext3': ['e2fsprogs'],
+ 'ext4': ['e2fsprogs'],
+ 'jfs': ['jfsutils'],
+ 'iscsi': [],
+ 'lvm_partition': ['lvm2'],
+ 'lvm_volgroup': ['lvm2'],
+ 'ntfs': [],
+ 'raid': ['mdadm'],
+ 'reiserfs': [],
+ 'xfs': ['xfsprogs'],
+ 'zfsroot': [],
+ 'zfs': [],
+ 'zpool': [],
+ },
}
if osfamily not in distro_mapping:
raise ValueError('No block package mapping for distro: %s' % osfamily)
diff --git a/curtin/commands/curthooks.py b/curtin/commands/curthooks.py
index 03a53b1..69833a2 100644
--- a/curtin/commands/curthooks.py
+++ b/curtin/commands/curthooks.py
@@ -1038,6 +1038,7 @@ def detect_and_handle_multipath(cfg, target, osfamily=DISTROS.debian):
DEFAULT_MULTIPATH_PACKAGES = {
DISTROS.debian: ['multipath-tools-boot'],
DISTROS.redhat: ['device-mapper-multipath'],
+ DISTROS.suse: ['multipath-tools'],
}
if osfamily not in DEFAULT_MULTIPATH_PACKAGES:
raise ValueError(
@@ -1154,7 +1155,7 @@ def detect_and_handle_multipath(cfg, target, osfamily=DISTROS.debian):
grub_cfg = os.path.sep.join(
[target, '/etc/default/grub.d/50-curtin-multipath.cfg'])
omode = 'w'
- elif osfamily == DISTROS.redhat:
+ elif osfamily in [DISTROS.redhat, DISTROS.suse]:
grub_cfg = os.path.sep.join([target, '/etc/default/grub'])
omode = 'a'
else:
@@ -1199,7 +1200,7 @@ def detect_and_handle_multipath(cfg, target, osfamily=DISTROS.debian):
# Initrams needs to be updated to include /etc/multipath.cfg
# and /etc/multipath/bindings files.
update_initramfs(target, all_kernels=True)
- elif osfamily == DISTROS.redhat:
+ elif osfamily in [DISTROS.redhat, DISTROS.suse]:
# Write out initramfs/dracut config for multipath
dracut_conf_multipath = os.path.sep.join(
[target, '/etc/dracut.conf.d/10-curtin-multipath.conf'])
@@ -1308,6 +1309,12 @@ def install_missing_packages(cfg, target, osfamily=DISTROS.debian):
# SecureBoot support
if distro.has_pkg_available("shim-signed"):
uefi_pkgs.append("shim-signed")
+ elif osfamily == DISTROS.suse:
+ uefi_pkgs.extend(['grub2', 'grub2-branding-SLE'])
+ arch = distro.get_architecture()
+ if arch == 'amd64':
+ arch = 'x86_64'
+ uefi_pkgs.append('grub2-%s-efi' % arch)
else:
raise ValueError('Unknown grub2 package list for distro: %s' %
osfamily)
@@ -1753,6 +1760,14 @@ def builtin_curthooks(cfg, target, state):
description="setting up swap"):
add_swap(cfg, target, state.get('fstab'))
+ if osfamily == DISTROS.suse:
+ # set cloud-init maas datasource for SuSE images
+ if cfg.get('cloudconfig'):
+ handle_cloudconfig(
+ cfg['cloudconfig'],
+ base_dir=paths.target_path(target,
+ 'etc/cloud/cloud.cfg.d'))
+
if osfamily == DISTROS.redhat:
# set cloud-init maas datasource for centos images
if cfg.get('cloudconfig'):
diff --git a/curtin/commands/install_grub.py b/curtin/commands/install_grub.py
index 79b6695..38bf71a 100644
--- a/curtin/commands/install_grub.py
+++ b/curtin/commands/install_grub.py
@@ -250,7 +250,7 @@ def get_grub_install_command(uefi, distroinfo, target):
# prefer grub-multi-install if present
if uefi and os.path.exists(target_path(target, GRUB_MULTI_INSTALL)):
grub_install_cmd = GRUB_MULTI_INSTALL
- elif distroinfo.family == distro.DISTROS.redhat:
+ elif distroinfo.family in [distro.DISTROS.redhat, distro.DISTROS.suse]:
grub_install_cmd = 'grub2-install'
LOG.debug('Using grub install command: %s', grub_install_cmd)
@@ -289,6 +289,23 @@ def gen_uefi_install_commands(grub_name, grub_target, grub_cmd, update_nvram,
'/boot/efi/EFI/%s/grub.cfg' % bootid])
else:
post_cmds.append(['grub2-mkconfig', '-o', '/boot/grub2/grub.cfg'])
+ elif distroinfo.family == distro.DISTROS.suse:
+ bootid = 'suse'
+ grub_cfg = '/boot/grub2/grub.cfg'
+ loader = find_efi_loader(target, bootid)
+ if loader:
+ grub_cmd = None
+ grub_cfg = '/boot/efi/EFI/%s/grub.cfg' % bootid
+ if update_nvram:
+ efi_disk, efi_part_num = get_efi_disk_part(devices)
+ # Add entry to the EFI boot menu
+ install_cmds.append(['efibootmgr', '--create',
+ '--write-signature', '--label', bootid,
+ '--disk', efi_disk,
+ '--part', efi_part_num,
+ '--loader',
+ efi_loader_esp_path(loader)])
+ post_cmds.append(['grub2-mkconfig', '-o', grub_cfg])
else:
raise ValueError("Unsupported os family for grub "
"install: %s" % distroinfo.family)
@@ -315,6 +332,8 @@ def gen_install_commands(grub_name, grub_cmd, distroinfo, devices,
if distroinfo.family == distro.DISTROS.debian:
install_cmds.append(['dpkg-reconfigure', grub_name])
install_cmds.append(['update-grub'])
+ elif distroinfo.family == distro.DISTROS.suse:
+ post_cmds.append(['grub2-mkconfig', '-o', '/boot/grub2/grub.cfg'])
elif distroinfo.family == distro.DISTROS.redhat:
if rhel_ver in ["7", "8", "9"]:
post_cmds.append(
diff --git a/curtin/distro.py b/curtin/distro.py
index 618af47..4266278 100644
--- a/curtin/distro.py
+++ b/curtin/distro.py
@@ -379,6 +379,26 @@ def rpm_get_dist_id(target=None):
return dist.rstrip()
+def run_zypper_command(mode, args=None, opts=None, env=None, target=None,
+ execute=True, allow_daemons=False):
+ defopts = ['--non-interactive', '--non-interactive-include-reboot-patches',
+ '--quiet']
+
+ if args is None:
+ args = []
+
+ if opts is None:
+ opts = []
+
+ cmd = ['zypper']
+ cmd += defopts + opts + [mode] + args
+ if not execute:
+ return env, cmd
+
+ with ChrootableTarget(target, allow_daemons=allow_daemons) as inchroot:
+ return inchroot.subp(cmd, env=env)
+
+
def system_upgrade(opts=None, target=None, env=None, allow_daemons=False,
osfamily=None):
LOG.debug("Upgrading system in %s", target)
@@ -391,6 +411,8 @@ def system_upgrade(opts=None, target=None, env=None, allow_daemons=False,
'subcommands': ('dist-upgrade', 'autoremove')},
DISTROS.redhat: {'function': run_yum_command,
'subcommands': ('upgrade',)},
+ DISTROS.suse: {'function': run_zypper_command,
+ 'subcommands': ('refresh', 'update', 'purge-kernels',)},
}
if osfamily not in distro_cfg:
raise ValueError('Distro "%s" does not have system_upgrade support',
@@ -414,6 +436,7 @@ def install_packages(pkglist, osfamily=None, opts=None, target=None, env=None,
installer_map = {
DISTROS.debian: run_apt_command,
DISTROS.redhat: run_yum_command,
+ DISTROS.suse: run_zypper_command,
}
install_cmd = installer_map.get(osfamily)
@@ -429,10 +452,15 @@ def has_pkg_available(pkg, target=None, osfamily=None):
if not osfamily:
osfamily = get_osfamily(target=target)
- if osfamily not in [DISTROS.debian, DISTROS.redhat]:
+ if osfamily not in [DISTROS.debian, DISTROS.redhat, DISTROS.suse]:
raise ValueError('has_pkg_available: unsupported distro family: %s',
osfamily)
+ if osfamily == DISTROS.suse:
+ out, _ = subp(['zypper', '--quiet', 'search',
+ '--match-exact', pkg], capture=True, target=target)
+ return 'No matching items found.' not in out
+
if osfamily == DISTROS.debian:
out, _ = subp(['apt-cache', 'pkgnames'], capture=True, target=target)
for item in out.splitlines():
@@ -603,7 +631,7 @@ def get_architecture(target=None, osfamily=None):
if osfamily == DISTROS.debian:
return dpkg_get_architecture(target=target)
- if osfamily == DISTROS.redhat:
+ if osfamily in [DISTROS.redhat, DISTROS.suse]:
return rpm_get_architecture(target=target)
raise ValueError("Unhandled osfamily=%s" % osfamily)
diff --git a/curtin/net/deps.py b/curtin/net/deps.py
index b78654d..e019a9e 100644
--- a/curtin/net/deps.py
+++ b/curtin/net/deps.py
@@ -75,6 +75,14 @@ def detect_required_packages_mapping(osfamily=DISTROS.debian):
'openvswitch': ['openvswitch-switch'],
'vlan': [],
'vlans': []},
+ DISTROS.suse: {
+ 'bond': [],
+ 'bonds': [],
+ 'bridge': ['bridge-utils'],
+ 'bridges': ['bridge-utils'],
+ 'openvswitch': ['openvswitch-switch'],
+ 'vlan': ['vlan'],
+ 'vlans': ['vlan']},
}
if osfamily not in distro_mapping:
raise ValueError('No net package mapping for distro: %s' % osfamily)
diff --git a/tests/unittests/test_commands_install_grub.py b/tests/unittests/test_commands_install_grub.py
index 24f9476..ab52505 100644
--- a/tests/unittests/test_commands_install_grub.py
+++ b/tests/unittests/test_commands_install_grub.py
@@ -830,6 +830,121 @@ class TestGenUefiInstallCommands(CiTestCase):
grub_name, grub_target, grub_cmd, update_nvram, distroinfo,
devices, self.target))
+ def test_suse_install(self):
+ self.m_os_release.return_value = {'ID': 'suse'}
+ distroinfo = install_grub.distro.get_distroinfo()
+ grub_name = 'grub2-efi-x64'
+ grub_target = 'x86_64-efi'
+ grub_cmd = 'grub2-install'
+ update_nvram = True
+ devices = ['/dev/disk-a-part1']
+ disk = '/dev/disk-a'
+ part = '1'
+ self.m_get_disk_part.return_value = (disk, part)
+
+ expected_install = [
+ ['efibootmgr', '-v'],
+ [grub_cmd, '--target=%s' % grub_target,
+ '--efi-directory=/boot/efi',
+ '--bootloader-id=suse', '--recheck'],
+ ]
+ expected_post = [
+ ['grub2-mkconfig', '-o', '/boot/grub2/grub.cfg'],
+ ['efibootmgr', '-v']
+ ]
+ self.assertEqual(
+ (expected_install, expected_post),
+ install_grub.gen_uefi_install_commands(
+ grub_name, grub_target, grub_cmd, update_nvram, distroinfo,
+ devices, self.target))
+
+ def test_suse_install_existing(self):
+ # simulate existing bootloaders already installed in target system
+ # by touching the files grub would have installed, including shim
+ def _enable_loaders(bootid):
+ efi_path = 'boot/efi/EFI'
+ target_efi_path = os.path.join(self.target, efi_path)
+ loaders = [
+ os.path.join(target_efi_path, bootid, 'shimx64.efi'),
+ os.path.join(target_efi_path, 'BOOT', 'BOOTX64.EFI'),
+ os.path.join(target_efi_path, bootid, 'grubx64.efi'),
+ ]
+ for loader in loaders:
+ util.ensure_dir(os.path.dirname(loader))
+ with open(loader, 'w+') as fh:
+ fh.write('\n')
+
+ self.m_os_release.return_value = {'ID': 'suse'}
+ distroinfo = install_grub.distro.get_distroinfo()
+ _enable_loaders("suse")
+ grub_name = 'grub2-efi-x64'
+ grub_target = 'x86_64-efi'
+ grub_cmd = 'grub2-install'
+ update_nvram = True
+ devices = ['/dev/disk-a-part1']
+ disk = '/dev/disk-a'
+ part = '1'
+ self.m_get_disk_part.return_value = (disk, part)
+
+ expected_loader = '/EFI/suse/shimx64.efi'
+ expected_install = [
+ ['efibootmgr', '-v'],
+ ['efibootmgr', '--create', '--write-signature',
+ '--label', 'suse', '--disk', disk, '--part', part,
+ '--loader', expected_loader],
+ ]
+ expected_post = [
+ ['grub2-mkconfig', '-o', '/boot/efi/EFI/suse/grub.cfg'],
+ ['efibootmgr', '-v']
+ ]
+
+ self.assertEqual(
+ (expected_install, expected_post),
+ install_grub.gen_uefi_install_commands(
+ grub_name, grub_target, grub_cmd, update_nvram, distroinfo,
+ devices, self.target))
+
+ def test_suse_install_existing_no_nvram(self):
+ # verify grub install command is not executed if update_nvram is False
+ # on suse.
+ def _enable_loaders(bootid):
+ efi_path = 'boot/efi/EFI'
+ target_efi_path = os.path.join(self.target, efi_path)
+ loaders = [
+ os.path.join(target_efi_path, bootid, 'shimx64.efi'),
+ os.path.join(target_efi_path, 'BOOT', 'BOOTX64.EFI'),
+ os.path.join(target_efi_path, bootid, 'grubx64.efi'),
+ ]
+ for loader in loaders:
+ util.write_file(loader, content="")
+
+ self.m_os_release.return_value = {'ID': 'suse'}
+ distroinfo = install_grub.distro.get_distroinfo()
+ bootid = distroinfo.variant
+ _enable_loaders(bootid)
+ grub_name = 'grub2-efi-x64'
+ grub_target = 'x86_64-efi'
+ grub_cmd = 'grub2-install'
+ update_nvram = False
+ devices = ['/dev/disk-a-part1']
+ disk = '/dev/disk-a'
+ part = '1'
+ self.m_get_disk_part.return_value = (disk, part)
+
+ expected_install = [
+ ['efibootmgr', '-v'],
+ ]
+ expected_post = [
+ ['grub2-mkconfig', '-o', '/boot/efi/EFI/%s/grub.cfg' % bootid],
+ ['efibootmgr', '-v']
+ ]
+
+ self.assertEqual(
+ (expected_install, expected_post),
+ install_grub.gen_uefi_install_commands(
+ grub_name, grub_target, grub_cmd, update_nvram, distroinfo,
+ devices, self.target))
+
class TestGenInstallCommands(CiTestCase):
diff --git a/tests/unittests/test_curthooks.py b/tests/unittests/test_curthooks.py
index a224819..c13434c 100644
--- a/tests/unittests/test_curthooks.py
+++ b/tests/unittests/test_curthooks.py
@@ -479,6 +479,22 @@ class TestInstallMissingPkgs(CiTestCase):
expected_pkgs, target=target, osfamily=self.distro_family)
@patch.object(events, 'ReportEventStack')
+ def test_install_packages_on_uefi_amd64_sles(self, mock_events):
+ arch = 'amd64'
+ self.mock_arch.return_value = arch
+ self.mock_machine.return_value = 'x86_64'
+ expected_pkgs = ['efibootmgr', 'grub2', 'grub2-branding-SLE',
+ 'grub2-x86_64-efi']
+ self.mock_uefi.return_value = True
+ self.mock_haspkg.return_value = True
+ target = "not-a-real-target"
+ cfg = {}
+ curthooks.install_missing_packages(
+ cfg, target=target, osfamily=distro.DISTROS.suse)
+ self.mock_install_packages.assert_called_with(
+ expected_pkgs, target=target, osfamily=distro.DISTROS.suse)
+
+ @patch.object(events, 'ReportEventStack')
def test_install_packages_on_uefi_amd64_centos(self, mock_events):
arch = 'amd64'
self.mock_arch.return_value = arch
diff --git a/tests/unittests/test_distro.py b/tests/unittests/test_distro.py
index 7532126..06c25db 100644
--- a/tests/unittests/test_distro.py
+++ b/tests/unittests/test_distro.py
@@ -370,6 +370,34 @@ class TestYumInstall(CiTestCase):
m_subp.assert_has_calls(expected_calls)
+class TestZypperInstall(CiTestCase):
+
+ @mock.patch.object(util.ChrootableTarget, "__enter__", new=lambda a: a)
+ @mock.patch('curtin.util.subp')
+ def test_zypper_install(self, m_subp):
+ pkglist = ['foobar', 'wark']
+ target = 'mytarget'
+ expected_calls = [
+ mock.call(['zypper', '--non-interactive',
+ '--non-interactive-include-reboot-patches',
+ '--quiet', 'install'] + pkglist, env=None,
+ target=paths.target_path(target))
+ ]
+
+ m_subp.reset_mock()
+ self.assertFalse(m_subp.called)
+ distro.run_zypper_command('install', pkglist, target=target)
+ m_subp.assert_has_calls(expected_calls)
+
+ # call yum_install through install_packages; expect the same calls
+ # so clear m_subp's call stack.
+ m_subp.reset_mock()
+ self.assertFalse(m_subp.called)
+ osfamily = distro.DISTROS.suse
+ distro.install_packages(pkglist, osfamily=osfamily, target=target)
+ m_subp.assert_has_calls(expected_calls)
+
+
class TestSystemUpgrade(CiTestCase):
@mock.patch.object(util.ChrootableTarget, "__enter__", new=lambda a: a)
@@ -469,6 +497,35 @@ class TestSystemUpgrade(CiTestCase):
m_apt_update.assert_has_calls(apt_update_calls)
m_subp.assert_has_calls(expected_calls)
+ @mock.patch.object(util.ChrootableTarget, "__enter__", new=lambda a: a)
+ @mock.patch('curtin.util.subp')
+ def test_system_upgrade_suse(self, m_subp):
+ """system_upgrade osfamily=suse
+ calls run_zypper_command mode=upgrade"""
+ osfamily = distro.DISTROS.suse
+ target = 'mytarget'
+ expected_calls = [
+ mock.call(['zypper', '--non-interactive',
+ '--non-interactive-include-reboot-patches', '--quiet',
+ 'refresh'], env=None,
+ target='/work/curtin/mytarget'),
+ mock.call(['zypper', '--non-interactive',
+ '--non-interactive-include-reboot-patches', '--quiet',
+ 'update'], env=None,
+ target='/work/curtin/mytarget'),
+ mock.call(['zypper', '--non-interactive',
+ '--non-interactive-include-reboot-patches', '--quiet',
+ 'purge-kernels'], env=None,
+ target='/work/curtin/mytarget'),
+ ]
+
+ # call system_upgrade via osfamily; note that we expect the same calls
+ # but to prevent a false positive we clear m_subp's call stack.
+ m_subp.reset_mock()
+ self.assertFalse(m_subp.called)
+ distro.system_upgrade(target=target, osfamily=osfamily)
+ m_subp.assert_has_calls(expected_calls)
+
class TestHasPkgAvailable(CiTestCase):
@@ -519,6 +576,18 @@ class TestHasPkgAvailable(CiTestCase):
self.assertEqual(pkg == self.package, result)
m_subp.assert_has_calls([mock.call('list', opts=['--cacheonly'])])
+ @mock.patch.object(util.ChrootableTarget, "__enter__", new=lambda a: a)
+ @mock.patch('curtin.distro.subp')
+ def test_has_pkg_available_suse_returns_false_not_avail(self, m_subp):
+ osfamily = distro.DISTROS.suse
+ m_subp.return_value = ('No matching items found.', '')
+ result = distro.has_pkg_available(self.package, self.target, osfamily)
+ self.assertEqual(False, result)
+ m_subp.assert_has_calls([mock.call(['zypper', '--quiet', 'search',
+ '--match-exact', self.package],
+ capture=True,
+ target=self.target)])
+
class TestGetArchitecture(CiTestCase):
@@ -563,4 +632,13 @@ class TestGetArchitecture(CiTestCase):
self.m_rpm_get_arch.call_args_list)
self.assertEqual(0, self.m_dpkg_get_arch.call_count)
+ def test_suse_osfamily_calls_rpm_get_arch(self):
+ osfamily = distro.DISTROS.suse
+ expected_result = self.m_rpm_get_arch.return_value
+ result = distro.get_architecture(target=self.target, osfamily=osfamily)
+ self.assertEqual(expected_result, result)
+ self.assertEqual([mock.call(target=self.target)],
+ self.m_rpm_get_arch.call_args_list)
+ self.assertEqual(0, self.m_dpkg_get_arch.call_count)
+
# vi: ts=4 expandtab syntax=python
Follow ups
-
[Merge] ~alexsander-souza/curtin:sles_support into curtin:master
From: Server Team CI bot, 2023-02-08
-
Re: [Merge] ~alexsander-souza/curtin:sles_support into curtin:master
From: Server Team CI bot, 2023-02-08
-
[Merge] ~alexsander-souza/curtin:sles_support into curtin:master
From: Dan Bungert, 2023-02-08
-
Re: [Merge] ~alexsander-souza/curtin:sles_support into curtin:master
From: Alexsander de Souza, 2023-02-08
-
Re: [Merge] ~alexsander-souza/curtin:sles_support into curtin:master
From: Server Team CI bot, 2023-02-08
-
Re: [Merge] ~alexsander-souza/curtin:sles_support into curtin:master
From: Dan Bungert, 2023-02-08
-
[Merge] ~alexsander-souza/curtin:sles_support into curtin:master
From: Dan Bungert, 2023-02-08
-
Re: [Merge] ~alexsander-souza/curtin:sles_support into curtin:master
From: Server Team CI bot, 2023-02-08
-
Re: [Merge] ~alexsander-souza/curtin:sles_support into curtin:master
From: Server Team CI bot, 2023-02-08
-
Re: [Merge] ~alexsander-souza/curtin:sles_support into curtin:master
From: Server Team CI bot, 2023-02-08
-
Re: [Merge] ~alexsander-souza/curtin:sles_support into curtin:master
From: Dan Bungert, 2023-02-08
-
Re: [Merge] ~alexsander-souza/curtin:sles_support into curtin:master
From: Dan Bungert, 2023-02-08
-
Re: [Merge] ~alexsander-souza/curtin:sles_support into curtin:master
From: Server Team CI bot, 2023-02-08
-
[Merge] ~alexsander-souza/curtin:sles_support into curtin:master
From: Dan Bungert, 2023-02-08
-
Re: [Merge] ~alexsander-souza/curtin:sles_support into curtin:master
From: Dan Bungert, 2023-02-08
-
Re: [Merge] ~alexsander-souza/curtin:sles_support into curtin:master
From: Michael Hudson-Doyle, 2023-02-08