diff options
Diffstat (limited to 'vmdb/plugins/grub_plugin.py')
-rw-r--r-- | vmdb/plugins/grub_plugin.py | 263 |
1 files changed, 0 insertions, 263 deletions
diff --git a/vmdb/plugins/grub_plugin.py b/vmdb/plugins/grub_plugin.py deleted file mode 100644 index 64522ec..0000000 --- a/vmdb/plugins/grub_plugin.py +++ /dev/null @@ -1,263 +0,0 @@ -# Copyright 2017 Lars Wirzenius -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. -# -# =*= License: GPL-3+ =*= - - -# Installing GRUB onto a disk image is a bit of a black art. I haven't -# found any good documentation for it. This plugin is written based on -# de-ciphering the build_openstack_image script. Here is an explanation -# of what I _THINK_ is happening. -# -# The crucial command is grub-install. It needs a ton of options to -# work correctly: see below in the code for the list, and the manpage -# for an explanation of what each of them means. We will be running -# grub-install in a chroot so that we use the version in the Debian -# version we're installing, rather than the host system, which might -# be any Debian version. -# -# To run grub-install in a chroot, we need to set up the chroot in -# various ways. Firstly, we need to tell grub-install which device -# file the image has. We can't just give it the image file itself, -# since it isn't inside the chroot, so instead we arrange to have a -# loop block device that covers the whole image file, and we bind -# mount /dev into the chroot so the device is available. -# -# grub-install seems to also require /proc and /sys so we bind mount -# those into the chroot as well. -# -# We install the UEFI version of GRUB, and for that we additionally -# bind mount the EFI partition in the image. Oh yeah, you MUST have -# one. -# -# We also make sure the right GRUB package is installed in the chroot, -# before we run grub-install. -# -# Further, there's some configuration tweaking we need to do. See the -# code. Don't ask me why they're necessary. -# -# For cleanliness, we also undo any bind mounts into the chroot. Don't -# want to leave them in case they cause trouble. -# -# Note that this is currently rather strongly assuming that UEFI and -# the amd64 (a.k.a. x86_64) architecture are being used. These should -# probably not be hardcoded. Patch welcome. - -# To use this plugin: write steps to create a root filesystem, and an -# VFAT filesystem to be mounted as /boot/efi. Install Debian onto the -# root filesystem. Then install grub with a step like this: -# -# - grub: uefi -# tag: root-part -# efi: efi-part -# -# Here: "tag" is the tag for the root filesystem (and corresponding -# partition), and efi is tag for the EFI partition. -# -# The grub step will take of the rest. - - -import logging -import os -import re - -import cliapp - -import vmdb - - -class GrubPlugin(cliapp.Plugin): - - def enable(self): - self.app.step_runners.add(GrubStepRunner()) - - -class GrubStepRunner(vmdb.StepRunnerInterface): - - def get_required_keys(self): - return ['grub', 'tag'] - - def run(self, step, settings, state): - state.grub_mounts = [] - flavor = step['grub'] - if flavor == 'uefi': - self.install_uefi(step, settings, state) - elif flavor == 'bios': - self.install_bios(step, settings, state) - else: - raise Exception('Unknown GRUB flavor {}'.format(flavor)) - - def install_uefi(self, step, settings, state): - if not 'efi' in step and 'efi-part' not in step: - raise Exception('"efi" or "efi-part" required in UEFI GRUB installtion') - - vmdb.progress('Installing GRUB for UEFI') - grub_package = 'grub-efi-amd64' - grub_target = 'x86_64-efi' - self.install_grub(step, settings, state, grub_package, grub_target) - - def install_bios(self, step, settings, state): - vmdb.progress('Installing GRUB for BIOS') - grub_package = 'grub-pc' - grub_target = 'i386-pc' - self.install_grub(step, settings, state, grub_package, grub_target) - - def install_grub(self, step, settings, state, grub_package, grub_target): - console = step.get('console', None) - - tag = step.get('tag') - if tag is None: - tag = step['root-fs'] - root_dev = state.tags.get_dev(tag) - chroot = state.tags.get_mount_point(tag) - - image_dev = step.get('image-dev') - if image_dev is None: - image_dev = self.get_image_loop_device(root_dev) - - if 'efi' in step: - efi = step['efi'] - efi_dev = state.tags.get_dev(efi) - elif 'efi-part' in step: - efi = step['efi-part'] - efi_dev = state.tags.get_dev(efi) - else: - efi_dev = None - - self.bind_mount_many(chroot, ['/dev', '/proc', '/sys'], state) - if efi_dev: - self.mount(chroot, efi_dev, '/boot/efi', state) - self.install_package(chroot, grub_package) - - kernel_params = [ - 'biosdevname=0', - 'net.ifnames=0', - 'consoleblank=0', - 'systemd.show_status=true', - 'rw', - 'quiet', - 'systemd.show_status=false', - ] - if console == 'serial': - kernel_params.extend([ - 'quiet', - 'loglevel=3', - 'rd.systemd.show_status=false', - 'systemd.show_status=false', - 'console=tty0', - 'console=ttyS0,115200n8', - ]) - - self.set_grub_cmdline_config(chroot, kernel_params) - self.add_grub_crypto_disk(chroot) - if console == 'serial': - self.add_grub_serial_console(chroot) - - vmdb.runcmd_chroot(chroot, ['grub-mkconfig', '-o', '/boot/grub/grub.cfg']) - vmdb.runcmd_chroot( - chroot, [ - 'grub-install', - '--target=' + grub_target, - '--no-nvram', - '--force-extra-removable', - '--no-floppy', - '--modules=part_msdos part_gpt', - '--grub-mkdevicemap=/boot/grub/device.map', - image_dev, - ] - ) - -# self.unmount(state) - - def teardown(self, step, settings, state): - self.unmount(state) - - def unmount(self, state): - mounts = getattr(state, 'grub_mounts', []) - mounts.reverse() - while mounts: - mount_point = mounts.pop() - try: - vmdb.unmount(mount_point) - except vmdb.NotMounted as e: - logging.warning(str(e)) - - def get_image_loop_device(self, partition_device): - # We get /dev/mappers/loopXpY and return /dev/loopX - # assert partition_device.startswith('/dev/mapper/loop') - - m = re.match(r'^/dev/mapper/(?P<loop>.*)p\d+$', partition_device) - if m is None: - raise Exception('Do not understand partition device name {}'.format( - partition_device)) - assert m is not None - - loop = m.group('loop') - return '/dev/{}'.format(loop) - - def bind_mount_many(self, chroot, paths, state): - for path in paths: - self.mount(chroot, path, path, state, mount_opts=['--bind']) - - def mount(self, chroot, path, mount_point, state, mount_opts=None): - chroot_path = self.chroot_path(chroot, mount_point) - if not os.path.exists(chroot_path): - os.makedirs(chroot_path) - - if mount_opts is None: - mount_opts = [] - - vmdb.runcmd(['mount'] + mount_opts + [path, chroot_path]) - state.grub_mounts.append(chroot_path) - - def chroot_path(self, chroot, path): - return os.path.normpath(os.path.join(chroot, '.' + path)) - - def install_package(self, chroot, package): - env = os.environ.copy() - env['DEBIAN_FRONTEND'] = 'noninteractive' - vmdb.runcmd_chroot( - chroot, - ['apt-get', '-y', '--no-show-progress', 'install', package], - env=env) - - def set_grub_cmdline_config(self, chroot, kernel_params): - param_string = ' '.join(kernel_params) - - filename = self.chroot_path(chroot, '/etc/default/grub') - - with open(filename) as f: - text = f.read() - - lines = text.splitlines() - lines = [line for line in lines - if not line.startswith('GRUB_CMDLINE_LINUX_DEFAULT')] - lines.append('GRUB_CMDLINE_LINUX_DEFAULT="{}"'.format(param_string)) - - with open(filename, 'w') as f: - f.write('\n'.join(lines) + '\n') - - def add_grub_serial_console(self, chroot): - filename = self.chroot_path(chroot, '/etc/default/grub') - - with open(filename, 'a') as f: - f.write('GRUB_TERMINAL=serial\n') - f.write('GRUB_SERIAL_COMMAND="serial --speed=115200 --unit=0 ' - '--word=8 --parity=no --stop=1"\n') - - def add_grub_crypto_disk(self, chroot): - filename = self.chroot_path(chroot, '/etc/default/grub') - with open(filename, 'a') as f: - f.write('GRUB_ENABLE_CRYPTODISK=y\n') |