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 _install_grub2(device, root_uuid, efi_system_part_uuid=None, prep_boot_part_uuid=None): """Install GRUB2 bootloader on a given device.""" LOG.debug("Installing GRUB2 bootloader on device %s", device) efi_partition = None efi_partition_mount_point = None efi_mounted = False # NOTE(TheJulia): Seems we need to get this before ever possibly # restart the device in the case of multi-device RAID as pyudev # doesn't exactly like the partition disappearing. root_partition = _get_partition(device, uuid=root_uuid) # If the root device is an md device (or partition), restart the device # (to help grub finding it) and identify the underlying holder disks # to install grub. if hardware.is_md_device(device): hardware.md_restart(device) # If an md device, we need to rescan the devices anyway to pickup # the md device partition. _rescan_device(device) elif (_is_bootloader_loaded(device) and not (efi_system_part_uuid or prep_boot_part_uuid)): # We always need to put the bootloader in place with software raid # so it is okay to elif into the skip doing a bootloader step. LOG.info("Skipping installation of bootloader on device %s " "as it is already marked bootable.", device) return try: # Mount the partition and binds path = tempfile.mkdtemp() if efi_system_part_uuid: efi_partition = _get_partition(device, uuid=efi_system_part_uuid) efi_partition_mount_point = os.path.join(path, "boot/efi") # For power we want to install grub directly onto the PreP partition if prep_boot_part_uuid: device = _get_partition(device, uuid=prep_boot_part_uuid) # If the root device is an md device (or partition), # identify the underlying holder disks to install grub. if hardware.is_md_device(device): disks = hardware.get_holder_disks(device) else: disks = [device] 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') if efi_partition: if not os.path.exists(efi_partition_mount_point): os.makedirs(efi_partition_mount_point) utils.execute('mount', efi_partition, efi_partition_mount_point) efi_mounted = True binary_name = "grub" if os.path.exists(os.path.join(path, 'usr/sbin/grub2-install')): binary_name = "grub2" # 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' % path_variable # Install grub. Normally, grub goes to one disk only. In case of # md devices, grub goes to all underlying holder (RAID-1) disks. LOG.info("GRUB2 will be installed on disks %s", disks) for grub_disk in disks: LOG.debug("Installing GRUB2 on disk %s", grub_disk) utils.execute('chroot %(path)s /bin/sh -c ' '"%(bin)s-install %(dev)s"' % {'path': path, 'bin': binary_name, 'dev': grub_disk}, shell=True, env_variables={'PATH': path_variable}) LOG.debug("GRUB2 successfully installed on device %s", grub_disk) # Also run grub-install with --removable, this installs grub to the # EFI fallback path. Useful if the NVRAM wasn't written correctly, # was reset or if testing with virt as libvirt resets the NVRAM # on instance start. # This operation is essentially a copy operation. Use of the # --removable flag, per the grub-install source code changes # the default file to be copied, destination file name, and # prevents NVRAM from being updated. # We only run grub2_install for uefi if we can't verify the uefi bits if efi_partition: utils.execute('chroot %(path)s /bin/sh -c ' '"%(bin)s-install %(dev)s --removable"' % {'path': path, 'bin': binary_name, 'dev': device}, shell=True, env_variables={'PATH': path_variable}) # 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)) # 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) except processutils.ProcessExecutionError as e: error_msg = ('Installing GRUB2 boot loader to device %(dev)s ' 'failed with %(err)s.' % {'dev': device, 'err': e}) LOG.error(error_msg) raise errors.CommandExecutionError(error_msg) finally: umount_warn_msg = "Unable to umount %(path)s. Error: %(error)s" # Umount binds and partition umount_binds_fail = False # If umount fails for efi partition, then we cannot be sure that all # the changes were written back to the filesystem. try: if efi_mounted: utils.execute('umount', efi_partition_mount_point, attempts=3, delay_on_retry=True) except processutils.ProcessExecutionError as e: error_msg = ('Umounting efi system partition failed. ' 'Attempted 3 times. Error: %s' % e) LOG.error(error_msg) raise errors.CommandExecutionError(error_msg) for fs in BIND_MOUNTS: try: utils.execute('umount', path + fs, attempts=3, delay_on_retry=True) except processutils.ProcessExecutionError as e: umount_binds_fail = True LOG.warning(umount_warn_msg, {'path': path + fs, 'error': e}) try: utils.execute('umount', path + '/sys', attempts=3, delay_on_retry=True) except processutils.ProcessExecutionError as e: umount_binds_fail = True LOG.warning(umount_warn_msg, {'path': path + '/sys', 'error': e}) # If umounting the binds succeed then we can try to delete it if not umount_binds_fail: try: utils.execute('umount', path, attempts=3, delay_on_retry=True) except processutils.ProcessExecutionError as e: LOG.warning(umount_warn_msg, {'path': path, 'error': e}) else: # After everything is umounted we can then remove the # temporary directory shutil.rmtree(path)