diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 2431ece..643b311 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,6 +6,7 @@ Changelog ---------- Anders Ingemann * Disable persistent network interface names for >=stretch + * grub defaults and linux boot options are now easier to configure 2016-06-02 ---------- diff --git a/bootstrapvz/common/task_groups.py b/bootstrapvz/common/task_groups.py index d47e197..a92d748 100644 --- a/bootstrapvz/common/task_groups.py +++ b/bootstrapvz/common/task_groups.py @@ -152,14 +152,22 @@ def get_locale_group(manifest): def get_bootloader_group(manifest): from bootstrapvz.common.releases import jessie + from bootstrapvz.common.releases import stretch group = [] if manifest.system['bootloader'] == 'grub': group.extend([grub.AddGrubPackage, - grub.ConfigureGrub]) + grub.InitGrubConfig, + grub.SetGrubTerminalToConsole, + grub.SetGrubConsolOutputDeviceToSerial, + grub.RemoveGrubTimeout, + grub.DisableGrubRecovery, + grub.WriteGrubConfig]) if manifest.release < jessie: group.append(grub.InstallGrub_1_99) else: group.append(grub.InstallGrub_2) + if manifest.release >= stretch: + group.append(grub.DisablePNIN) if manifest.system['bootloader'] == 'extlinux': group.append(extlinux.AddExtlinuxPackage) if manifest.release < jessie: diff --git a/bootstrapvz/common/tasks/grub.py b/bootstrapvz/common/tasks/grub.py index 95c00ad..7cff507 100644 --- a/bootstrapvz/common/tasks/grub.py +++ b/bootstrapvz/common/tasks/grub.py @@ -1,4 +1,5 @@ from bootstrapvz.base import Task +from ..exceptions import TaskError from .. import phases from ..tools import log_check_call import filesystem @@ -16,32 +17,273 @@ class AddGrubPackage(Task): info.packages.add('grub-pc') -class ConfigureGrub(Task): - description = 'Configuring grub' +class InitGrubConfig(Task): + description = 'Initializing grub standard configuration' + phase = phases.preparation + + @classmethod + def run(cls, info): + # The default values and documentation below was fetched from + # https://www.gnu.org/software/grub/manual/html_node/Simple-configuration.html + # Some explanations have been shortened + info.grub_config = { + # The default menu entry. This may be a number, in which case it identifies the Nth entry + # in the generated menu counted from zero, or the title of a menu entry, + # or the special string `saved'. Using the title may be useful if you want to set a menu entry + # as the default even though there may be a variable number of entries before it. + # If you set this to `saved', then the default menu entry will be that + # saved by `GRUB_SAVEDEFAULT', grub-set-default, or grub-reboot. + # The default is `0'. + 'GRUB_DEFAULT': 0, + + # If this option is set to `true', then, when an entry is selected, + # save it as a new default entry for use by future runs of GRUB. + # This is only useful if `GRUB_DEFAULT=saved'; + # it is a separate option because `GRUB_DEFAULT=saved' is useful without this option, + # in conjunction with grub-set-default or grub-reboot. Unset by default. + 'GRUB_SAVEDEFAULT': None, + + # Boot the default entry this many seconds after the menu is displayed, unless a key is pressed. + # The default is `5'. Set to `0' to boot immediately without displaying the menu, + # or to `-1' to wait indefinitely. + 'GRUB_TIMEOUT': 5, + + # Wait this many seconds for a key to be pressed before displaying the menu. + # If no key is pressed during that time, display the menu for the number of seconds specified + # in GRUB_TIMEOUT before booting the default entry. + # We expect that most people who use GRUB_HIDDEN_TIMEOUT will want to have GRUB_TIMEOUT set to `0' + # so that the menu is not displayed at all unless a key is pressed. Unset by default. + 'GRUB_HIDDEN_TIMEOUT': None, + + # In conjunction with `GRUB_HIDDEN_TIMEOUT', set this to `true' to suppress the verbose countdown + # while waiting for a key to be pressed before displaying the menu. Unset by default. + 'GRUB_HIDDEN_TIMEOUT_QUIET': None, + + # Variants of the corresponding variables without the `_BUTTON' suffix, + # used to support vendor-specific power buttons. See Vendor power-on keys. + 'GRUB_DEFAULT_BUTTON': None, + 'GRUB_TIMEOUT_BUTTON': None, + 'GRUB_HIDDEN_TIMEOUT_BUTTON': None, + 'GRUB_BUTTON_CMOS_ADDRESS': None, + + # Set by distributors of GRUB to their identifying name. + # This is used to generate more informative menu entry titles. + 'GRUB_DISTRIBUTOR': None, + + # Select the terminal input device. You may select multiple devices here, separated by spaces. + # Valid terminal input names depend on the platform, but may include + # `console' (PC BIOS and EFI consoles), + # `serial' (serial terminal), + # `ofconsole' (Open Firmware console), + # `at_keyboard' (PC AT keyboard, mainly useful with Coreboot), + # or `usb_keyboard' (USB keyboard using the HID Boot Protocol, + # for cases where the firmware does not handle this). + # The default is to use the platform's native terminal input. + 'GRUB_TERMINAL_INPUT': None, + + # Select the terminal output device. You may select multiple devices here, separated by spaces. + # Valid terminal output names depend on the platform, but may include + # `console' (PC BIOS and EFI consoles), + # `serial' (serial terminal), + # `gfxterm' (graphics-mode output), + # `ofconsole' (Open Firmware console), + # or `vga_text' (VGA text output, mainly useful with Coreboot). + # The default is to use the platform's native terminal output. + 'GRUB_TERMINAL_OUTPUT': None, + + # If this option is set, it overrides both `GRUB_TERMINAL_INPUT' and `GRUB_TERMINAL_OUTPUT' + # to the same value. + 'GRUB_TERMINAL': None, + + # A command to configure the serial port when using the serial console. + # See serial. Defaults to `serial'. + 'GRUB_SERIAL_COMMAND': 'serial', + + # Command-line arguments to add to menu entries for the Linux kernel. + 'GRUB_CMDLINE_LINUX': [], + + # Unless `GRUB_DISABLE_RECOVERY' is set to `true', + # two menu entries will be generated for each Linux kernel: + # one default entry and one entry for recovery mode. + # This option lists command-line arguments to add only to the default menu entry, + # after those listed in `GRUB_CMDLINE_LINUX'. + 'GRUB_CMDLINE_LINUX_DEFAULT': [], + + # As `GRUB_CMDLINE_LINUX' and `GRUB_CMDLINE_LINUX_DEFAULT', but for NetBSD. + 'GRUB_CMDLINE_NETBSD': [], + 'GRUB_CMDLINE_NETBSD_DEFAULT': [], + + # As `GRUB_CMDLINE_LINUX', but for GNU Mach. + 'GRUB_CMDLINE_GNUMACH': [], + + # The values of these options are appended to the values of + # `GRUB_CMDLINE_LINUX' and `GRUB_CMDLINE_LINUX_DEFAULT' + # for Linux and Xen menu entries. + 'GRUB_CMDLINE_XEN': [], + 'GRUB_CMDLINE_XEN_DEFAULT': [], + + # The values of these options replace the values of + # `GRUB_CMDLINE_LINUX' and `GRUB_CMDLINE_LINUX_DEFAULT' for Linux and Xen menu entries. + 'GRUB_CMDLINE_LINUX_XEN_REPLACE': [], + 'GRUB_CMDLINE_LINUX_XEN_REPLACE_DEFAULT': [], + + # Normally, grub-mkconfig will generate menu entries that use + # universally-unique identifiers (UUIDs) to identify the root filesystem to the Linux kernel, + # using a `root=UUID=...' kernel parameter. This is usually more reliable, + # but in some cases it may not be appropriate. + # To disable the use of UUIDs, set this option to `true'. + 'GRUB_DISABLE_LINUX_UUID': None, + + # If this option is set to `true', disable the generation of recovery mode menu entries. + 'GRUB_DISABLE_RECOVERY': None, + + # If graphical video support is required, either because the `gfxterm' graphical terminal is + # in use or because `GRUB_GFXPAYLOAD_LINUX' is set, + # then grub-mkconfig will normally load all available GRUB video drivers and + # use the one most appropriate for your hardware. + # If you need to override this for some reason, then you can set this option. + # After grub-install has been run, the available video drivers are listed in /boot/grub/video.lst. + 'GRUB_VIDEO_BACKEND': None, + + # Set the resolution used on the `gfxterm' graphical terminal. + # Note that you can only use modes which your graphics card supports + # via VESA BIOS Extensions (VBE), so for example native LCD panel resolutions + # may not be available. + # The default is `auto', which tries to select a preferred resolution. See gfxmode. + 'GRUB_GFXMODE': 'auto', + + # Set a background image for use with the `gfxterm' graphical terminal. + # The value of this option must be a file readable by GRUB at boot time, + # and it must end with .png, .tga, .jpg, or .jpeg. + # The image will be scaled if necessary to fit the screen. + 'GRUB_BACKGROUND': None, + + # Set a theme for use with the `gfxterm' graphical terminal. + 'GRUB_THEME': None, + + # Set to `text' to force the Linux kernel to boot in normal text mode, + # `keep' to preserve the graphics mode set using `GRUB_GFXMODE', + # `widthxheight'[`xdepth'] to set a particular graphics mode, + # or a sequence of these separated by commas or semicolons to try several modes in sequence. + # See gfxpayload. + # Depending on your kernel, your distribution, your graphics card, and the phase of the moon, + # note that using this option may cause GNU/Linux to suffer from various display problems, + # particularly during the early part of the boot sequence. + # If you have problems, set this option to `text' and GRUB will + # tell Linux to boot in normal text mode. + 'GRUB_GFXPAYLOAD_LINUX': None, + + # Normally, grub-mkconfig will try to use the external os-prober program, if installed, + # to discover other operating systems installed on the same system and generate appropriate + # menu entries for them. Set this option to `true' to disable this. + 'GRUB_DISABLE_OS_PROBER': None, + + # Play a tune on the speaker when GRUB starts. + # This is particularly useful for users unable to see the screen. + # The value of this option is passed directly to play. + 'GRUB_INIT_TUNE': None, + + # If this option is set, GRUB will issue a badram command to filter out specified regions of RAM. + 'GRUB_BADRAM': None, + + # This option may be set to a list of GRUB module names separated by spaces. + # Each module will be loaded as early as possible, at the start of grub.cfg. + 'GRUB_PRELOAD_MODULES': [], + } + + +class WriteGrubConfig(Task): + description = 'Writing grub defaults configuration' phase = phases.system_modification @classmethod def run(cls, info): - from bootstrapvz.common.releases import stretch - from bootstrapvz.common.tools import sed_i - grub_def = os.path.join(info.root, 'etc/default/grub') - if info.manifest.release >= stretch: - # Disable "Predictable Network Interface Names" - # See issue #245 for more details - sed_i(grub_def, '^GRUB_CMDLINE_LINUX=""', 'GRUB_CMDLINE_LINUX="net.ifnames=0 biosdevname=0"') - sed_i(grub_def, '^#GRUB_TERMINAL=console', 'GRUB_TERMINAL=console') - sed_i(grub_def, '^GRUB_CMDLINE_LINUX_DEFAULT="quiet"', - 'GRUB_CMDLINE_LINUX_DEFAULT="console=ttyS0"') - sed_i(grub_def, '^GRUB_TIMEOUT=[0-9]+', 'GRUB_TIMEOUT=0\n' - 'GRUB_HIDDEN_TIMEOUT=0\n' - 'GRUB_HIDDEN_TIMEOUT_QUIET=true') - sed_i(grub_def, '^#GRUB_DISABLE_RECOVERY="true"', 'GRUB_DISABLE_RECOVERY="true"') + grub_config_contents = '' + for key, value in info.grub_config.items(): + if value is None: + continue + if isinstance(value, str): + grub_config_contents += '{}="{}"\n'.format(key, value) + continue + if isinstance(value, int): + grub_config_contents += '{}={}\n'.format(key, value) + continue + if isinstance(value, bool): + grub_config_contents += '{}="{}"\n'.format(key, str(value).lower()) + continue + if isinstance(value, list): + if len(value) == 0: + continue + args_list = ' '.join(map(str, value)) + grub_config_contents += '{}="{}"\n'.format(key, args_list) + continue + raise TaskError('Don\'t know how to handle type {}, ' + 'when creating grub config'.format(type(value))) + grub_defaults = os.path.join(info.root, 'etc/default/grub') + with open(grub_defaults, 'w') as grub_defaults_handle: + grub_defaults_handle.write(grub_config_contents) + + +class DisablePNIN(Task): + description = 'Disabling Predictable Network Interfaces' + phase = phases.system_modification + successors = [WriteGrubConfig] + + @classmethod + def run(cls, info): + # See issue #245 for more details + info.grub_config['GRUB_CMDLINE_LINUX'].append('net.ifnames=0') + info.grub_config['GRUB_CMDLINE_LINUX'].append('biosdevname=0') + + +class SetGrubTerminalToConsole(Task): + description = 'Setting the grub terminal to `console\'' + phase = phases.system_modification + successors = [WriteGrubConfig] + + @classmethod + def run(cls, info): + # See issue #245 for more details + info.grub_config['TERMINAL'] = 'console' + + +class SetGrubConsolOutputDeviceToSerial(Task): + description = 'Setting the grub terminal output device to `ttyS0\'' + phase = phases.system_modification + successors = [WriteGrubConfig] + + @classmethod + def run(cls, info): + # See issue #245 for more details + info.grub_config['GRUB_CMDLINE_LINUX_DEFAULT'].append('console=ttyS0') + + +class RemoveGrubTimeout(Task): + description = 'Setting grub menu timeout to 0' + phase = phases.system_modification + successors = [WriteGrubConfig] + + @classmethod + def run(cls, info): + info.grub_config['GRUB_TIMEOUT'] = 0 + info.grub_config['GRUB_HIDDEN_TIMEOUT'] = 0 + info.grub_config['GRUB_HIDDEN_TIMEOUT_QUIET'] = True + + +class DisableGrubRecovery(Task): + description = 'Disabling the grub recovery menu entry' + phase = phases.system_modification + successors = [WriteGrubConfig] + + @classmethod + def run(cls, info): + info.grub_config['GRUB_DISABLE_RECOVERY'] = True class InstallGrub_1_99(Task): description = 'Installing grub 1.99' phase = phases.system_modification - predecessors = [filesystem.FStab, ConfigureGrub] + predecessors = [filesystem.FStab, WriteGrubConfig] @classmethod def run(cls, info): @@ -83,7 +325,7 @@ class InstallGrub_1_99(Task): class InstallGrub_2(Task): description = 'Installing grub 2' phase = phases.system_modification - predecessors = [filesystem.FStab, ConfigureGrub] + predecessors = [filesystem.FStab, WriteGrubConfig] # Make sure the kernel image is updated after we have installed the bootloader successors = [kernel.UpdateInitramfs] diff --git a/bootstrapvz/plugins/docker_daemon/tasks.py b/bootstrapvz/plugins/docker_daemon/tasks.py index 380e5a2..998f9e5 100644 --- a/bootstrapvz/plugins/docker_daemon/tasks.py +++ b/bootstrapvz/plugins/docker_daemon/tasks.py @@ -4,7 +4,6 @@ from bootstrapvz.common.tasks import grub from bootstrapvz.common.tasks import initd from bootstrapvz.common.tools import log_check_call from bootstrapvz.common.tools import sed_i -from bootstrapvz.providers.gce.tasks import boot as gceboot import os import os.path import shutil @@ -61,15 +60,13 @@ class AddDockerInit(Task): class EnableMemoryCgroup(Task): - description = 'Change grub configuration to enable the memory cgroup' + description = 'Enable the memory cgroup in the grub config' phase = phases.system_modification - successors = [grub.InstallGrub_1_99, grub.InstallGrub_2] - predecessors = [grub.ConfigureGrub, gceboot.ConfigureGrub] + successors = [grub.WriteGrubConfig] @classmethod def run(cls, info): - grub_config = os.path.join(info.root, 'etc/default/grub') - sed_i(grub_config, r'^(GRUB_CMDLINE_LINUX*=".*)"\s*$', r'\1 cgroup_enable=memory"') + info.grub_config['GRUB_CMDLINE_LINUX'].append('cgroup_enable=memory') class PullDockerImages(Task): diff --git a/bootstrapvz/providers/azure/__init__.py b/bootstrapvz/providers/azure/__init__.py index aa06af0..9737cf5 100644 --- a/bootstrapvz/providers/azure/__init__.py +++ b/bootstrapvz/providers/azure/__init__.py @@ -6,6 +6,7 @@ from bootstrapvz.common.tasks import loopback from bootstrapvz.common.tasks import initd from bootstrapvz.common.tasks import ssh from bootstrapvz.common.tasks import apt +from bootstrapvz.common.tasks import grub def validate_manifest(data, validator, error): @@ -29,6 +30,7 @@ def resolve_tasks(taskset, manifest): tasks.boot.ConfigureGrub, tasks.boot.PatchUdev, ]) + taskset.discard(grub.SetGrubConsolOutputDeviceToSerial) def resolve_rollback_tasks(taskset, manifest, completed, counter_task): diff --git a/bootstrapvz/providers/azure/tasks/boot.py b/bootstrapvz/providers/azure/tasks/boot.py index 9d7e6a8..1b0b3c2 100644 --- a/bootstrapvz/providers/azure/tasks/boot.py +++ b/bootstrapvz/providers/azure/tasks/boot.py @@ -23,14 +23,13 @@ class PatchUdev(Task): class ConfigureGrub(Task): description = 'Change grub configuration to allow for ttyS0 output' phase = phases.system_modification - predecessors = [grub.ConfigureGrub] - successors = [grub.InstallGrub_1_99, grub.InstallGrub_2] + successors = [grub.WriteGrubConfig] @classmethod def run(cls, info): - from bootstrapvz.common.tools import sed_i - grub_config = os.path.join(info.root, 'etc/default/grub') - sed_i(grub_config, r'^(GRUB_CMDLINE_LINUX_DEFAULT=.*)', r'GRUB_CMDLINE_LINUX_DEFAULT=""') - sed_i(grub_config, r'^(GRUB_CMDLINE_LINUX*=".*)"\s*$', r'\1console=tty0 console=ttyS0,115200n8 earlyprintk=ttyS0,115200 rootdelay=300"') - sed_i(grub_config, r'^(GRUB_HIDDEN_TIMEOUT=).*', r'#GRUB_HIDDEN_TIMEOUT=true') - sed_i(grub_config, r'^.*(GRUB_TIMEOUT=).*$', r'GRUB_TIMEOUT=5') + info.grub_config['GRUB_CMDLINE_LINUX'].extend([ + 'console=tty0', + 'console=ttyS0,115200n8', + 'earlyprintk=ttyS0,115200', + 'rootdelay=300', + ]) diff --git a/bootstrapvz/providers/ec2/__init__.py b/bootstrapvz/providers/ec2/__init__.py index 67ce08f..f003d5f 100644 --- a/bootstrapvz/providers/ec2/__init__.py +++ b/bootstrapvz/providers/ec2/__init__.py @@ -93,7 +93,16 @@ def resolve_tasks(taskset, manifest): if manifest.system['bootloader'] == 'pvgrub': taskset.add(grub.AddGrubPackage) - taskset.add(tasks.boot.ConfigurePVGrub) + taskset.update([grub.AddGrubPackage, + grub.InitGrubConfig, + grub.SetGrubTerminalToConsole, + grub.RemoveGrubTimeout, + grub.DisableGrubRecovery, + tasks.boot.CreatePVGrubCustomRule, + tasks.boot.ConfigurePVGrub, + grub.WriteGrubConfig, + tasks.boot.UpdateGrubConfig, + tasks.boot.LinkGrubConfig]) if manifest.volume['backing'].lower() == 'ebs': taskset.update([tasks.host.GetInstanceMetadata, diff --git a/bootstrapvz/providers/ec2/tasks/boot.py b/bootstrapvz/providers/ec2/tasks/boot.py index 7893b91..ce0485b 100644 --- a/bootstrapvz/providers/ec2/tasks/boot.py +++ b/bootstrapvz/providers/ec2/tasks/boot.py @@ -1,19 +1,29 @@ from bootstrapvz.base import Task from bootstrapvz.common import phases +from bootstrapvz.common.tasks import grub from . import assets import os +from bootstrapvz.common.tools import log_check_call -class ConfigurePVGrub(Task): - description = 'Creating grub config files for PVGrub' +class UpdateGrubConfig(Task): + description = 'Updating the grub config' phase = phases.system_modification + successors = [grub.WriteGrubConfig] + + @classmethod + def run(cls, info): + log_check_call(['chroot', info.root, 'update-grub']) + + +class CreatePVGrubCustomRule(Task): + description = 'Creating special rule for PVGrub' + phase = phases.system_modification + successors = [UpdateGrubConfig] @classmethod def run(cls, info): import stat - rwxr_xr_x = (stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | - stat.S_IRGRP | stat.S_IXGRP | - stat.S_IROTH | stat.S_IXOTH) x_all = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH grubd = os.path.join(info.root, 'etc/grub.d') @@ -24,7 +34,7 @@ class ConfigurePVGrub(Task): script_src = os.path.join(assets, 'grub.d/40_custom') script_dst = os.path.join(info.root, 'etc/grub.d/40_custom') copy(script_src, script_dst) - os.chmod(script_dst, rwxr_xr_x) + os.chmod(script_dst, 0755) from bootstrapvz.base.fs.partitionmaps.none import NoPartitions if not isinstance(info.volume.partition_map, NoPartitions): @@ -39,14 +49,27 @@ class ConfigurePVGrub(Task): from bootstrapvz.common.tools import sed_i sed_i(script_dst, '^GRUB_DEVICE=/dev/xvda$', 'GRUB_DEVICE=/dev/xvda1') - from bootstrapvz.common.tools import sed_i - grub_def = os.path.join(info.root, 'etc/default/grub') - sed_i(grub_def, '^GRUB_TIMEOUT=[0-9]+', 'GRUB_TIMEOUT=0\n' - 'GRUB_HIDDEN_TIMEOUT=true') - sed_i(grub_def, '^#GRUB_TERMINAL=console', 'GRUB_TERMINAL=console') - sed_i(grub_def, '^GRUB_CMDLINE_LINUX_DEFAULT=.*', 'GRUB_CMDLINE_LINUX_DEFAULT="consoleblank=0 console=hvc0 elevator=noop"') - from bootstrapvz.common.tools import log_check_call - log_check_call(['chroot', info.root, 'update-grub']) +class ConfigurePVGrub(Task): + description = 'Configuring PVGrub' + phase = phases.system_modification + successors = [UpdateGrubConfig] + + @classmethod + def run(cls, info): + info.grub_config['GRUB_CMDLINE_LINUX'].extend([ + 'consoleblank=0', + 'console=hvc0', + 'elevator=noop', + ]) + + +class LinkGrubConfig(Task): + description = 'Linking the grub config to /boot/grub/menu.lst' + phase = phases.system_modification + predecessors = [UpdateGrubConfig] + + @classmethod + def run(cls, info): log_check_call(['chroot', info.root, 'ln', '--symbolic', '/boot/grub/grub.cfg', '/boot/grub/menu.lst']) diff --git a/bootstrapvz/providers/gce/__init__.py b/bootstrapvz/providers/gce/__init__.py index 8cc3832..cd7ea0f 100644 --- a/bootstrapvz/providers/gce/__init__.py +++ b/bootstrapvz/providers/gce/__init__.py @@ -12,6 +12,7 @@ from bootstrapvz.common.tasks import loopback from bootstrapvz.common.tasks import initd from bootstrapvz.common.tasks import ssh from bootstrapvz.common.tasks import volume +from bootstrapvz.common.tasks import grub def validate_manifest(data, validator, error): @@ -43,6 +44,7 @@ def resolve_tasks(taskset, manifest): tasks.image.CreateTarball, volume.Delete, ]) + taskset.discard(grub.SetGrubConsolOutputDeviceToSerial) if 'gcs_destination' in manifest.provider: taskset.add(tasks.image.UploadImage) diff --git a/bootstrapvz/providers/gce/tasks/boot.py b/bootstrapvz/providers/gce/tasks/boot.py index 75657c1..217dc6a 100644 --- a/bootstrapvz/providers/gce/tasks/boot.py +++ b/bootstrapvz/providers/gce/tasks/boot.py @@ -1,17 +1,14 @@ from bootstrapvz.base import Task from bootstrapvz.common import phases from bootstrapvz.common.tasks import grub -import os.path class ConfigureGrub(Task): description = 'Change grub configuration to allow for ttyS0 output' phase = phases.system_modification - successors = [grub.InstallGrub_1_99, grub.InstallGrub_2] + successors = [grub.WriteGrubConfig] @classmethod def run(cls, info): - from bootstrapvz.common.tools import sed_i - grub_config = os.path.join(info.root, 'etc/default/grub') - sed_i(grub_config, r'^(GRUB_CMDLINE_LINUX*=".*)"\s*$', r'\1console=ttyS0,38400n8 elevator=noop"') - sed_i(grub_config, r'^.*(GRUB_TIMEOUT=).*$', r'GRUB_TIMEOUT=0') + info.grub_config['GRUB_CMDLINE_LINUX'].append('console=ttyS0,38400n8') + info.grub_config['GRUB_CMDLINE_LINUX'].append('elevator=noop')