Matthew Booth
2010-Apr-14 10:31 UTC
[Libguestfs] [PATCH] Workaround issue where grubby fails when run under libguestfs
When installing kernel-2.6.18-128.el5 in a guest with kernel-xen-2.6.18-164.el5xen already installed, and 'root=LABEL=/' on the kernel command line, grubby fails to install a grub entry for the new kernel. It only fails when run under libguestfs. Installing in the original Xen guest works fine. The underlying cause appears to be related to blkid, although I haven't managed to pin it down exactly. This change works around the issue. If virt-v2v encounters a problem with grub, it will install a new kernel entry itself using augeas. Note that this change introduces a dependency on the new aug_clear libguestfs command. --- lib/Sys/VirtV2V/GuestOS/RedHat.pm | 119 ++++++++++++++++++++++++++++++++++--- 1 files changed, 110 insertions(+), 9 deletions(-) diff --git a/lib/Sys/VirtV2V/GuestOS/RedHat.pm b/lib/Sys/VirtV2V/GuestOS/RedHat.pm index 0e469f5..036f4eb 100644 --- a/lib/Sys/VirtV2V/GuestOS/RedHat.pm +++ b/lib/Sys/VirtV2V/GuestOS/RedHat.pm @@ -1043,10 +1043,17 @@ sub prepare_bootable my $initrd; my $found = 0; eval { + my $prefix; + if ($self->{desc}->{boot}->{grub_fs} eq "/boot") { + $prefix = ''; + } else { + $prefix = '/boot'; + } + foreach my $kernel ($g->aug_match('/files/boot/grub/menu.lst/title/kernel')) { - if($g->aug_get($kernel) eq "/vmlinuz-$version") { + if($g->aug_get($kernel) eq "$prefix/vmlinuz-$version") { # Ensure it's the default $kernel =~ m{/files/boot/grub/menu.lst/title(?:\[(\d+)\])?/kernel} or die($kernel); @@ -1068,24 +1075,118 @@ sub prepare_bootable last; } } - $g->aug_save(); + + # grubby can sometimes fail to correctly update grub.conf when run from + # libguestfs. If it looks like this happened, install a new grub config + # here. + if (!$found) { + # Check that an appropriately named kernel and initrd exist + if ($g->exists("/boot/vmlinuz-$version") && + $g->exists("/boot/initrd-$version.img")) + { + $initrd = "$prefix/initrd-$version.img"; + + my $title; + # No point in dying if /etc/redhat-release can't be read + eval { + ($title) = $g->read_lines('/etc/redhat-release'); + }; + $title ||= 'Linux'; + + # This is how new-kernel-pkg does it + $title =~ s/ release.*//; + $title .= " ($version)"; + + my $default; + eval { + $default = $g->aug_get('/files/boot/grub/menu.lst/default'); + }; + + if (defined($default)) { + $g->aug_defvar('template', + '/files/boot/grub/menu.lst/title['.($default + 1).']'); + } + + # If there's no default, take the first entry with a kernel + else { + my ($match) + $g->aug_match('/files/boot/grub/menu.lst/title/kernel'); + + die("No template kernel found in grub") + unless(defined($match)); + + $match =~ s/\/kernel$//; + $g->aug_defvar('template', $match); + } + + # Add a new title node at the end + $g->aug_defnode('new', + '/files/boot/grub/menu.lst/title[last()+1]', + $title); + + # N.B. Don't change the order of root, kernel and initrd below, + # or the guest will not boot. + + # Copy root from the template + $g->aug_set('$new/root', $g->aug_get('$template/root')); + + # Set kernel and initrd to the new values + $g->aug_set('$new/kernel', "$prefix/vmlinuz-$version"); + $g->aug_set('$new/initrd', "$prefix/initrd-$version.img"); + + # Copy all kernel command-line arguments + foreach my $arg ($g->aug_match('$template/kernel/*')) { + # kernel arguments don't necessarily have values + my $val; + eval { + $val = $g->aug_get($arg); + }; + + $arg =~ /([^\/]*)$/; + $arg = $1; + + if (defined($val)) { + $g->aug_set('$new/kernel/'.$arg, $val); + } else { + $g->aug_clear('$new/kernel/'.$arg); + } + } + + my ($new) = $g->aug_match('$new'); + $new =~ /\[(\d+)\]$/; + + $g->aug_set('/files/boot/grub/menu.lst/default', + defined($1) ? $1 - 1 : 0); + } + + else { + die("Didn't find a grub entry for kernel version $version"); + } + } + + eval { + $g->aug_save(); + }; + + if ($@) { + my $msg = ''; + foreach my $error ($g->aug_match('/augeas//error')) { + $msg .= $error.': '.$g->aug_get($error)."\n"; + } + die($msg); + } }; # Propagate augeas failure die($@) if($@); - if(!$found) { - die(user_message(__x("Didn't find a grub entry for kernel version ". - "{version}", version => $version))); - } - if(!defined($initrd)) { print STDERR user_message(__x("WARNING: Kernel version {version} ". "doesn't have an initrd entry in grub", version => $version)); } else { - # Initrd as returned by grub is relative to /boot - $initrd = "/boot$initrd"; + # Initrd as returned by grub may be relative to /boot + $initrd = $self->{desc}->{boot}->{grub_fs}.$initrd; # Backup the original initrd $g->mv("$initrd", "$initrd.pre-v2v"); -- 1.6.6.1
Richard W.M. Jones
2010-Apr-14 10:36 UTC
[Libguestfs] [PATCH] Workaround issue where grubby fails when run under libguestfs
On Wed, Apr 14, 2010 at 11:31:46AM +0100, Matthew Booth wrote:> When installing kernel-2.6.18-128.el5 in a guest with > kernel-xen-2.6.18-164.el5xen already installed, and 'root=LABEL=/' on the kernel > command line, grubby fails to install a grub entry for the new kernel. It only > fails when run under libguestfs. Installing in the original Xen guest works > fine. The underlying cause appears to be related to blkid, although I haven't > managed to pin it down exactly. > > This change works around the issue. If virt-v2v encounters a problem with grub, > it will install a new kernel entry itself using augeas. > > Note that this change introduces a dependency on the new aug_clear libguestfs > command.Looks good, ACK. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones virt-p2v converts physical machines to virtual machines. Boot with a live CD or over the network (PXE) and turn machines into Xen guests. http://et.redhat.com/~rjones/virt-p2v