Merge "Prevent broken partition image UEFI deploys" into stable/victoria
This commit is contained in:
commit
b6fde30a9b
|
@ -329,6 +329,7 @@ def _manage_uefi(device, efi_system_part_uuid=None):
|
||||||
LOG.error(error_msg)
|
LOG.error(error_msg)
|
||||||
raise errors.CommandExecutionError(error_msg)
|
raise errors.CommandExecutionError(error_msg)
|
||||||
finally:
|
finally:
|
||||||
|
LOG.debug('Executing _manage_uefi clean-up.')
|
||||||
umount_warn_msg = "Unable to umount %(local_path)s. Error: %(error)s"
|
umount_warn_msg = "Unable to umount %(local_path)s. Error: %(error)s"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -497,7 +498,9 @@ def _install_grub2(device, root_uuid, efi_system_part_uuid=None,
|
||||||
efi_part = None
|
efi_part = None
|
||||||
efi_partition_mount_point = None
|
efi_partition_mount_point = None
|
||||||
efi_mounted = False
|
efi_mounted = False
|
||||||
|
efi_preserved = False
|
||||||
holders = None
|
holders = None
|
||||||
|
path_variable = _get_path_variable()
|
||||||
|
|
||||||
# NOTE(TheJulia): Seems we need to get this before ever possibly
|
# NOTE(TheJulia): Seems we need to get this before ever possibly
|
||||||
# restart the device in the case of multi-device RAID as pyudev
|
# restart the device in the case of multi-device RAID as pyudev
|
||||||
|
@ -524,13 +527,6 @@ def _install_grub2(device, root_uuid, efi_system_part_uuid=None,
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Add /bin to PATH variable as grub requires it to find efibootmgr
|
|
||||||
# when running in uefi boot mode.
|
|
||||||
# Add /usr/sbin to PATH variable to ensure it is there as we do
|
|
||||||
# not use full path to grub binary anymore.
|
|
||||||
path_variable = os.environ.get('PATH', '')
|
|
||||||
path_variable = '%s:/bin:/usr/sbin:/sbin' % path_variable
|
|
||||||
|
|
||||||
# Mount the partition and binds
|
# Mount the partition and binds
|
||||||
path = tempfile.mkdtemp()
|
path = tempfile.mkdtemp()
|
||||||
if efi_system_part_uuid:
|
if efi_system_part_uuid:
|
||||||
|
@ -558,10 +554,33 @@ def _install_grub2(device, root_uuid, efi_system_part_uuid=None,
|
||||||
disks = [device]
|
disks = [device]
|
||||||
|
|
||||||
utils.execute('mount', root_partition, path)
|
utils.execute('mount', root_partition, path)
|
||||||
for fs in BIND_MOUNTS:
|
|
||||||
utils.execute('mount', '-o', 'bind', fs, path + fs)
|
|
||||||
|
|
||||||
utils.execute('mount', '-t', 'sysfs', 'none', path + '/sys')
|
_mount_for_chroot(path)
|
||||||
|
|
||||||
|
# UEFI asset management for RAID is handled elsewhere
|
||||||
|
if not hardware.is_md_device(device) and efi_partition_mount_point:
|
||||||
|
# NOTE(TheJulia): It may make sense to retool all efi
|
||||||
|
# asset preservation logic at some point since the paths
|
||||||
|
# can be a little different, but largely this is JUST for
|
||||||
|
# partition images as there _should not_ be a mount
|
||||||
|
# point if we have no efi partitions at all.
|
||||||
|
efi_preserved = _try_preserve_efi_assets(
|
||||||
|
device, path, efi_system_part_uuid,
|
||||||
|
efi_partitions, efi_partition_mount_point)
|
||||||
|
if efi_preserved:
|
||||||
|
# Success preserving efi assets
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
# Failure, either via exception or not found
|
||||||
|
# which in this case the partition needs to be
|
||||||
|
# remounted.
|
||||||
|
LOG.debug('No EFI assets were preserved for setup or the '
|
||||||
|
'ramdisk was unable to complete the setup. '
|
||||||
|
'falling back to bootloader installation from'
|
||||||
|
'deployed image.')
|
||||||
|
if not os.path.ismount(root_partition):
|
||||||
|
LOG.debug('Re-mounting the root partition.')
|
||||||
|
utils.execute('mount', root_partition, path)
|
||||||
|
|
||||||
binary_name = "grub"
|
binary_name = "grub"
|
||||||
if os.path.exists(os.path.join(path, 'usr/sbin/grub2-install')):
|
if os.path.exists(os.path.join(path, 'usr/sbin/grub2-install')):
|
||||||
|
@ -578,8 +597,9 @@ def _install_grub2(device, root_uuid, efi_system_part_uuid=None,
|
||||||
if efi_partitions:
|
if efi_partitions:
|
||||||
if not os.path.exists(efi_partition_mount_point):
|
if not os.path.exists(efi_partition_mount_point):
|
||||||
os.makedirs(efi_partition_mount_point)
|
os.makedirs(efi_partition_mount_point)
|
||||||
LOG.info("GRUB2 will be installed for UEFI on efi partitions %s",
|
LOG.warning("GRUB2 will be installed for UEFI on efi partitions "
|
||||||
efi_partitions)
|
"%s using the install command which does not place "
|
||||||
|
"Secure Boot signed binaries.", efi_partitions)
|
||||||
for efi_partition in efi_partitions:
|
for efi_partition in efi_partitions:
|
||||||
utils.execute(
|
utils.execute(
|
||||||
'mount', efi_partition, efi_partition_mount_point)
|
'mount', efi_partition, efi_partition_mount_point)
|
||||||
|
@ -645,28 +665,10 @@ def _install_grub2(device, root_uuid, efi_system_part_uuid=None,
|
||||||
LOG.debug("GRUB2 successfully installed on device %s",
|
LOG.debug("GRUB2 successfully installed on device %s",
|
||||||
grub_disk)
|
grub_disk)
|
||||||
|
|
||||||
# If the image has dracut installed, set the rd.md.uuid kernel
|
# NOTE(TheJulia): Setup grub configuration again since IF we reach
|
||||||
# parameter for discovered md devices.
|
# this point, then we've manually installed grub which is not the
|
||||||
if hardware.is_md_device(device) and _has_dracut(path):
|
# recommended path.
|
||||||
rd_md_uuids = ["rd.md.uuid=%s" % x['UUID']
|
_configure_grub(device, path)
|
||||||
for x in hardware.md_get_raid_devices().values()]
|
|
||||||
|
|
||||||
LOG.debug("Setting rd.md.uuid kernel parameters: %s", rd_md_uuids)
|
|
||||||
with open('%s/etc/default/grub' % path, 'r') as g:
|
|
||||||
contents = g.read()
|
|
||||||
with open('%s/etc/default/grub' % path, 'w') as g:
|
|
||||||
g.write(
|
|
||||||
re.sub(r'GRUB_CMDLINE_LINUX="(.*)"',
|
|
||||||
r'GRUB_CMDLINE_LINUX="\1 %s"'
|
|
||||||
% " ".join(rd_md_uuids),
|
|
||||||
contents))
|
|
||||||
|
|
||||||
# Generate the grub configuration file
|
|
||||||
utils.execute('chroot %(path)s /bin/sh -c '
|
|
||||||
'"%(bin)s-mkconfig -o '
|
|
||||||
'/boot/%(bin)s/grub.cfg"' %
|
|
||||||
{'path': path, 'bin': binary_name}, shell=True,
|
|
||||||
env_variables={'PATH': path_variable})
|
|
||||||
|
|
||||||
LOG.info("GRUB2 successfully installed on %s", device)
|
LOG.info("GRUB2 successfully installed on %s", device)
|
||||||
|
|
||||||
|
@ -677,6 +679,7 @@ def _install_grub2(device, root_uuid, efi_system_part_uuid=None,
|
||||||
raise errors.CommandExecutionError(error_msg)
|
raise errors.CommandExecutionError(error_msg)
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
|
LOG.debug('Executing _install_grub2 clean-up.')
|
||||||
# Umount binds and partition
|
# Umount binds and partition
|
||||||
umount_warn_msg = "Unable to umount %(path)s. Error: %(error)s"
|
umount_warn_msg = "Unable to umount %(path)s. Error: %(error)s"
|
||||||
|
|
||||||
|
@ -693,7 +696,9 @@ def _install_grub2(device, root_uuid, efi_system_part_uuid=None,
|
||||||
raise errors.CommandExecutionError(error_msg)
|
raise errors.CommandExecutionError(error_msg)
|
||||||
|
|
||||||
# If umounting the binds succeed then we can try to delete it
|
# If umounting the binds succeed then we can try to delete it
|
||||||
if _umount_all_partitions(path, path_variable, umount_warn_msg):
|
if _umount_all_partitions(path,
|
||||||
|
path_variable,
|
||||||
|
umount_warn_msg):
|
||||||
try:
|
try:
|
||||||
utils.execute('umount', path, attempts=3, delay_on_retry=True)
|
utils.execute('umount', path, attempts=3, delay_on_retry=True)
|
||||||
except processutils.ProcessExecutionError as e:
|
except processutils.ProcessExecutionError as e:
|
||||||
|
@ -704,6 +709,242 @@ def _install_grub2(device, root_uuid, efi_system_part_uuid=None,
|
||||||
shutil.rmtree(path)
|
shutil.rmtree(path)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_path_variable():
|
||||||
|
# Add /bin to PATH variable as grub requires it to find efibootmgr
|
||||||
|
# when running in uefi boot mode.
|
||||||
|
# Add /usr/sbin to PATH variable to ensure it is there as we do
|
||||||
|
# not use full path to grub binary anymore.
|
||||||
|
path_variable = os.environ.get('PATH', '')
|
||||||
|
return '%s:/bin:/usr/sbin:/sbin' % path_variable
|
||||||
|
|
||||||
|
|
||||||
|
def _configure_grub(device, path):
|
||||||
|
"""Make consolidated grub configuration as it is device aware.
|
||||||
|
|
||||||
|
:param device: The device for the filesystem.
|
||||||
|
:param path: The path in which the filesystem is mounted.
|
||||||
|
"""
|
||||||
|
LOG.debug('Attempting to generate grub Configuration')
|
||||||
|
path_variable = _get_path_variable()
|
||||||
|
binary_name = "grub"
|
||||||
|
if os.path.exists(os.path.join(path, 'usr/sbin/grub2-install')):
|
||||||
|
binary_name = "grub2"
|
||||||
|
# If the image has dracut installed, set the rd.md.uuid kernel
|
||||||
|
# parameter for discovered md devices.
|
||||||
|
if hardware.is_md_device(device) and _has_dracut(path):
|
||||||
|
rd_md_uuids = ["rd.md.uuid=%s" % x['UUID']
|
||||||
|
for x in hardware.md_get_raid_devices().values()]
|
||||||
|
LOG.debug("Setting rd.md.uuid kernel parameters: %s", rd_md_uuids)
|
||||||
|
with open('%s/etc/default/grub' % path, 'r') as g:
|
||||||
|
contents = g.read()
|
||||||
|
with open('%s/etc/default/grub' % path, 'w') as g:
|
||||||
|
g.write(
|
||||||
|
re.sub(r'GRUB_CMDLINE_LINUX="(.*)"',
|
||||||
|
r'GRUB_CMDLINE_LINUX="\1 %s"'
|
||||||
|
% " ".join(rd_md_uuids),
|
||||||
|
contents))
|
||||||
|
|
||||||
|
utils.execute('chroot %(path)s /bin/sh -c '
|
||||||
|
'"%(bin)s-mkconfig -o '
|
||||||
|
'/boot/%(bin)s/grub.cfg"' %
|
||||||
|
{'path': path, 'bin': binary_name}, shell=True,
|
||||||
|
env_variables={'PATH': path_variable,
|
||||||
|
'GRUB_DISABLE_OS_PROBER': 'true',
|
||||||
|
'GRUB_SAVEDEFAULT': 'true'},
|
||||||
|
use_standard_locale=True)
|
||||||
|
LOG.debug('Completed basic grub configuration.')
|
||||||
|
|
||||||
|
|
||||||
|
def _mount_for_chroot(path):
|
||||||
|
"""Mount items for grub-mkconfig to succeed."""
|
||||||
|
LOG.debug('Mounting Linux standard partitions for bootloader '
|
||||||
|
'configuration generation')
|
||||||
|
for fs in BIND_MOUNTS:
|
||||||
|
utils.execute('mount', '-o', 'bind', fs, path + fs)
|
||||||
|
utils.execute('mount', '-t', 'sysfs', 'none', path + '/sys')
|
||||||
|
|
||||||
|
|
||||||
|
def _try_preserve_efi_assets(device, path,
|
||||||
|
efi_system_part_uuid,
|
||||||
|
efi_partitions,
|
||||||
|
efi_partition_mount_point):
|
||||||
|
"""Attempt to preserve UEFI boot assets.
|
||||||
|
|
||||||
|
:param device: The device upon which wich to try to preserve
|
||||||
|
assets.
|
||||||
|
:param path: The path in which the filesystem is already mounted
|
||||||
|
which we should examine to preserve assets from.
|
||||||
|
:param efi_system_part_uuid: The partition ID representing the
|
||||||
|
created EFI system partition.
|
||||||
|
:param efi_partitions: The list of partitions upon wich to
|
||||||
|
write the preserved assets to.
|
||||||
|
:param efi_partition_mount_point: The folder at which to mount
|
||||||
|
the assets for the process of
|
||||||
|
preservation.
|
||||||
|
|
||||||
|
:returns: True if assets have been preserved, otherwise False.
|
||||||
|
None is the result of this method if a failure has
|
||||||
|
occured.
|
||||||
|
"""
|
||||||
|
efi_assets_folder = efi_partition_mount_point + '/EFI'
|
||||||
|
if os.path.exists(efi_assets_folder):
|
||||||
|
# We appear to have EFI Assets, that need to be preserved
|
||||||
|
# and as such if we succeed preserving them, we will be returned
|
||||||
|
# True from _preserve_efi_assets to correspond with success or
|
||||||
|
# failure in this action.
|
||||||
|
# NOTE(TheJulia): Still makes sense to invoke grub-install as
|
||||||
|
# fragmentation of grub has occured.
|
||||||
|
if (os.path.exists(os.path.join(path, 'usr/sbin/grub2-install'))
|
||||||
|
or os.path.exists(os.path.join(path, 'usr/sbin/grub-install'))):
|
||||||
|
_configure_grub(device, path)
|
||||||
|
# But first, if we have grub, we should try to build a grub config!
|
||||||
|
LOG.debug('EFI asset folder detected, attempting to preserve assets.')
|
||||||
|
if _preserve_efi_assets(path, efi_assets_folder,
|
||||||
|
efi_partitions,
|
||||||
|
efi_partition_mount_point):
|
||||||
|
try:
|
||||||
|
# Since we have preserved the assets, we should be able
|
||||||
|
# to call the _efi_boot_setup method to scan the device
|
||||||
|
# and add loader entries
|
||||||
|
efi_preserved = _efi_boot_setup(device, efi_system_part_uuid)
|
||||||
|
# Executed before the return so we don't return and then begin
|
||||||
|
# execution.
|
||||||
|
return efi_preserved
|
||||||
|
except Exception as e:
|
||||||
|
# Remount the partition and proceed as we were.
|
||||||
|
LOG.debug('Exception encountered while attempting to '
|
||||||
|
'setup the EFI loader from a root '
|
||||||
|
'filesystem. Error: %s', e)
|
||||||
|
|
||||||
|
|
||||||
|
def _efi_boot_setup(device, efi_system_part_uuid=None, target_boot_mode=None):
|
||||||
|
"""Identify and setup an EFI bootloader from supplied partition/disk.
|
||||||
|
|
||||||
|
:param device: The device upon which to attempt the EFI bootloader setup.
|
||||||
|
:param efi_system_part_uuid: The partition UUID to utilize in searching
|
||||||
|
for an EFI bootloader.
|
||||||
|
:param target_boot_mode: The requested boot mode target for the
|
||||||
|
machine. This is optional and is mainly used
|
||||||
|
for the purposes of identifying a mismatch and
|
||||||
|
reporting a warning accordingly.
|
||||||
|
:returns: True if we succeeded in setting up an EFI bootloader in the
|
||||||
|
EFI nvram table.
|
||||||
|
False if we were unable to set the machine to EFI boot,
|
||||||
|
due to inability to locate assets required OR the efibootmgr
|
||||||
|
tool not being present.
|
||||||
|
None is returned if the node is NOT in UEFI boot mode or
|
||||||
|
the system is deploying upon a software RAID device.
|
||||||
|
"""
|
||||||
|
boot = hardware.dispatch_to_managers('get_boot_info')
|
||||||
|
# Explicitly only run if a target_boot_mode is set which prevents
|
||||||
|
# callers following-up from re-logging the same message
|
||||||
|
if target_boot_mode and boot.current_boot_mode != target_boot_mode:
|
||||||
|
LOG.warning('Boot mode mismatch: target boot mode is %(target)s, '
|
||||||
|
'current boot mode is %(current)s. Installing boot '
|
||||||
|
'loader may fail or work incorrectly.',
|
||||||
|
{'target': target_boot_mode,
|
||||||
|
'current': boot.current_boot_mode})
|
||||||
|
|
||||||
|
# FIXME(arne_wiebalck): make software RAID work with efibootmgr
|
||||||
|
if (boot.current_boot_mode == 'uefi'
|
||||||
|
and not hardware.is_md_device(device)):
|
||||||
|
try:
|
||||||
|
utils.execute('efibootmgr', '--version')
|
||||||
|
except FileNotFoundError:
|
||||||
|
LOG.warning("efibootmgr is not available in the ramdisk")
|
||||||
|
else:
|
||||||
|
if _manage_uefi(device,
|
||||||
|
efi_system_part_uuid=efi_system_part_uuid):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def _preserve_efi_assets(path, efi_assets_folder, efi_partitions,
|
||||||
|
efi_partition_mount_point):
|
||||||
|
"""Preserve the EFI assets in a partition image.
|
||||||
|
|
||||||
|
:param path: The path used for the mounted image filesystem.
|
||||||
|
:param efi_assets_folder: The folder where we can find the
|
||||||
|
UEFI assets required for booting.
|
||||||
|
:param efi_partitions: The list of partitions upon which to
|
||||||
|
write the perserved assets to.
|
||||||
|
:param efi_partition_mount_point: The folder at which to mount
|
||||||
|
the assets for the process of
|
||||||
|
preservation.
|
||||||
|
:returns: True if EFI assets were able to be located and preserved
|
||||||
|
to their appropriate locations based upon the supplied
|
||||||
|
efi_partitions list.
|
||||||
|
False if any error is encountered in this process.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
save_efi = os.path.join(tempfile.mkdtemp(), 'efi_loader')
|
||||||
|
LOG.debug('Copying EFI assets to %s.', save_efi)
|
||||||
|
shutil.copytree(efi_assets_folder, save_efi)
|
||||||
|
|
||||||
|
# Identify grub2 config file for EFI booting as grub may require it
|
||||||
|
# in the folder.
|
||||||
|
|
||||||
|
destlist = os.listdir(efi_assets_folder)
|
||||||
|
grub2_file = os.path.join(path, 'boot/grub2/grub.cfg')
|
||||||
|
if os.path.isfile(grub2_file):
|
||||||
|
LOG.debug('Local Grub2 configuration detected.')
|
||||||
|
# A grub2 config seems to be present, we should preserve it!
|
||||||
|
for dest in destlist:
|
||||||
|
grub_dest = os.path.join(save_efi, dest, 'grub.cfg')
|
||||||
|
if not os.path.isfile(grub_dest):
|
||||||
|
LOG.debug('A grub.cfg file was not found in %s. %s'
|
||||||
|
'will be copied to that location.',
|
||||||
|
grub_dest, grub2_file)
|
||||||
|
try:
|
||||||
|
shutil.copy2(grub2_file, grub_dest)
|
||||||
|
except (IOError, OSError, shutil.SameFileError) as e:
|
||||||
|
LOG.warning('Failed to copy grub.cfg file for '
|
||||||
|
'EFI boot operation. Error %s', e)
|
||||||
|
grub2_env_file = os.path.join(path, 'boot/grub2/grubenv')
|
||||||
|
# NOTE(TheJulia): By saving the default, this file should be created.
|
||||||
|
# this appears to what diskimage-builder does.
|
||||||
|
# if the file is just a file, then we'll need to copy it. If it is
|
||||||
|
# anything else like a link, we're good. This behaivor is inconsistent
|
||||||
|
# depending on packager install scripts for grub.
|
||||||
|
if os.path.isfile(grub2_env_file):
|
||||||
|
LOG.debug('Detected grub environment file %s, will attempt '
|
||||||
|
'to copy this file to align with apparent bootloaders',
|
||||||
|
grub2_env_file)
|
||||||
|
for dest in destlist:
|
||||||
|
grub2env_dest = os.path.join(save_efi, dest, 'grubenv')
|
||||||
|
if not os.path.isfile(grub2env_dest):
|
||||||
|
LOG.debug('A grubenv file was not found. Copying '
|
||||||
|
'to %s along with the grub.cfg file as '
|
||||||
|
'grub generally expects it is present.',
|
||||||
|
grub2env_dest)
|
||||||
|
try:
|
||||||
|
shutil.copy2(grub2_env_file, grub2env_dest)
|
||||||
|
except (IOError, OSError, shutil.SameFileError) as e:
|
||||||
|
LOG.warning('Failed to copy grubenv file. '
|
||||||
|
'Error: %s', e)
|
||||||
|
# Loop through partitions because software RAID.
|
||||||
|
for efi_part in efi_partitions:
|
||||||
|
utils.execute('mount', '-t', 'vfat', efi_part,
|
||||||
|
efi_partition_mount_point)
|
||||||
|
shutil.copytree(save_efi, efi_assets_folder)
|
||||||
|
LOG.debug('Files preserved to %(disk)s for %(part)s. '
|
||||||
|
'Files: %(filelist)s From: %(from)s',
|
||||||
|
{'disk': efi_part,
|
||||||
|
'part': efi_partition_mount_point,
|
||||||
|
'filelist': os.listdir(efi_assets_folder),
|
||||||
|
'from': save_efi})
|
||||||
|
utils.execute('umount', efi_partition_mount_point)
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
LOG.debug('Failed to preserve EFI assets. Error %s', e)
|
||||||
|
try:
|
||||||
|
utils.execute('umount', efi_partition_mount_point)
|
||||||
|
except Exception as e:
|
||||||
|
LOG.debug('Exception encountered while attempting unmount '
|
||||||
|
'the EFI partition mount point. Error: %s', e)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
class ImageExtension(base.BaseAgentExtension):
|
class ImageExtension(base.BaseAgentExtension):
|
||||||
|
|
||||||
@base.async_command('install_bootloader')
|
@base.async_command('install_bootloader')
|
||||||
|
@ -739,34 +980,13 @@ class ImageExtension(base.BaseAgentExtension):
|
||||||
else:
|
else:
|
||||||
ignore_failure = ignore_bootloader_failure
|
ignore_failure = ignore_bootloader_failure
|
||||||
|
|
||||||
boot = hardware.dispatch_to_managers('get_boot_info')
|
try:
|
||||||
if boot.current_boot_mode != target_boot_mode:
|
if _efi_boot_setup(device, efi_system_part_uuid, target_boot_mode):
|
||||||
LOG.warning('Boot mode mismatch: target boot mode is %(target)s, '
|
return
|
||||||
'current boot mode is %(current)s. Installing boot '
|
except Exception as e:
|
||||||
'loader may fail or work incorrectly.',
|
LOG.error('Error setting up bootloader. Error %s', e)
|
||||||
{'target': target_boot_mode,
|
if not ignore_failure:
|
||||||
'current': boot.current_boot_mode})
|
raise
|
||||||
|
|
||||||
# FIXME(arne_wiebalck): make software RAID work with efibootmgr
|
|
||||||
if (boot.current_boot_mode == 'uefi'
|
|
||||||
and not hardware.is_md_device(device)):
|
|
||||||
has_efibootmgr = True
|
|
||||||
try:
|
|
||||||
utils.execute('efibootmgr', '--version')
|
|
||||||
except FileNotFoundError:
|
|
||||||
LOG.warning("efibootmgr is not available in the ramdisk")
|
|
||||||
has_efibootmgr = False
|
|
||||||
|
|
||||||
if has_efibootmgr:
|
|
||||||
try:
|
|
||||||
if _manage_uefi(
|
|
||||||
device,
|
|
||||||
efi_system_part_uuid=efi_system_part_uuid):
|
|
||||||
return
|
|
||||||
except Exception as e:
|
|
||||||
LOG.error('Error setting up bootloader. Error %s', e)
|
|
||||||
if not ignore_failure:
|
|
||||||
raise
|
|
||||||
|
|
||||||
# We don't have a working root UUID detection for whole disk images.
|
# We don't have a working root UUID detection for whole disk images.
|
||||||
# Until we can do it, avoid a confusing traceback.
|
# Until we can do it, avoid a confusing traceback.
|
||||||
|
|
|
@ -539,7 +539,10 @@ efibootmgr: ** Warning ** : Boot0005 has same label ironic1\n
|
||||||
'/boot/grub/grub.cfg"' % self.fake_dir),
|
'/boot/grub/grub.cfg"' % self.fake_dir),
|
||||||
shell=True,
|
shell=True,
|
||||||
env_variables={
|
env_variables={
|
||||||
'PATH': '/sbin:/bin:/usr/sbin:/sbin'}),
|
'PATH': '/sbin:/bin:/usr/sbin:/sbin',
|
||||||
|
'GRUB_DISABLE_OS_PROBER': 'true',
|
||||||
|
'GRUB_SAVEDEFAULT': 'true'},
|
||||||
|
use_standard_locale=True),
|
||||||
mock.call(('chroot %s /bin/sh -c "umount -a -t vfat"' %
|
mock.call(('chroot %s /bin/sh -c "umount -a -t vfat"' %
|
||||||
(self.fake_dir)), shell=True,
|
(self.fake_dir)), shell=True,
|
||||||
env_variables={
|
env_variables={
|
||||||
|
@ -599,7 +602,10 @@ efibootmgr: ** Warning ** : Boot0005 has same label ironic1\n
|
||||||
'/boot/grub/grub.cfg"' % self.fake_dir),
|
'/boot/grub/grub.cfg"' % self.fake_dir),
|
||||||
shell=True,
|
shell=True,
|
||||||
env_variables={
|
env_variables={
|
||||||
'PATH': '/sbin:/bin:/usr/sbin:/sbin'}),
|
'PATH': '/sbin:/bin:/usr/sbin:/sbin',
|
||||||
|
'GRUB_DISABLE_OS_PROBER': 'true',
|
||||||
|
'GRUB_SAVEDEFAULT': 'true'},
|
||||||
|
use_standard_locale=True),
|
||||||
mock.call(('chroot %s /bin/sh -c "umount -a -t vfat"' %
|
mock.call(('chroot %s /bin/sh -c "umount -a -t vfat"' %
|
||||||
(self.fake_dir)), shell=True,
|
(self.fake_dir)), shell=True,
|
||||||
env_variables={
|
env_variables={
|
||||||
|
@ -621,6 +627,7 @@ efibootmgr: ** Warning ** : Boot0005 has same label ironic1\n
|
||||||
uuid=self.fake_prep_boot_part_uuid)
|
uuid=self.fake_prep_boot_part_uuid)
|
||||||
self.assertFalse(mock_dispatch.called)
|
self.assertFalse(mock_dispatch.called)
|
||||||
|
|
||||||
|
@mock.patch.object(os.path, 'ismount', lambda *_: True)
|
||||||
@mock.patch.object(os.path, 'exists', lambda *_: False)
|
@mock.patch.object(os.path, 'exists', lambda *_: False)
|
||||||
@mock.patch.object(image, '_is_bootloader_loaded', lambda *_: True)
|
@mock.patch.object(image, '_is_bootloader_loaded', lambda *_: True)
|
||||||
@mock.patch.object(hardware, 'is_md_device', autospec=True)
|
@mock.patch.object(hardware, 'is_md_device', autospec=True)
|
||||||
|
@ -677,7 +684,10 @@ efibootmgr: ** Warning ** : Boot0005 has same label ironic1\n
|
||||||
'/boot/grub/grub.cfg"' % self.fake_dir),
|
'/boot/grub/grub.cfg"' % self.fake_dir),
|
||||||
shell=True,
|
shell=True,
|
||||||
env_variables={
|
env_variables={
|
||||||
'PATH': '/sbin:/bin:/usr/sbin:/sbin'}),
|
'PATH': '/sbin:/bin:/usr/sbin:/sbin',
|
||||||
|
'GRUB_DISABLE_OS_PROBER': 'true',
|
||||||
|
'GRUB_SAVEDEFAULT': 'true'},
|
||||||
|
use_standard_locale=True),
|
||||||
mock.call('umount', self.fake_dir + '/boot/efi',
|
mock.call('umount', self.fake_dir + '/boot/efi',
|
||||||
attempts=3, delay_on_retry=True),
|
attempts=3, delay_on_retry=True),
|
||||||
mock.call(('chroot %s /bin/sh -c "umount -a -t vfat"' %
|
mock.call(('chroot %s /bin/sh -c "umount -a -t vfat"' %
|
||||||
|
@ -702,6 +712,468 @@ efibootmgr: ** Warning ** : Boot0005 has same label ironic1\n
|
||||||
uuid=self.fake_efi_system_part_uuid)
|
uuid=self.fake_efi_system_part_uuid)
|
||||||
self.assertFalse(mock_dispatch.called)
|
self.assertFalse(mock_dispatch.called)
|
||||||
|
|
||||||
|
@mock.patch.object(os.path, 'ismount', lambda *_: False)
|
||||||
|
@mock.patch.object(os, 'listdir', lambda *_: ['file1', 'file2'])
|
||||||
|
@mock.patch.object(image, '_is_bootloader_loaded', lambda *_: False)
|
||||||
|
@mock.patch.object(image, '_efi_boot_setup', autospec=True)
|
||||||
|
@mock.patch.object(shutil, 'copytree', autospec=True)
|
||||||
|
@mock.patch.object(os.path, 'exists', autospec=True)
|
||||||
|
@mock.patch.object(hardware, 'is_md_device', autospec=True)
|
||||||
|
@mock.patch.object(hardware, 'md_get_raid_devices', autospec=True)
|
||||||
|
@mock.patch.object(os, 'environ', autospec=True)
|
||||||
|
@mock.patch.object(os, 'makedirs', autospec=True)
|
||||||
|
@mock.patch.object(image, '_get_partition', autospec=True)
|
||||||
|
def test__install_grub2_uefi_partition_image_with_loader(
|
||||||
|
self, mock_get_part_uuid, mkdir_mock,
|
||||||
|
environ_mock, mock_md_get_raid_devices,
|
||||||
|
mock_is_md_device, mock_exists,
|
||||||
|
mock_copytree, mock_efi_setup,
|
||||||
|
mock_execute, mock_dispatch):
|
||||||
|
mock_exists.return_value = True
|
||||||
|
mock_efi_setup.return_value = True
|
||||||
|
mock_get_part_uuid.side_effect = [self.fake_root_part,
|
||||||
|
self.fake_efi_system_part]
|
||||||
|
environ_mock.get.return_value = '/sbin'
|
||||||
|
mock_is_md_device.return_value = False
|
||||||
|
mock_md_get_raid_devices.return_value = {}
|
||||||
|
|
||||||
|
image._install_grub2(
|
||||||
|
self.fake_dev, root_uuid=self.fake_root_uuid,
|
||||||
|
efi_system_part_uuid=self.fake_efi_system_part_uuid,
|
||||||
|
target_boot_mode='uefi')
|
||||||
|
mock_efi_setup.assert_called_once_with(self.fake_dev,
|
||||||
|
self.fake_efi_system_part_uuid)
|
||||||
|
mock_copytree.assert_has_calls([
|
||||||
|
mock.call(self.fake_dir + '/boot/efi/EFI',
|
||||||
|
self.fake_dir + '/efi_loader'),
|
||||||
|
mock.call(self.fake_dir + '/efi_loader',
|
||||||
|
self.fake_dir + '/boot/efi/EFI')])
|
||||||
|
|
||||||
|
expected = [mock.call('mount', '/dev/fake2', self.fake_dir),
|
||||||
|
mock.call('mount', '-o', 'bind', '/dev',
|
||||||
|
self.fake_dir + '/dev'),
|
||||||
|
mock.call('mount', '-o', 'bind', '/proc',
|
||||||
|
self.fake_dir + '/proc'),
|
||||||
|
mock.call('mount', '-o', 'bind', '/run',
|
||||||
|
self.fake_dir + '/run'),
|
||||||
|
mock.call('mount', '-t', 'sysfs', 'none',
|
||||||
|
self.fake_dir + '/sys'),
|
||||||
|
mock.call('chroot %s /bin/sh -c "grub2-mkconfig -o '
|
||||||
|
'/boot/grub2/grub.cfg"' % self.fake_dir,
|
||||||
|
shell=True,
|
||||||
|
env_variables={
|
||||||
|
'PATH': '/sbin:/bin:/usr/sbin:/sbin',
|
||||||
|
'GRUB_DISABLE_OS_PROBER': 'true',
|
||||||
|
'GRUB_SAVEDEFAULT': 'true'},
|
||||||
|
use_standard_locale=True),
|
||||||
|
mock.call('mount', '-t', 'vfat', '/dev/fake1',
|
||||||
|
self.fake_dir + '/boot/efi'),
|
||||||
|
mock.call('umount', self.fake_dir + '/boot/efi'),
|
||||||
|
mock.call('chroot %s /bin/sh -c "umount -a -t '
|
||||||
|
'vfat"' % self.fake_dir, shell=True,
|
||||||
|
env_variables={
|
||||||
|
'PATH': '/sbin:/bin:/usr/sbin:/sbin'}),
|
||||||
|
mock.call('umount', self.fake_dir + '/dev', attempts=3,
|
||||||
|
delay_on_retry=True),
|
||||||
|
mock.call('umount', self.fake_dir + '/proc', attempts=3,
|
||||||
|
delay_on_retry=True),
|
||||||
|
mock.call('umount', self.fake_dir + '/run', attempts=3,
|
||||||
|
delay_on_retry=True),
|
||||||
|
mock.call('umount', self.fake_dir + '/sys', attempts=3,
|
||||||
|
delay_on_retry=True),
|
||||||
|
mock.call('umount', self.fake_dir, attempts=3,
|
||||||
|
delay_on_retry=True)]
|
||||||
|
mkdir_mock.assert_not_called()
|
||||||
|
mock_execute.assert_has_calls(expected)
|
||||||
|
mock_get_part_uuid.assert_any_call(self.fake_dev,
|
||||||
|
uuid=self.fake_root_uuid)
|
||||||
|
mock_get_part_uuid.assert_any_call(self.fake_dev,
|
||||||
|
uuid=self.fake_efi_system_part_uuid)
|
||||||
|
self.assertFalse(mock_dispatch.called)
|
||||||
|
|
||||||
|
@mock.patch.object(os, 'listdir', lambda *_: ['file1', 'file2'])
|
||||||
|
@mock.patch.object(image, '_is_bootloader_loaded', lambda *_: False)
|
||||||
|
@mock.patch.object(shutil, 'copy2', autospec=True)
|
||||||
|
@mock.patch.object(os.path, 'isfile', autospec=True)
|
||||||
|
@mock.patch.object(image, '_efi_boot_setup', autospec=True)
|
||||||
|
@mock.patch.object(shutil, 'copytree', autospec=True)
|
||||||
|
@mock.patch.object(os.path, 'exists', autospec=True)
|
||||||
|
@mock.patch.object(hardware, 'is_md_device', autospec=True)
|
||||||
|
@mock.patch.object(hardware, 'md_get_raid_devices', autospec=True)
|
||||||
|
@mock.patch.object(os, 'environ', autospec=True)
|
||||||
|
@mock.patch.object(os, 'makedirs', autospec=True)
|
||||||
|
@mock.patch.object(image, '_get_partition', autospec=True)
|
||||||
|
def test__install_grub2_uefi_partition_image_with_loader_with_grubcfg(
|
||||||
|
self, mock_get_part_uuid, mkdir_mock,
|
||||||
|
environ_mock, mock_md_get_raid_devices,
|
||||||
|
mock_is_md_device, mock_exists,
|
||||||
|
mock_copytree, mock_efi_setup,
|
||||||
|
mock_isfile, mock_copy2,
|
||||||
|
mock_execute, mock_dispatch):
|
||||||
|
mock_exists.return_value = True
|
||||||
|
mock_efi_setup.return_value = True
|
||||||
|
mock_get_part_uuid.side_effect = [self.fake_root_part,
|
||||||
|
self.fake_efi_system_part]
|
||||||
|
environ_mock.get.return_value = '/sbin'
|
||||||
|
mock_is_md_device.return_value = False
|
||||||
|
mock_md_get_raid_devices.return_value = {}
|
||||||
|
mock_isfile.side_effect = [True, False, False, True, True, False]
|
||||||
|
|
||||||
|
image._install_grub2(
|
||||||
|
self.fake_dev, root_uuid=self.fake_root_uuid,
|
||||||
|
efi_system_part_uuid=self.fake_efi_system_part_uuid,
|
||||||
|
target_boot_mode='uefi')
|
||||||
|
mock_efi_setup.assert_called_once_with(self.fake_dev,
|
||||||
|
self.fake_efi_system_part_uuid)
|
||||||
|
mock_copytree.assert_has_calls([
|
||||||
|
mock.call(self.fake_dir + '/boot/efi/EFI',
|
||||||
|
self.fake_dir + '/efi_loader'),
|
||||||
|
mock.call(self.fake_dir + '/efi_loader',
|
||||||
|
self.fake_dir + '/boot/efi/EFI')])
|
||||||
|
|
||||||
|
expected = [mock.call('mount', '/dev/fake2', self.fake_dir),
|
||||||
|
mock.call('mount', '-o', 'bind', '/dev',
|
||||||
|
self.fake_dir + '/dev'),
|
||||||
|
mock.call('mount', '-o', 'bind', '/proc',
|
||||||
|
self.fake_dir + '/proc'),
|
||||||
|
mock.call('mount', '-o', 'bind', '/run',
|
||||||
|
self.fake_dir + '/run'),
|
||||||
|
mock.call('mount', '-t', 'sysfs', 'none',
|
||||||
|
self.fake_dir + '/sys'),
|
||||||
|
mock.call(('chroot ' + self.fake_dir + ' /bin/sh -c '
|
||||||
|
'"grub2-mkconfig -o /boot/grub2/grub.cfg"'),
|
||||||
|
shell=True,
|
||||||
|
env_variables={
|
||||||
|
'PATH': '/sbin:/bin:/usr/sbin:/sbin',
|
||||||
|
'GRUB_DISABLE_OS_PROBER': 'true',
|
||||||
|
'GRUB_SAVEDEFAULT': 'true'},
|
||||||
|
use_standard_locale=True),
|
||||||
|
mock.call('mount', '-t', 'vfat', '/dev/fake1',
|
||||||
|
self.fake_dir + '/boot/efi'),
|
||||||
|
mock.call('umount', self.fake_dir + '/boot/efi'),
|
||||||
|
mock.call(('chroot ' + self.fake_dir
|
||||||
|
+ ' /bin/sh -c "umount -a -t vfat"'),
|
||||||
|
shell=True,
|
||||||
|
env_variables={
|
||||||
|
'PATH': '/sbin:/bin:/usr/sbin:/sbin'}),
|
||||||
|
mock.call('umount', self.fake_dir + '/dev', attempts=3,
|
||||||
|
delay_on_retry=True),
|
||||||
|
mock.call('umount', self.fake_dir + '/proc', attempts=3,
|
||||||
|
delay_on_retry=True),
|
||||||
|
mock.call('umount', self.fake_dir + '/run', attempts=3,
|
||||||
|
delay_on_retry=True),
|
||||||
|
mock.call('umount', self.fake_dir + '/sys', attempts=3,
|
||||||
|
delay_on_retry=True),
|
||||||
|
mock.call('umount', self.fake_dir, attempts=3,
|
||||||
|
delay_on_retry=True)]
|
||||||
|
mkdir_mock.assert_not_called()
|
||||||
|
mock_execute.assert_has_calls(expected)
|
||||||
|
mock_copy2.assert_has_calls([])
|
||||||
|
mock_get_part_uuid.assert_any_call(self.fake_dev,
|
||||||
|
uuid=self.fake_root_uuid)
|
||||||
|
mock_get_part_uuid.assert_any_call(self.fake_dev,
|
||||||
|
uuid=self.fake_efi_system_part_uuid)
|
||||||
|
self.assertFalse(mock_dispatch.called)
|
||||||
|
|
||||||
|
@mock.patch.object(os.path, 'ismount', lambda *_: True)
|
||||||
|
@mock.patch.object(image, '_is_bootloader_loaded', lambda *_: False)
|
||||||
|
@mock.patch.object(image, '_preserve_efi_assets', autospec=True)
|
||||||
|
@mock.patch.object(image, '_efi_boot_setup', autospec=True)
|
||||||
|
@mock.patch.object(os.path, 'exists', autospec=True)
|
||||||
|
@mock.patch.object(hardware, 'is_md_device', autospec=True)
|
||||||
|
@mock.patch.object(hardware, 'md_get_raid_devices', autospec=True)
|
||||||
|
@mock.patch.object(os, 'environ', autospec=True)
|
||||||
|
@mock.patch.object(os, 'makedirs', autospec=True)
|
||||||
|
@mock.patch.object(image, '_get_partition', autospec=True)
|
||||||
|
def test__install_grub2_uefi_partition_image_with_preserve_failure(
|
||||||
|
self, mock_get_part_uuid, mkdir_mock,
|
||||||
|
environ_mock, mock_md_get_raid_devices,
|
||||||
|
mock_is_md_device, mock_exists,
|
||||||
|
mock_efi_setup,
|
||||||
|
mock_preserve_efi_assets,
|
||||||
|
mock_execute, mock_dispatch):
|
||||||
|
mock_exists.return_value = True
|
||||||
|
mock_efi_setup.side_effect = Exception('meow')
|
||||||
|
mock_get_part_uuid.side_effect = [self.fake_root_part,
|
||||||
|
self.fake_efi_system_part]
|
||||||
|
environ_mock.get.return_value = '/sbin'
|
||||||
|
mock_is_md_device.return_value = False
|
||||||
|
mock_md_get_raid_devices.return_value = {}
|
||||||
|
mock_preserve_efi_assets.return_value = False
|
||||||
|
|
||||||
|
image._install_grub2(
|
||||||
|
self.fake_dev, root_uuid=self.fake_root_uuid,
|
||||||
|
efi_system_part_uuid=self.fake_efi_system_part_uuid,
|
||||||
|
target_boot_mode='uefi')
|
||||||
|
self.assertFalse(mock_efi_setup.called)
|
||||||
|
|
||||||
|
expected = [mock.call('mount', '/dev/fake2', self.fake_dir),
|
||||||
|
mock.call('mount', '-o', 'bind', '/dev',
|
||||||
|
self.fake_dir + '/dev'),
|
||||||
|
mock.call('mount', '-o', 'bind', '/proc',
|
||||||
|
self.fake_dir + '/proc'),
|
||||||
|
mock.call('mount', '-o', 'bind', '/run',
|
||||||
|
self.fake_dir + '/run'),
|
||||||
|
mock.call('mount', '-t', 'sysfs', 'none',
|
||||||
|
self.fake_dir + '/sys'),
|
||||||
|
mock.call(('chroot %s /bin/sh -c '
|
||||||
|
'"grub2-mkconfig -o '
|
||||||
|
'/boot/grub2/grub.cfg"' % self.fake_dir),
|
||||||
|
shell=True,
|
||||||
|
env_variables={
|
||||||
|
'PATH': '/sbin:/bin:/usr/sbin:/sbin',
|
||||||
|
'GRUB_DISABLE_OS_PROBER': 'true',
|
||||||
|
'GRUB_SAVEDEFAULT': 'true'},
|
||||||
|
use_standard_locale=True),
|
||||||
|
mock.call(('chroot %s /bin/sh -c "mount -a -t vfat"' %
|
||||||
|
(self.fake_dir)), shell=True,
|
||||||
|
env_variables={
|
||||||
|
'PATH': '/sbin:/bin:/usr/sbin:/sbin'}),
|
||||||
|
mock.call('mount', self.fake_efi_system_part,
|
||||||
|
self.fake_dir + '/boot/efi'),
|
||||||
|
mock.call(('chroot %s /bin/sh -c "grub2-install"' %
|
||||||
|
self.fake_dir), shell=True,
|
||||||
|
env_variables={
|
||||||
|
'PATH': '/sbin:/bin:/usr/sbin:/sbin'}),
|
||||||
|
mock.call(('chroot %s /bin/sh -c '
|
||||||
|
'"grub2-install --removable"' %
|
||||||
|
self.fake_dir), shell=True,
|
||||||
|
env_variables={
|
||||||
|
'PATH': '/sbin:/bin:/usr/sbin:/sbin'}),
|
||||||
|
mock.call(
|
||||||
|
'umount', self.fake_dir + '/boot/efi',
|
||||||
|
attempts=3, delay_on_retry=True),
|
||||||
|
mock.call('mount', self.fake_efi_system_part,
|
||||||
|
'/tmp/fake-dir/boot/efi'),
|
||||||
|
mock.call(('chroot %s /bin/sh -c '
|
||||||
|
'"grub2-mkconfig -o '
|
||||||
|
'/boot/grub2/grub.cfg"' % self.fake_dir),
|
||||||
|
shell=True,
|
||||||
|
env_variables={
|
||||||
|
'PATH': '/sbin:/bin:/usr/sbin:/sbin',
|
||||||
|
'GRUB_DISABLE_OS_PROBER': 'true',
|
||||||
|
'GRUB_SAVEDEFAULT': 'true'},
|
||||||
|
use_standard_locale=True),
|
||||||
|
mock.call('umount', self.fake_dir + '/boot/efi',
|
||||||
|
attempts=3, delay_on_retry=True),
|
||||||
|
mock.call(('chroot %s /bin/sh -c "umount -a -t vfat"' %
|
||||||
|
(self.fake_dir)), shell=True,
|
||||||
|
env_variables={
|
||||||
|
'PATH': '/sbin:/bin:/usr/sbin:/sbin'}),
|
||||||
|
mock.call('umount', self.fake_dir + '/dev',
|
||||||
|
attempts=3, delay_on_retry=True),
|
||||||
|
mock.call('umount', self.fake_dir + '/proc',
|
||||||
|
attempts=3, delay_on_retry=True),
|
||||||
|
mock.call('umount', self.fake_dir + '/run',
|
||||||
|
attempts=3, delay_on_retry=True),
|
||||||
|
mock.call('umount', self.fake_dir + '/sys',
|
||||||
|
attempts=3, delay_on_retry=True),
|
||||||
|
mock.call('umount', self.fake_dir, attempts=3,
|
||||||
|
delay_on_retry=True)]
|
||||||
|
|
||||||
|
mkdir_mock.assert_not_called()
|
||||||
|
mock_execute.assert_has_calls(expected)
|
||||||
|
mock_get_part_uuid.assert_any_call(self.fake_dev,
|
||||||
|
uuid=self.fake_root_uuid)
|
||||||
|
mock_get_part_uuid.assert_any_call(self.fake_dev,
|
||||||
|
uuid=self.fake_efi_system_part_uuid)
|
||||||
|
self.assertFalse(mock_dispatch.called)
|
||||||
|
mock_preserve_efi_assets.assert_called_with(
|
||||||
|
self.fake_dir,
|
||||||
|
self.fake_dir + '/boot/efi/EFI',
|
||||||
|
['/dev/fake1'],
|
||||||
|
self.fake_dir + '/boot/efi')
|
||||||
|
|
||||||
|
@mock.patch.object(image, '_is_bootloader_loaded', lambda *_: False)
|
||||||
|
@mock.patch.object(os, 'listdir', autospec=True)
|
||||||
|
@mock.patch.object(shutil, 'copy2', autospec=True)
|
||||||
|
@mock.patch.object(os.path, 'isfile', autospec=True)
|
||||||
|
@mock.patch.object(image, '_efi_boot_setup', autospec=True)
|
||||||
|
@mock.patch.object(shutil, 'copytree', autospec=True)
|
||||||
|
@mock.patch.object(os.path, 'exists', autospec=True)
|
||||||
|
@mock.patch.object(hardware, 'is_md_device', autospec=True)
|
||||||
|
@mock.patch.object(hardware, 'md_get_raid_devices', autospec=True)
|
||||||
|
@mock.patch.object(os, 'environ', autospec=True)
|
||||||
|
@mock.patch.object(os, 'makedirs', autospec=True)
|
||||||
|
@mock.patch.object(image, '_get_partition', autospec=True)
|
||||||
|
def test__install_grub2_uefi_partition_image_with_loader_grubcfg_fails(
|
||||||
|
self, mock_get_part_uuid, mkdir_mock,
|
||||||
|
environ_mock, mock_md_get_raid_devices,
|
||||||
|
mock_is_md_device, mock_exists,
|
||||||
|
mock_copytree, mock_efi_setup,
|
||||||
|
mock_isfile, mock_copy2,
|
||||||
|
mock_oslistdir, mock_execute,
|
||||||
|
mock_dispatch):
|
||||||
|
mock_exists.return_value = True
|
||||||
|
mock_efi_setup.return_value = True
|
||||||
|
mock_get_part_uuid.side_effect = [self.fake_root_part,
|
||||||
|
self.fake_efi_system_part]
|
||||||
|
environ_mock.get.return_value = '/sbin'
|
||||||
|
mock_is_md_device.return_value = False
|
||||||
|
mock_md_get_raid_devices.return_value = {}
|
||||||
|
mock_isfile.side_effect = [True, False, False, True, False,
|
||||||
|
True, False]
|
||||||
|
mock_copy2.side_effect = OSError('copy failed')
|
||||||
|
mock_oslistdir.return_value = ['file1', 'file2']
|
||||||
|
|
||||||
|
image._install_grub2(
|
||||||
|
self.fake_dev, root_uuid=self.fake_root_uuid,
|
||||||
|
efi_system_part_uuid=self.fake_efi_system_part_uuid,
|
||||||
|
target_boot_mode='uefi')
|
||||||
|
mock_efi_setup.assert_called_once_with(self.fake_dev,
|
||||||
|
self.fake_efi_system_part_uuid)
|
||||||
|
mock_copytree.assert_has_calls([
|
||||||
|
mock.call(self.fake_dir + '/boot/efi/EFI',
|
||||||
|
self.fake_dir + '/efi_loader'),
|
||||||
|
mock.call(self.fake_dir + '/efi_loader',
|
||||||
|
self.fake_dir + '/boot/efi/EFI')])
|
||||||
|
|
||||||
|
expected = [mock.call('mount', '/dev/fake2', self.fake_dir),
|
||||||
|
mock.call('mount', '-o', 'bind', '/dev',
|
||||||
|
self.fake_dir + '/dev'),
|
||||||
|
mock.call('mount', '-o', 'bind', '/proc',
|
||||||
|
self.fake_dir + '/proc'),
|
||||||
|
mock.call('mount', '-o', 'bind', '/run',
|
||||||
|
self.fake_dir + '/run'),
|
||||||
|
mock.call('mount', '-t', 'sysfs', 'none',
|
||||||
|
self.fake_dir + '/sys'),
|
||||||
|
mock.call(('chroot ' + self.fake_dir + ' /bin/sh -c '
|
||||||
|
'"grub2-mkconfig -o /boot/grub2/grub.cfg"'),
|
||||||
|
shell=True,
|
||||||
|
env_variables={
|
||||||
|
'PATH': '/sbin:/bin:/usr/sbin:/sbin',
|
||||||
|
'GRUB_DISABLE_OS_PROBER': 'true',
|
||||||
|
'GRUB_SAVEDEFAULT': 'true'},
|
||||||
|
use_standard_locale=True),
|
||||||
|
mock.call('mount', '-t', 'vfat', '/dev/fake1',
|
||||||
|
self.fake_dir + '/boot/efi'),
|
||||||
|
mock.call('umount', self.fake_dir + '/boot/efi'),
|
||||||
|
mock.call(('chroot ' + self.fake_dir
|
||||||
|
+ ' /bin/sh -c "umount -a -t vfat"'),
|
||||||
|
shell=True,
|
||||||
|
env_variables={
|
||||||
|
'PATH': '/sbin:/bin:/usr/sbin:/sbin'}),
|
||||||
|
mock.call('umount', self.fake_dir + '/dev', attempts=3,
|
||||||
|
delay_on_retry=True),
|
||||||
|
mock.call('umount', self.fake_dir + '/proc', attempts=3,
|
||||||
|
delay_on_retry=True),
|
||||||
|
mock.call('umount', self.fake_dir + '/run', attempts=3,
|
||||||
|
delay_on_retry=True),
|
||||||
|
mock.call('umount', self.fake_dir + '/sys', attempts=3,
|
||||||
|
delay_on_retry=True),
|
||||||
|
mock.call('umount', self.fake_dir, attempts=3,
|
||||||
|
delay_on_retry=True)]
|
||||||
|
mkdir_mock.assert_not_called()
|
||||||
|
mock_execute.assert_has_calls(expected)
|
||||||
|
self.assertEqual(3, mock_copy2.call_count)
|
||||||
|
mock_get_part_uuid.assert_any_call(self.fake_dev,
|
||||||
|
uuid=self.fake_root_uuid)
|
||||||
|
mock_get_part_uuid.assert_any_call(self.fake_dev,
|
||||||
|
uuid=self.fake_efi_system_part_uuid)
|
||||||
|
self.assertFalse(mock_dispatch.called)
|
||||||
|
self.assertEqual(2, mock_oslistdir.call_count)
|
||||||
|
|
||||||
|
@mock.patch.object(os.path, 'ismount', lambda *_: True)
|
||||||
|
@mock.patch.object(image, '_is_bootloader_loaded', lambda *_: False)
|
||||||
|
@mock.patch.object(os, 'listdir', autospec=True)
|
||||||
|
@mock.patch.object(image, '_efi_boot_setup', autospec=True)
|
||||||
|
@mock.patch.object(shutil, 'copytree', autospec=True)
|
||||||
|
@mock.patch.object(os.path, 'exists', autospec=True)
|
||||||
|
@mock.patch.object(hardware, 'is_md_device', autospec=True)
|
||||||
|
@mock.patch.object(hardware, 'md_get_raid_devices', autospec=True)
|
||||||
|
@mock.patch.object(os, 'environ', autospec=True)
|
||||||
|
@mock.patch.object(os, 'makedirs', autospec=True)
|
||||||
|
@mock.patch.object(image, '_get_partition', autospec=True)
|
||||||
|
def test__install_grub2_uefi_partition_image_with_no_loader(
|
||||||
|
self, mock_get_part_uuid, mkdir_mock,
|
||||||
|
environ_mock, mock_md_get_raid_devices,
|
||||||
|
mock_is_md_device, mock_exists,
|
||||||
|
mock_copytree, mock_efi_setup,
|
||||||
|
mock_oslistdir, mock_execute,
|
||||||
|
mock_dispatch):
|
||||||
|
mock_exists.side_effect = [True, False, False, True, True, True, True]
|
||||||
|
mock_efi_setup.side_effect = Exception('meow')
|
||||||
|
mock_oslistdir.return_value = ['file1']
|
||||||
|
mock_get_part_uuid.side_effect = [self.fake_root_part,
|
||||||
|
self.fake_efi_system_part]
|
||||||
|
environ_mock.get.return_value = '/sbin'
|
||||||
|
mock_is_md_device.return_value = False
|
||||||
|
mock_md_get_raid_devices.return_value = {}
|
||||||
|
|
||||||
|
image._install_grub2(
|
||||||
|
self.fake_dev, root_uuid=self.fake_root_uuid,
|
||||||
|
efi_system_part_uuid=self.fake_efi_system_part_uuid,
|
||||||
|
target_boot_mode='uefi')
|
||||||
|
|
||||||
|
expected = [mock.call('mount', '/dev/fake2', self.fake_dir),
|
||||||
|
mock.call('mount', '-o', 'bind', '/dev',
|
||||||
|
self.fake_dir + '/dev'),
|
||||||
|
mock.call('mount', '-o', 'bind', '/proc',
|
||||||
|
self.fake_dir + '/proc'),
|
||||||
|
mock.call('mount', '-o', 'bind', '/run',
|
||||||
|
self.fake_dir + '/run'),
|
||||||
|
mock.call('mount', '-t', 'sysfs', 'none',
|
||||||
|
self.fake_dir + '/sys'),
|
||||||
|
mock.call('mount', '-t', 'vfat', '/dev/fake1',
|
||||||
|
self.fake_dir + '/boot/efi'),
|
||||||
|
mock.call('umount', self.fake_dir + '/boot/efi'),
|
||||||
|
|
||||||
|
mock.call(('chroot %s /bin/sh -c "mount -a -t vfat"' %
|
||||||
|
(self.fake_dir)), shell=True,
|
||||||
|
env_variables={
|
||||||
|
'PATH': '/sbin:/bin:/usr/sbin:/sbin'}),
|
||||||
|
mock.call('mount', self.fake_efi_system_part,
|
||||||
|
self.fake_dir + '/boot/efi'),
|
||||||
|
mock.call(('chroot %s /bin/sh -c "grub2-install"' %
|
||||||
|
self.fake_dir), shell=True,
|
||||||
|
env_variables={
|
||||||
|
'PATH': '/sbin:/bin:/usr/sbin:/sbin'}),
|
||||||
|
mock.call(('chroot %s /bin/sh -c '
|
||||||
|
'"grub2-install --removable"' %
|
||||||
|
self.fake_dir), shell=True,
|
||||||
|
env_variables={
|
||||||
|
'PATH': '/sbin:/bin:/usr/sbin:/sbin'}),
|
||||||
|
mock.call(
|
||||||
|
'umount', self.fake_dir + '/boot/efi',
|
||||||
|
attempts=3, delay_on_retry=True),
|
||||||
|
mock.call('mount', self.fake_efi_system_part,
|
||||||
|
'/tmp/fake-dir/boot/efi'),
|
||||||
|
mock.call(('chroot %s /bin/sh -c '
|
||||||
|
'"grub2-mkconfig -o '
|
||||||
|
'/boot/grub2/grub.cfg"' % self.fake_dir),
|
||||||
|
shell=True,
|
||||||
|
env_variables={
|
||||||
|
'PATH': '/sbin:/bin:/usr/sbin:/sbin',
|
||||||
|
'GRUB_DISABLE_OS_PROBER': 'true',
|
||||||
|
'GRUB_SAVEDEFAULT': 'true'},
|
||||||
|
use_standard_locale=True),
|
||||||
|
mock.call('umount', self.fake_dir + '/boot/efi',
|
||||||
|
attempts=3, delay_on_retry=True),
|
||||||
|
mock.call(('chroot %s /bin/sh -c "umount -a -t vfat"' %
|
||||||
|
(self.fake_dir)), shell=True,
|
||||||
|
env_variables={
|
||||||
|
'PATH': '/sbin:/bin:/usr/sbin:/sbin'}),
|
||||||
|
mock.call('umount', self.fake_dir + '/dev',
|
||||||
|
attempts=3, delay_on_retry=True),
|
||||||
|
mock.call('umount', self.fake_dir + '/proc',
|
||||||
|
attempts=3, delay_on_retry=True),
|
||||||
|
mock.call('umount', self.fake_dir + '/run',
|
||||||
|
attempts=3, delay_on_retry=True),
|
||||||
|
mock.call('umount', self.fake_dir + '/sys',
|
||||||
|
attempts=3, delay_on_retry=True),
|
||||||
|
mock.call('umount', self.fake_dir, attempts=3,
|
||||||
|
delay_on_retry=True)]
|
||||||
|
|
||||||
|
mkdir_mock.assert_not_called()
|
||||||
|
mock_execute.assert_has_calls(expected)
|
||||||
|
self.assertEqual(2, mock_copytree.call_count)
|
||||||
|
self.assertTrue(mock_efi_setup.called)
|
||||||
|
mock_get_part_uuid.assert_any_call(self.fake_dev,
|
||||||
|
uuid=self.fake_root_uuid)
|
||||||
|
mock_get_part_uuid.assert_any_call(self.fake_dev,
|
||||||
|
uuid=self.fake_efi_system_part_uuid)
|
||||||
|
self.assertFalse(mock_dispatch.called)
|
||||||
|
|
||||||
@mock.patch.object(image, '_is_bootloader_loaded', lambda *_: False)
|
@mock.patch.object(image, '_is_bootloader_loaded', lambda *_: False)
|
||||||
@mock.patch.object(hardware, 'is_md_device', autospec=True)
|
@mock.patch.object(hardware, 'is_md_device', autospec=True)
|
||||||
@mock.patch.object(hardware, 'md_get_raid_devices', autospec=True)
|
@mock.patch.object(hardware, 'md_get_raid_devices', autospec=True)
|
||||||
|
@ -737,6 +1209,7 @@ efibootmgr: ** Warning ** : Boot0005 has same label ironic1\n
|
||||||
self.fake_dir + '/run'),
|
self.fake_dir + '/run'),
|
||||||
mock.call('mount', '-t', 'sysfs', 'none',
|
mock.call('mount', '-t', 'sysfs', 'none',
|
||||||
self.fake_dir + '/sys'),
|
self.fake_dir + '/sys'),
|
||||||
|
mock.call('mount', '/dev/fake2', self.fake_dir),
|
||||||
mock.call(('chroot %s /bin/sh -c "mount -a -t vfat"' %
|
mock.call(('chroot %s /bin/sh -c "mount -a -t vfat"' %
|
||||||
(self.fake_dir)), shell=True,
|
(self.fake_dir)), shell=True,
|
||||||
env_variables={
|
env_variables={
|
||||||
|
@ -1084,7 +1557,10 @@ efibootmgr: ** Warning ** : Boot0005 has same label ironic1\n
|
||||||
'/boot/grub/grub.cfg"' % self.fake_dir),
|
'/boot/grub/grub.cfg"' % self.fake_dir),
|
||||||
shell=True,
|
shell=True,
|
||||||
env_variables={
|
env_variables={
|
||||||
'PATH': '/sbin:/bin:/usr/sbin:/sbin'}),
|
'PATH': '/sbin:/bin:/usr/sbin:/sbin',
|
||||||
|
'GRUB_DISABLE_OS_PROBER': 'true',
|
||||||
|
'GRUB_SAVEDEFAULT': 'true'},
|
||||||
|
use_standard_locale=True),
|
||||||
mock.call('umount', self.fake_dir + '/boot/efi',
|
mock.call('umount', self.fake_dir + '/boot/efi',
|
||||||
attempts=3, delay_on_retry=True),
|
attempts=3, delay_on_retry=True),
|
||||||
mock.call(('chroot %s /bin/sh -c "umount -a -t vfat"' %
|
mock.call(('chroot %s /bin/sh -c "umount -a -t vfat"' %
|
||||||
|
@ -1176,7 +1652,10 @@ efibootmgr: ** Warning ** : Boot0005 has same label ironic1\n
|
||||||
'/boot/grub/grub.cfg"' % self.fake_dir),
|
'/boot/grub/grub.cfg"' % self.fake_dir),
|
||||||
shell=True,
|
shell=True,
|
||||||
env_variables={
|
env_variables={
|
||||||
'PATH': '/sbin:/bin:/usr/sbin:/sbin'}),
|
'PATH': '/sbin:/bin:/usr/sbin:/sbin',
|
||||||
|
'GRUB_DISABLE_OS_PROBER': 'true',
|
||||||
|
'GRUB_SAVEDEFAULT': 'true'},
|
||||||
|
use_standard_locale=True),
|
||||||
mock.call(('chroot %s /bin/sh -c "umount -a -t vfat"' %
|
mock.call(('chroot %s /bin/sh -c "umount -a -t vfat"' %
|
||||||
(self.fake_dir)), shell=True,
|
(self.fake_dir)), shell=True,
|
||||||
env_variables={
|
env_variables={
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
---
|
||||||
|
fixes:
|
||||||
|
- |
|
||||||
|
Fixes the agent's EFI boot handling such that EFI assets from a partition
|
||||||
|
image are preserved and used instead of overridden. This should permit
|
||||||
|
operators to use Secure Boot with partition images IF the assets are
|
||||||
|
already present in the partition image.
|
Loading…
Reference in New Issue