patternrubyMinor
Making kexec reboots less painful
Viewed 0 times
kexecpainfulrebootslessmaking
Problem
kexec is a way for a Linux kernel to directly boot another Linux kernel without going through the usual BIOS startup sequence, which can take several minutes on enterprise servers. The big problem with kexec is that Linux distributions don't set it up for you when you install a kernel, and setting it up yourself is manual and error-prone, so not many people actually use it when rebooting their servers.
I wanted to learn some Ruby, (I've worked through this Rails tutorial but I haven't previously written anything substantial in Ruby) and so I wrote a Ruby script to help automate kexec somewhat. It can simply stage the latest installed kernel for kexec, or it has an interactive mode where you can choose a kernel from a list. It does both of these by searching for the GRUB configuration file, parsing it to get the kernel, initrd and kernel command line, and then calling kexec with these arguments.
My big concern here is with the obvious duplicate code in
I also literally wrote this yesterday evening, and I might not have had enough coffee while writing it, so I'm open to suggestions on any other part of the code that might need improvement.
(Note: Because this code is meant to be part of the process of rebooting, I suggest testing in a virtual machine. Run the script with no arguments for a usage statement. I've personally tested it on EL6, EL7, Ubuntu 10.04, 12.04, and Debian wheezy and it should work properly on any Linux distirbution that uses GRUB 1 or GRUB 2.)
```
#!/usr/bin/env ruby
# kexec-reboot - Easily choose a kernel to kexec
require 'optparse'
# Find a mount poin
I wanted to learn some Ruby, (I've worked through this Rails tutorial but I haven't previously written anything substantial in Ruby) and so I wrote a Ruby script to help automate kexec somewhat. It can simply stage the latest installed kernel for kexec, or it has an interactive mode where you can choose a kernel from a list. It does both of these by searching for the GRUB configuration file, parsing it to get the kernel, initrd and kernel command line, and then calling kexec with these arguments.
My big concern here is with the obvious duplicate code in
process_grub_config and the functions it calls, load_kernels_grub and load_kernels_grub2. I know these bits need to be refactored, but I'm not familiar enough with the language to know the best way to go about it. In particular, it's necessary to parse GRUB 1 and GRUB 2 style configuration files differently, and these files can be in different locations depending on Linux distribution.I also literally wrote this yesterday evening, and I might not have had enough coffee while writing it, so I'm open to suggestions on any other part of the code that might need improvement.
(Note: Because this code is meant to be part of the process of rebooting, I suggest testing in a virtual machine. Run the script with no arguments for a usage statement. I've personally tested it on EL6, EL7, Ubuntu 10.04, 12.04, and Debian wheezy and it should work properly on any Linux distirbution that uses GRUB 1 or GRUB 2.)
```
#!/usr/bin/env ruby
# kexec-reboot - Easily choose a kernel to kexec
require 'optparse'
# Find a mount poin
Solution
The task is painful because it really is difficult. It's difficult because
For example, from my Debian squeeze server, here is an excerpt from
… and
Two of the complications are:
To claim completeness, you would need to be able to do the inverse of everything that
Perhaps it would be more advantageous to avoid trying to interpret GRUB's device nomenclature altogether and stay entirely within Linux's device-naming scheme by looking for the
Considering the enormity of the task of writing a complete interpreter for
grub.cfg is written in GRUB’s built-in scripting language, which has a syntax quite similar to that of GNU Bash and other Bourne shell derivatives.For example, from my Debian squeeze server, here is an excerpt from
grub.cfg:### BEGIN /etc/grub.d/10_linux ###
menuentry 'Debian GNU/Linux, with Linux 2.6.32-5-amd64' --class debian --class gnu-linux --class gnu --class os {
set gfxpayload=1024x768
insmod lvm
insmod part_gpt
insmod ext2
set root='(vg-root1)'
search --no-floppy --fs-uuid --set 84cc28cc-e54f-43f2-9e62-182d5e6af329
echo 'Loading Linux 2.6.32-5-amd64 ...'
linux /boot/vmlinuz-2.6.32-5-amd64 root=/dev/mapper/vg-root1 ro console=tty0 console=ttyS1,115200n8r quiet vga=791 text
echo 'Loading initial ramdisk ...'
initrd /boot/initrd.img-2.6.32-5-amd64
}
… and
device.map:(hd0) /dev/disk/by-id/cciss-3600508b100104439535547344832000b
Two of the complications are:
- The configuration opted to use the commands
set root=…andlinuxrather thanroot …andkernel ….
- Due to the use of LVM, you won't be able to easily correlate the GRUB device name
(vg-root1)with the mountpoint by looking indevice.map.
To claim completeness, you would need to be able to do the inverse of everything that
grub-mkconfig_lib is capable of generating. A fully general solution would be even more difficult, as it would involve reimplementing a huge chunk of GRUB itself.Perhaps it would be more advantageous to avoid trying to interpret GRUB's device nomenclature altogether and stay entirely within Linux's device-naming scheme by looking for the
root=… kernel command-line parameter. (The rdev(8) command from util-linux may be of interest here, if no kernel parameters are passed — which is a rare practice these days.)Considering the enormity of the task of writing a complete interpreter for
grub.cfg, I'd be happy if you handled just a limited subset of the configuration language properly. Ignoring #Comments would be a good start.Context
StackExchange Code Review Q#73611, answer score: 6
Revisions (0)
No revisions yet.