Fix use of by-label device persistency in grub

If devicepersistency="by-label" is set in the KIWI description
it will correctly operate on the fstab values but still uses
the UUID based setting for root= in the grub config. This commit
allows to set root=LABEL=... in the grub config in case the
devicepersistency requested it. In order for this to work this
commit also had to increase the scope of the grub helper
method _fix_grub_root_device_reference which is now called in
any case. This Fixes #1757
This commit is contained in:
Marcus Schäfer 2021-03-09 12:14:41 +01:00
parent 5ef9f83e0c
commit 791adcfcb7
No known key found for this signature in database
GPG Key ID: AD11DD02B44996EF
6 changed files with 82 additions and 58 deletions

View File

@ -25,6 +25,7 @@ from kiwi.mount_manager import MountManager
from kiwi.storage.setup import DiskSetup
from kiwi.path import Path
from kiwi.defaults import Defaults
from kiwi.utils.block import BlockID
from kiwi.exceptions import (
KiwiBootLoaderTargetError
@ -549,6 +550,7 @@ class BootLoaderConfigBase:
def _get_root_cmdline_parameter(self, uuid):
cmdline = self.xml_state.build_type.get_kernelcmdline()
persistency_type = self.xml_state.build_type.get_devicepersistency()
if cmdline and 'root=' in cmdline:
log.info(
'Kernel root device explicitly set via kernelcmdline'
@ -560,10 +562,13 @@ class BootLoaderConfigBase:
if uuid and self.xml_state.build_type.get_overlayroot():
return 'root=overlay:UUID={0}'.format(uuid)
elif uuid:
return 'root=UUID={0} rw'.format(uuid)
block_operation = BlockID(f'UUID={uuid}')
blkid_type = 'LABEL' if persistency_type == 'by-label' else 'UUID'
location = block_operation.get_blkid(blkid_type)
return f'root={blkid_type}={location} rw'
else:
log.warning(
'root=UUID=<uuid> setup requested, but uuid is not provided'
'root setup based on UUID requested, but uuid is not provided'
)
def __del__(self):

View File

@ -101,6 +101,8 @@ class BootLoaderConfigGrub2(BootLoaderConfigBase):
self.failsafe_boot = self.failsafe_boot_entry_requested()
self.mediacheck_boot = self.xml_state.build_type.get_mediacheck()
self.xen_guest = self.xml_state.is_xen_guest()
self.persistency_type = \
self.xml_state.build_type.get_devicepersistency()
self.firmware = FirmWare(
self.xml_state
)
@ -610,6 +612,7 @@ class BootLoaderConfigGrub2(BootLoaderConfigBase):
* GRUB_GFXMODE
* GRUB_TERMINAL
* GRUB_DISTRIBUTOR
* GRUB_DISABLE_LINUX_UUID
"""
grub_default_entries = {
'GRUB_TIMEOUT': self.timeout,
@ -619,6 +622,8 @@ class BootLoaderConfigGrub2(BootLoaderConfigBase):
grub_final_cmdline = re.sub(
r'root=.* |root=.*$', '', self.cmdline
).strip()
if self.persistency_type != 'by-uuid':
grub_default_entries['GRUB_DISABLE_LINUX_UUID'] = 'true'
if self.displayname:
grub_default_entries['GRUB_DISTRIBUTOR'] = '"{0}"'.format(
self.displayname
@ -1229,48 +1234,45 @@ class BootLoaderConfigGrub2(BootLoaderConfigBase):
def _fix_grub_root_device_reference(self, config_file, boot_options):
if self.root_reference:
if self.root_filesystem_is_overlay or \
self.arch.startswith('s390') or \
Defaults.is_buildservice_worker():
# grub2-mkconfig has no idea how the correct root= setup is
# for disk images created with overlayroot enabled or in a
# buildservice worker environment. Because of that the mkconfig
# tool just finds the raw partition loop device and includes it
# which is wrong. In this particular case we have to patch the
# written config file and replace the wrong root= reference with
# the correct value.
with open(config_file) as grub_config_file:
grub_config = grub_config_file.read()
# The following expression matches any of the following
# grub mkconfig root= settings and replaces it with a
# correct value
# 1. root=LOCAL-KIWI-MAPPED-DEVICE
# 2. root=[a-zA-Z]=ANY-LINUX-BY-ID-VALUE
grub_config = re.sub(
r'(root=[a-zA-Z]+=[a-zA-Z0-9:\.-]+)|(root={0})'.format(
boot_options.get('root_device')
),
'{0}'.format(self.root_reference),
grub_config
)
# grub2-mkconfig has no idea how the correct root= setup is
# for disk images created with overlayroot enabled or in a
# buildservice worker environment. Because of that the mkconfig
# tool just finds the raw partition loop device and includes it
# which is wrong. In this particular case we have to patch the
# written config file and replace the wrong root= reference with
# the correct value.
with open(config_file) as grub_config_file:
grub_config = grub_config_file.read()
# The following expression matches any of the following
# grub mkconfig root= settings and replaces it with a
# correct value
# 1. root=LOCAL-KIWI-MAPPED-DEVICE
# 2. root=[a-zA-Z]=ANY-LINUX-BY-ID-VALUE
grub_config = re.sub(
r'(root=[a-zA-Z]+=[a-zA-Z0-9:\.-]+)|(root={0})'.format(
boot_options.get('root_device')
),
'{0}'.format(self.root_reference),
grub_config
)
with open(config_file, 'w') as grub_config_file:
grub_config_file.write(grub_config)
with open(config_file, 'w') as grub_config_file:
grub_config_file.write(grub_config)
if self.firmware.efi_mode():
vendor_grubenv_file = \
Defaults.get_vendor_grubenv(self.efi_mount.mountpoint)
if vendor_grubenv_file:
with open(vendor_grubenv_file) as vendor_grubenv:
grubenv = vendor_grubenv.read()
grubenv = grubenv.replace(
'root={0}'.format(
boot_options.get('root_device')
),
self.root_reference
)
with open(vendor_grubenv_file, 'w') as vendor_grubenv:
vendor_grubenv.write(grubenv)
if self.firmware.efi_mode():
vendor_grubenv_file = \
Defaults.get_vendor_grubenv(self.efi_mount.mountpoint)
if vendor_grubenv_file:
with open(vendor_grubenv_file) as vendor_grubenv:
grubenv = vendor_grubenv.read()
grubenv = grubenv.replace(
'root={0}'.format(
boot_options.get('root_device')
),
self.root_reference
)
with open(vendor_grubenv_file, 'w') as vendor_grubenv:
vendor_grubenv.write(grubenv)
def _fix_grub_loader_entries_boot_cmdline(self):
if self.cmdline:

View File

@ -16,6 +16,7 @@
# along with kiwi. If not, see <http://www.gnu.org/licenses/>
#
import os
import re
# project
from kiwi.command import Command
@ -25,11 +26,20 @@ class BlockID:
"""
**Get information from a block device**
:param str device: block device node name name
:param str device:
block device node name name. The device can
also be specified as UUID=<uuid>
"""
def __init__(self, device):
self.device = device
uuid_format = re.match(r'^UUID=(.*)', device)
if uuid_format:
blkid_result = Command.run(
['blkid', '--uuid', uuid_format.group(1)]
)
self.device = blkid_result.output.strip(os.linesep)
else:
self.device = device
def get_label(self):
"""

View File

@ -109,22 +109,26 @@ class TestBootLoaderConfigBase:
mock_cmdline.return_value = 'root=/dev/myroot'
assert self.bootloader.get_boot_cmdline() == 'root=/dev/myroot'
@patch('kiwi.xml_parse.type_.get_firmware')
def test_get_boot_cmdline_firmware_ec2(self, mock_firmware):
mock_firmware.return_value = 'ec2'
assert self.bootloader.get_boot_cmdline('uuid') == \
'splash root=UUID=uuid rw'
@patch('kiwi.xml_parse.type_.get_initrd_system')
def test_get_boot_cmdline_initrd_system_is_dracut(self, mock_initrd):
@patch('kiwi.bootloader.config.base.BlockID')
def test_get_boot_cmdline_initrd_system_is_dracut(
self, mock_BlockID, mock_initrd
):
block_operation = Mock()
block_operation.get_blkid.return_value = 'uuid'
mock_BlockID.return_value = block_operation
mock_initrd.return_value = 'dracut'
assert self.bootloader.get_boot_cmdline('uuid') == \
'splash root=UUID=uuid rw'
@patch('kiwi.xml_parse.type_.get_initrd_system')
@patch('kiwi.bootloader.config.base.BlockID')
def test_get_boot_cmdline_initrd_system_is_dracut_with_overlay(
self, mock_initrd
self, mock_BlockID, mock_initrd
):
block_operation = Mock()
block_operation.get_blkid.return_value = 'uuid'
mock_BlockID.return_value = block_operation
mock_initrd.return_value = 'dracut'
self.state.build_type.get_overlayroot = Mock(
return_value=True
@ -132,12 +136,6 @@ class TestBootLoaderConfigBase:
assert self.bootloader.get_boot_cmdline('uuid') == \
'splash root=overlay:UUID=uuid'
@patch('kiwi.xml_parse.type_.get_firmware')
def test_get_boot_cmdline_firmware_ec2_no_uuid(self, mock_firmware):
mock_firmware.return_value = 'ec2'
with self._caplog.at_level(logging.WARNING):
self.bootloader.get_boot_cmdline()
@patch('kiwi.xml_parse.type_.get_installboot')
def test_get_install_image_boot_default(self, mock_installboot):
mock_installboot.return_value = None

View File

@ -539,6 +539,7 @@ class TestBootLoaderConfigGrub2:
'/boot/grub2/themes/openSUSE/background.png'
),
call('GRUB_CMDLINE_LINUX_DEFAULT', '"some-cmdline"'),
call('GRUB_DISABLE_LINUX_UUID', 'true'),
call('GRUB_DISTRIBUTOR', '"Bob"'),
call('GRUB_ENABLE_BLSCFG', 'true'),
call('GRUB_ENABLE_CRYPTODISK', 'y'),
@ -580,6 +581,7 @@ class TestBootLoaderConfigGrub2:
'GRUB_BACKGROUND',
'/boot/grub2/themes/openSUSE/background.png'
),
call('GRUB_DISABLE_LINUX_UUID', 'true'),
call('GRUB_DISTRIBUTOR', '"Bob"'),
call('GRUB_ENABLE_BLSCFG', 'true'),
call('GRUB_ENABLE_CRYPTODISK', 'y'),

View File

@ -7,6 +7,13 @@ class TestBlockID:
def setup(self):
self.blkid = BlockID('device')
@patch('kiwi.utils.block.Command.run')
def test_setup_with_uuid_format(self, mock_command):
BlockID('UUID=uuid')
mock_command.assert_called_once_with(
['blkid', '--uuid', 'uuid']
)
@patch('kiwi.utils.block.Command.run')
def test_get_blkid(self, mock_command):
self.blkid.get_blkid('LABEL')