curtin-dev team mailing list archive
-
curtin-dev team
-
Mailing list archive
-
Message #04062
[Merge] ~sjg1/curtin:ext into curtin:master
Simon Glass has proposed merging ~sjg1/curtin:ext into curtin:master.
Commit message:
This is the -master version of changes to fully support extlinux in Curtin.
This is only unit-tested so far, but future work will add some vmtests.
Requested reviews:
Dan Bungert (dbungert)
For more details, see:
https://code.launchpad.net/~sjg1/curtin/+git/curtin/+merge/484176
--
Your team curtin developers is subscribed to branch curtin:master.
diff --git a/HACKING.rst b/HACKING.rst
index f2b618d..f723906 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -46,6 +46,12 @@ Do these things once
git remote add LP_USER ssh://LP_USER@xxxxxxxxxxxxxxxxx/~LP_USER/curtin
git push LP_USER master
+* Install `libapt-pkg-dev` as it is needed by tox:
+
+ .. code:: sh
+
+ sudo apt install libapt-pkg-dev
+
.. _repository: https://git.launchpad.net/curtin
.. _contributor license agreement: https://ubuntu.com/legal/contributors
.. _contributor-agreement-canonical: https://launchpad.net/%7Econtributor-agreement-canonical/+members
diff --git a/curtin/commands/block_meta.py b/curtin/commands/block_meta.py
index 5749647..7c053e4 100644
--- a/curtin/commands/block_meta.py
+++ b/curtin/commands/block_meta.py
@@ -1366,6 +1366,19 @@ def proc_filesystems_passno(fstype):
return "1"
+def resolve_fdata_spec(fdata: FstabData) -> str:
+ """Get the spec to use for an fdata item (like a line in /etc/fstab)
+
+ :param: Data to look at
+ Return: The correct spec to use, e.g. /dev/sda1'
+ """
+ if fdata.spec is None:
+ if not fdata.device:
+ raise ValueError("FstabData missing both spec and device.")
+ return get_volume_spec(fdata.device)
+ return fdata.spec
+
+
def fstab_line_for_data(fdata):
"""Return a string representing fdata in /etc/fstab format.
@@ -1378,12 +1391,7 @@ def fstab_line_for_data(fdata):
else:
raise ValueError("empty path in %s." % str(fdata))
- if fdata.spec is None:
- if not fdata.device:
- raise ValueError("FstabData missing both spec and device.")
- spec = get_volume_spec(fdata.device)
- else:
- spec = fdata.spec
+ spec = resolve_fdata_spec(fdata)
if fdata.options in (None, "", "defaults"):
if fdata.fstype == "swap":
diff --git a/curtin/commands/curthooks.py b/curtin/commands/curthooks.py
index 0c4d7fd..34c4a5d 100644
--- a/curtin/commands/curthooks.py
+++ b/curtin/commands/curthooks.py
@@ -30,7 +30,7 @@ from curtin.block import deps as bdeps
from curtin.distro import DISTROS
from curtin.net import deps as ndeps
from curtin.reporter import events
-from curtin.commands import apply_net, apt_config
+from curtin.commands import apply_net, apt_config, block_meta
from curtin.commands.install_grub import install_grub
from curtin.commands.install_extlinux import install_extlinux
from curtin.url_helper import get_maas_version
@@ -414,8 +414,8 @@ def install_kernel(cfg, target):
try:
map_suffix = mapping[codename][version]
except KeyError:
- LOG.warn("Couldn't detect kernel package to install for %s."
- % kernel)
+ LOG.warning("Couldn't detect kernel package to install for %s."
+ % kernel)
if kernel_cfg.fallback_package is not None:
install(kernel_cfg.fallback_package)
return
@@ -873,6 +873,38 @@ def translate_old_grub_schema(cfg):
cfg['boot'] = grub_cfg
+def setup_extlinux(
+ cfg: dict,
+ target: str):
+ """Set up an extlinux.conf file
+
+ :param: cfg: A config dict containing config.BootCfg in cfg['boot'].
+ # :param: target: A string specifying the path to the chroot mountpoint.
+ """
+ bootcfg = config.fromdict(config.BootCfg, cfg.get('boot', {}))
+
+ # If there is a separate boot partition, set fw_boot_dir to empty
+ storage_cfg = cfg.get('storage', {}).get('config', {})
+ fw_boot_dir = '/boot'
+ root = None
+ for item in storage_cfg:
+ if item['type'] == 'mount':
+ if item['path'] == '/boot':
+ fw_boot_dir = ''
+ elif item['path'] == '/':
+ root = item
+
+ if not root:
+ raise ValueError("Storage configuration has no root directory")
+
+ storage_config_dict = block_meta.extract_storage_ordered_dict(cfg)
+
+ fdata = block_meta.mount_data(root, storage_config_dict)
+ spec = block_meta.resolve_fdata_spec(fdata)
+
+ install_extlinux(bootcfg, target, fw_boot_dir, spec)
+
+
def setup_boot(
cfg: dict,
target: str,
@@ -905,7 +937,7 @@ def setup_boot(
if machine not in ['i586', 'i686', 'x86_64']:
raise ValueError('Invalid arch %s: Only x86 platforms support '
'extlinux at present' % machine)
- install_extlinux(cfg, target)
+ setup_extlinux(cfg, target)
if machine == 's390x':
with events.ReportEventStack(
@@ -1931,7 +1963,14 @@ def uses_grub(machine):
return True
-def builtin_curthooks(cfg, target, state):
+def builtin_curthooks(cfg: dict, target: str, state: dict):
+ """Run the built-in curthooks
+
+ :param: cfg: Config dict
+ :param: target: Target directory for the new installation
+ :param: state: State information obtained from environment variables. See
+ load_command_environment()
+ """
LOG.info('Running curtin builtin curthooks')
stack_prefix = state.get('report_stack_prefix', '')
state_etcd = os.path.split(state['fstab'])[0]
diff --git a/curtin/commands/install_extlinux.py b/curtin/commands/install_extlinux.py
index b52b591..dc1bd64 100644
--- a/curtin/commands/install_extlinux.py
+++ b/curtin/commands/install_extlinux.py
@@ -7,12 +7,14 @@ import os
from curtin import config
from curtin import paths
+from curtin import util
from curtin.log import LOG
EXTLINUX_DIR = '/boot/extlinux'
-def build_content(bootcfg: config.BootCfg, target: str):
+def build_content(bootcfg: config.BootCfg, target: str, fw_boot_dir: str,
+ root_spec: str):
"""Build the content of the extlinux.conf file
For now this only supports x86, since it does not handle the 'fdt' option.
@@ -21,18 +23,25 @@ def build_content(bootcfg: config.BootCfg, target: str):
associated with fdt and fdtdir options.
We assume that the boot directory is available as /boot in the target.
+
+ :param: bootcfg: A boot-config dict
+ :param: target: A string specifying the path to the chroot mountpoint.
+ :param: fw_boot_dir: Firmware's view of the /boot directory; when there is
+ a separate /boot partition, firmware will access that as the root
+ directory of the filesystem, so '' should be passed here. When the boot
+ directory is just a subdirectory of '/', then '/boot' should be passed
+ :param: root_spec: Root device to pass to kernel
"""
def get_entry(label, params, menu_label_append=''):
return f'''\
label {label}
\tmenu label {menu_label} {version}{menu_label_append}
-\tlinux /{kernel_path}
-\tinitrd /{initrd_path}
+\tlinux {fw_boot_dir}/{kernel_path}
+\tinitrd {fw_boot_dir}/{initrd_path}
\tappend {params}'''
buf = io.StringIO()
- params = 'ro quiet'
- alternatives = ['default', 'recovery']
+ params = f'{root_spec} ro quiet'
menu_label = 'Linux'
# For the recovery option, remove 'quiet' and add 'single'
@@ -56,10 +65,11 @@ timeout 50''', file=buf)
LOG.debug('P: Writing config for %s...', kernel_path)
initrd_path = os.path.basename(full_initrd_path)
print(file=buf)
- print(file=buf)
- print(get_entry(f'l{seq}', params), file=buf)
+ if 'default' in bootcfg.alternatives:
+ print(file=buf)
+ print(get_entry(f'l{seq}', params), file=buf)
- if 'recovery' in alternatives:
+ if 'rescue' in bootcfg.alternatives:
print(file=buf)
print(get_entry(f'l{seq}r', rec_params, ' (rescue target)'),
file=buf)
@@ -70,14 +80,20 @@ timeout 50''', file=buf)
def install_extlinux(
bootcfg: config.BootCfg,
target: str,
+ fw_boot_dir: str,
+ root_spec: str,
):
"""Install extlinux to the target chroot.
- :param: bootcfg: An config dict with grub config options.
+ :param: bootcfg: A boot-config dict.
:param: target: A string specifying the path to the chroot mountpoint.
+ :param: fw_boot_dir: Firmware's view of the /boot directory
+ :param: root_spec: Root device to pass to kernel
"""
- content = build_content(bootcfg, target)
+ LOG.debug("P: Writing extlinux, fw_boot_dir '%s' root_spec '%s'...",
+ fw_boot_dir, root_spec)
+ content = build_content(bootcfg, target, fw_boot_dir, root_spec)
extlinux_path = paths.target_path(target, '/boot/extlinux')
- os.makedirs(extlinux_path, exist_ok=True)
+ util.ensure_dir(extlinux_path)
with open(extlinux_path + '/extlinux.conf', 'w', encoding='utf-8') as outf:
outf.write(content)
diff --git a/curtin/config.py b/curtin/config.py
index da9e486..c5182a5 100644
--- a/curtin/config.py
+++ b/curtin/config.py
@@ -148,6 +148,18 @@ def _check_bootloaders(inst, attr, vals):
raise ValueError(f'Unknown bootloader {val}: {vals}')
+def _check_alternatives(inst, attr, vals):
+ if not isinstance(vals, list):
+ raise ValueError(f'alternatives must be a list: {vals}')
+ if not vals:
+ raise ValueError(f'Empty alternatives list: {vals}')
+ if len(vals) != len(set(vals)):
+ raise ValueError(f'alternatives list contains duplicates: {vals}')
+ for val in vals:
+ if val not in ['default', 'rescue']:
+ raise ValueError(f'Unknown alternative {val}: {vals}')
+
+
@attr.s(auto_attribs=True)
class BootCfg:
bootloaders: typing.List[str] = attr.ib(validator=_check_bootloaders)
@@ -165,6 +177,8 @@ class BootCfg:
default=True, converter=value_as_boolean)
terminal: str = "console"
update_nvram: bool = attr.ib(default=True, converter=value_as_boolean)
+ alternatives: typing.Optional[typing.List[str]] = attr.ib(
+ validator=_check_alternatives, default=['default', 'rescue'])
@attr.s(auto_attribs=True)
diff --git a/doc/topics/config.rst b/doc/topics/config.rst
index fde668b..7040909 100644
--- a/doc/topics/config.rst
+++ b/doc/topics/config.rst
@@ -296,7 +296,13 @@ This setting is ignored if *update_nvram* is False.
extlinux
""""""""
-There are no options specific to extlinux.
+**alternatives**: *<List of alternatives to add for each OS>*
+
+Specifies a list of alternative boot options to add for each OS. Valid values
+are:
+
+ *default*: Run with default command-line flags (ro quiet)
+ *rescue*: Run with rescue command-line flags (ro single)
**Example**::
diff --git a/tests/unittests/test_commands_install_extlinux.py b/tests/unittests/test_commands_install_extlinux.py
index fa7dece..e914a95 100644
--- a/tests/unittests/test_commands_install_extlinux.py
+++ b/tests/unittests/test_commands_install_extlinux.py
@@ -8,10 +8,11 @@ from .helpers import CiTestCase
from curtin import config
from curtin import paths
-from curtin.commands import install_extlinux
+from curtin.commands import curthooks, install_extlinux
USE_EXTLINUX = ['extlinux']
+ROOT_DEV = '/dev/sda1'
EXPECT_HDR = '''\
## /boot/extlinux/extlinux.conf
@@ -27,47 +28,90 @@ prompt 0
timeout 50
'''
-EXPECT_BODY = '''
-
+EXPECT_L0 = '''
label l0
\tmenu label Linux 6.8.0-48-generic
-\tlinux /vmlinuz-6.8.0-48-generic
-\tinitrd /initrd.img-6.8.0-48-generic
-\tappend ro quiet
+\tlinux {fw_boot_dir}/vmlinuz-6.8.0-48-generic
+\tinitrd {fw_boot_dir}/initrd.img-6.8.0-48-generic
+\tappend {ROOT_DEV} ro quiet
+'''
+EXPECT_L0R = '''
label l0r
\tmenu label Linux 6.8.0-48-generic (rescue target)
-\tlinux /vmlinuz-6.8.0-48-generic
-\tinitrd /initrd.img-6.8.0-48-generic
-\tappend ro single
-
+\tlinux {fw_boot_dir}/vmlinuz-6.8.0-48-generic
+\tinitrd {fw_boot_dir}/initrd.img-6.8.0-48-generic
+\tappend {ROOT_DEV} ro single
+'''
+EXPECT_L1 = '''
label l1
\tmenu label Linux 6.8.0-40-generic
-\tlinux /vmlinuz-6.8.0-40-generic
-\tinitrd /initrd.img-6.8.0-40-generic
-\tappend ro quiet
+\tlinux {fw_boot_dir}/vmlinuz-6.8.0-40-generic
+\tinitrd {fw_boot_dir}/initrd.img-6.8.0-40-generic
+\tappend {ROOT_DEV} ro quiet
+'''
+EXPECT_L1R = '''
label l1r
\tmenu label Linux 6.8.0-40-generic (rescue target)
-\tlinux /vmlinuz-6.8.0-40-generic
-\tinitrd /initrd.img-6.8.0-40-generic
-\tappend ro single
-
+\tlinux {fw_boot_dir}/vmlinuz-6.8.0-40-generic
+\tinitrd {fw_boot_dir}/initrd.img-6.8.0-40-generic
+\tappend {ROOT_DEV} ro single
+'''
+EXPECT_L2 = '''
label l2
\tmenu label Linux 5.15.0-127-generic
-\tlinux /vmlinuz-5.15.0-127-generic
-\tinitrd /initrd.img-5.15.0-127-generic
-\tappend ro quiet
+\tlinux {fw_boot_dir}/vmlinuz-5.15.0-127-generic
+\tinitrd {fw_boot_dir}/initrd.img-5.15.0-127-generic
+\tappend {ROOT_DEV} ro quiet
+'''
+EXPECT_L2R = '''
label l2r
\tmenu label Linux 5.15.0-127-generic (rescue target)
-\tlinux /vmlinuz-5.15.0-127-generic
-\tinitrd /initrd.img-5.15.0-127-generic
-\tappend ro single
+\tlinux {fw_boot_dir}/vmlinuz-5.15.0-127-generic
+\tinitrd {fw_boot_dir}/initrd.img-5.15.0-127-generic
+\tappend {ROOT_DEV} ro single
'''
+EXPECT_BODY = ('\n' + EXPECT_L0 + EXPECT_L0R +
+ '\n' + EXPECT_L1 + EXPECT_L1R +
+ '\n' + EXPECT_L2 + EXPECT_L2R)
+
+STORAGE = {
+ 'version': 1,
+ 'config': [
+ {
+ 'id': 'vdb',
+ 'type': 'disk',
+ 'name': 'vdb',
+ 'path': '/dev/vdb',
+ 'ptable': 'gpt',
+ },
+ {
+ 'id': 'vdb-part1',
+ 'type': 'partition',
+ 'device': 'vdb',
+ 'number': 1,
+ },
+ {
+ 'id': 'vdb-part1_format',
+ 'type': 'format',
+ 'volume': 'vdb-part1',
+ 'fstype': 'ext4',
+ },
+ {
+ 'id': 'vdb-part1_mount',
+ 'type': 'mount',
+ 'path': '/',
+ 'device': 'vdb-part1_format',
+ 'spec': ROOT_DEV,
+ },
+ ]
+}
+
class TestInstallExtlinux(CiTestCase):
def setUp(self):
@@ -87,6 +131,7 @@ class TestInstallExtlinux(CiTestCase):
self.maxDiff = None
def test_get_kernel_list(self):
+ """Check that the list of kernels is correct"""
iter = paths.get_kernel_list(self.target, full_initrd_path=False)
self.assertEqual(
('vmlinuz-6.8.0-48-generic', 'initrd.img-6.8.0-48-generic',
@@ -107,27 +152,162 @@ class TestInstallExtlinux(CiTestCase):
pass
def test_empty(self):
+ """An empty configuration with no kernels should just have a header"""
out = install_extlinux.build_content(config.BootCfg(USE_EXTLINUX),
- f'{self.target}/empty-dir')
+ f'{self.target}/empty-dir',
+ '', ROOT_DEV)
self.assertEqual(out, EXPECT_HDR)
def test_normal(self):
+ """Normal configuration, with both 'default' and 'rescue' options"""
out = install_extlinux.build_content(config.BootCfg(USE_EXTLINUX),
- self.target)
- self.assertEqual(EXPECT_HDR + EXPECT_BODY, out)
+ self.target, '/boot', ROOT_DEV)
+ self.assertEqual(
+ EXPECT_HDR + EXPECT_BODY.format(fw_boot_dir='/boot',
+ ROOT_DEV=ROOT_DEV),
+ out)
- def test_no_recovery(self):
- out = install_extlinux.build_content(config.BootCfg(USE_EXTLINUX),
- self.target)
- self.assertEqual(EXPECT_HDR + EXPECT_BODY, out)
+ def test_no_rescue(self):
+ """Configuration with only the 'default' options"""
+ cfg = config.BootCfg(USE_EXTLINUX, alternatives=['default'])
+ out = install_extlinux.build_content(cfg, self.target, '/boot',
+ ROOT_DEV)
+ self.assertEqual(
+ EXPECT_HDR +
+ ('\n' + EXPECT_L0 + '\n' + EXPECT_L1 + '\n' + EXPECT_L2).format(
+ fw_boot_dir='/boot', ROOT_DEV=ROOT_DEV),
+ out)
- def test_install(self):
- install_extlinux.install_extlinux(config.BootCfg(USE_EXTLINUX),
- self.target)
+ def test_no_default(self):
+ """Configuration with only the 'rescue' options"""
+ cfg = config.BootCfg(USE_EXTLINUX, alternatives=['rescue'])
+ out = install_extlinux.build_content(cfg, self.target, '/boot',
+ ROOT_DEV)
+ self.assertEqual(
+ EXPECT_HDR +
+ ('\n' + EXPECT_L0R + '\n' + EXPECT_L1R + '\n' + EXPECT_L2R).format(
+ fw_boot_dir='/boot', ROOT_DEV=ROOT_DEV),
+ out)
+
+ def test_separate_boot_partition(self):
+ """Check handling of a separate /boot partition"""
+ cfg = config.BootCfg(USE_EXTLINUX)
+ out = install_extlinux.build_content(cfg, self.target, '', ROOT_DEV)
+ self.assertEqual(EXPECT_HDR + EXPECT_BODY.format(
+ fw_boot_dir='', ROOT_DEV=ROOT_DEV), out)
+
+ def check_extlinux(self) -> str:
+ """Common checks for extlinux
+
+ Return: Contents of extlinux.conf
+ """
extlinux_path = self.target + '/boot/extlinux'
self.assertTrue(os.path.exists(extlinux_path))
extlinux_file = extlinux_path + '/extlinux.conf'
self.assertTrue(os.path.exists(extlinux_file))
+ with open(extlinux_file, encoding='utf-8') as inf:
+ return inf.read()
+
+ def test_install(self):
+ """Make sure the file is written to the disk"""
+ install_extlinux.install_extlinux(config.BootCfg(USE_EXTLINUX),
+ self.target, '/boot', ROOT_DEV)
+ out = self.check_extlinux()
+ self.assertEqual(EXPECT_HDR + EXPECT_BODY.format(
+ fw_boot_dir='/boot', ROOT_DEV=ROOT_DEV), out)
+
+ def test_install_separate_boot_partition(self):
+ """Check installation with a separate /boot partition"""
+ cfg = config.BootCfg(USE_EXTLINUX)
+ install_extlinux.install_extlinux(cfg, self.target, '', ROOT_DEV)
+ out = self.check_extlinux()
+ self.assertEqual(EXPECT_HDR + EXPECT_BODY.format
+ (fw_boot_dir='', ROOT_DEV=ROOT_DEV), out)
+
+ def test_install_no_alternatives(self):
+ """The default in BootCfg is not used when reading a yaml file"""
+ cfg = {
+ 'boot': {'bootloaders': USE_EXTLINUX},
+ 'storage': STORAGE,
+ }
+ curthooks.setup_extlinux(cfg, self.target)
+ out = self.check_extlinux()
+ self.assertEqual(EXPECT_HDR + EXPECT_BODY.format(
+ fw_boot_dir='/boot', ROOT_DEV=ROOT_DEV), out)
+
+ def test_separate_boot_partition_cfg(self):
+ """setup_extlinux() should see a separate mount and set fw_boot_dir"""
+ cfg = {
+ 'boot': {'bootloaders': USE_EXTLINUX},
+ 'storage': {
+ 'version': 1,
+ 'config': [
+ {
+ 'id': 'vdb',
+ 'type': 'disk',
+ 'name': 'vdb',
+ 'path': '/dev/vdb',
+ 'ptable': 'gpt',
+ },
+ {
+ 'id': 'vdb-part1',
+ 'type': 'partition',
+ 'device': 'vdb',
+ 'number': 1,
+ },
+ {
+ 'id': 'vdb-part2',
+ 'type': 'partition',
+ 'device': 'vdb',
+ 'flag': 'boot',
+ 'number': 2,
+ },
+ {
+ 'id': 'vdb-part1_format',
+ 'type': 'format',
+ 'volume': 'vdb-part1',
+ 'fstype': 'ext4',
+ },
+ {
+ 'id': 'vdb-part2_format',
+ 'type': 'format',
+ 'volume': 'vdb-part2',
+ 'fstype': 'ext4',
+ },
+ {
+ 'id': 'vdb-part1_mount',
+ 'type': 'mount',
+ 'path': '/',
+ 'device': 'vdb-part1_format',
+ 'spec': ROOT_DEV,
+ },
+ {
+ 'id': 'vdb-part2_mount',
+ 'type': 'mount',
+ 'path': '/boot',
+ 'device': 'vdb-part2_format',
+ }
+ ]
+ }
+ }
+ curthooks.setup_extlinux(cfg, self.target)
+ out = self.check_extlinux()
+ self.assertEqual(EXPECT_HDR + EXPECT_BODY.format(
+ fw_boot_dir='', ROOT_DEV=ROOT_DEV), out)
+
+ def test_single_partition_cfg(self):
+ """setup_extlinux() should see a single mount and set fw_boot_dir"""
+ cfg = {
+ 'boot': {
+ 'bootloaders': USE_EXTLINUX,
+ 'alternatives': ['default', 'rescue'],
+ },
+ 'storage': STORAGE,
+ }
+ curthooks.setup_extlinux(cfg, self.target)
+ out = self.check_extlinux()
+ self.assertEqual(EXPECT_HDR + EXPECT_BODY.format(
+ fw_boot_dir='/boot', ROOT_DEV=ROOT_DEV), out)
# vi: ts=4 expandtab syntax=python
diff --git a/tests/unittests/test_config.py b/tests/unittests/test_config.py
index f812628..44caf51 100644
--- a/tests/unittests/test_config.py
+++ b/tests/unittests/test_config.py
@@ -11,6 +11,10 @@ from curtin import config
from .helpers import CiTestCase
+# Selects the extlinux bootloader
+EXTLINUX = ['extlinux']
+
+
class TestMerge(CiTestCase):
def test_merge_cfg_string(self):
d1 = {'str1': 'str_one'}
@@ -226,7 +230,7 @@ class TestDeserializer(CiTestCase):
deserializer.deserialize(UnionClass, {"val": None}))
-class TestBootCfg(CiTestCase):
+class TestBootCfgBootloaders(CiTestCase):
def test_empty(self):
with self.assertRaises(TypeError) as exc:
config.BootCfg()
@@ -262,4 +266,39 @@ class TestBootCfg(CiTestCase):
config.BootCfg(['extlinux', 'grub'])
+class TestBootCfgAlternatives(CiTestCase):
+ def test_defaults(self):
+ cfg = config.BootCfg(['extlinux'])
+ self.assertEqual(['default', 'rescue'], cfg.alternatives)
+
+ def test_not_list(self):
+ with self.assertRaises(ValueError) as exc:
+ config.BootCfg(['extlinux'], alternatives='invalid')
+ self.assertIn("alternatives must be a list: invalid",
+ str(exc.exception))
+
+ def test_empty_list(self):
+ with self.assertRaises(ValueError) as exc:
+ config.BootCfg(['extlinux'], alternatives=[])
+ self.assertIn("Empty alternatives list:", str(exc.exception))
+
+ def test_duplicate(self):
+ with self.assertRaises(ValueError) as exc:
+ config.BootCfg(['extlinux'], alternatives=['default', 'default'])
+ self.assertIn(
+ "alternatives list contains duplicates: ['default', 'default']",
+ str(exc.exception))
+
+ def test_invalid(self):
+ with self.assertRaises(ValueError) as exc:
+ config.BootCfg(['extlinux'], alternatives=['fred'])
+ self.assertIn("Unknown alternative fred: ['fred']", str(exc.exception))
+
+ def test_valid(self):
+ config.BootCfg(EXTLINUX, alternatives=['default'])
+ config.BootCfg(EXTLINUX, alternatives=['rescue'])
+ config.BootCfg(EXTLINUX, alternatives=['default', 'rescue'])
+ config.BootCfg(EXTLINUX, alternatives=['rescue', 'default'])
+
+
# vi: ts=4 expandtab syntax=python
diff --git a/tests/unittests/test_curthooks.py b/tests/unittests/test_curthooks.py
index f1bccdb..cd94c51 100644
--- a/tests/unittests/test_curthooks.py
+++ b/tests/unittests/test_curthooks.py
@@ -1479,12 +1479,45 @@ class TestSetupExtlinux(CiTestCase):
'bootloaders': ['extlinux'],
'install_devices': ['/dev/vdb'],
},
+ 'storage': {
+ 'version': 1,
+ 'config': [
+ {
+ 'id': 'vdb',
+ 'type': 'disk',
+ 'name': 'vdb',
+ 'path': '/dev/vdb',
+ 'ptable': 'gpt',
+ },
+ {
+ 'id': 'vdb-part1',
+ 'type': 'partition',
+ 'device': 'vdb',
+ 'number': 1,
+ },
+ {
+ 'id': 'vdb-part1_format',
+ 'type': 'format',
+ 'volume': 'vdb-part1',
+ 'fstype': 'ext4',
+ },
+ {
+ 'id': 'vdb-part1_mount',
+ 'type': 'mount',
+ 'path': '/',
+ 'device': 'vdb-part1_format',
+ 'spec': '/dev/vdb1',
+ },
+ ]
+ }
}
for machine in ['i586', 'i686', 'x86_64']:
curthooks.setup_boot(
cfg, self.target, machine, '/testing',
osfamily=self.distro_family, variant=self.variant)
- self.m_install_extlinux.assert_called_with(cfg, self.target)
+ bootcfg = config.fromdict(config.BootCfg, cfg['boot'])
+ self.m_install_extlinux.assert_called_with(
+ bootcfg, self.target, '/boot', '/dev/vdb1')
self.m_setup_grub.assert_not_called()
self.m_run_zipl.assert_not_called()
Follow ups
-
[Merge] ~sjg1/curtin:ext into curtin:master
From: mp+484176, 2025-05-22
-
Re: [Merge] ~sjg1/curtin:ext into curtin:master
From: Server Team CI bot, 2025-05-21
-
Re: [Merge] ~sjg1/curtin:ext into curtin:master
From: Server Team CI bot, 2025-05-21
-
Re: [Merge] ~sjg1/curtin:ext into curtin:master
From: Server Team CI bot, 2025-05-21
-
Re: [Merge] ~sjg1/curtin:ext into curtin:master
From: Server Team CI bot, 2025-05-13
-
Re: [Merge] ~sjg1/curtin:ext into curtin:master
From: Server Team CI bot, 2025-05-13
-
Re: [Merge] ~sjg1/curtin:ext into curtin:master
From: Server Team CI bot, 2025-05-12
-
Re: [Merge] ~sjg1/curtin:ext into curtin:master
From: Server Team CI bot, 2025-05-12
-
Re: [Merge] ~sjg1/curtin:ext into curtin:master
From: Server Team CI bot, 2025-05-08
-
Re: [Merge] ~sjg1/curtin:ext into curtin:master
From: Simon Glass, 2025-05-07
-
Re: [Merge] ~sjg1/curtin:ext into curtin:master
From: Dan Bungert, 2025-05-05
-
Re: [Merge] ~sjg1/curtin:ext into curtin:master
From: Simon Glass, 2025-05-02
-
Re: [Merge] ~sjg1/curtin:ext into curtin:master
From: Server Team CI bot, 2025-04-30
-
Re: [Merge] ~sjg1/curtin:ext into curtin:master
From: Dan Bungert, 2025-04-25
-
Re: [Merge] ~sjg1/curtin:ext into curtin:master
From: Server Team CI bot, 2025-04-09