[Libguestfs] [PATCH] virt-v2v: Convert RedHat.pm to Linux.pm - for SUSE support

Mike Latimer mlatimer at suse.com
Fri Oct 11 22:57:32 UTC 2013


On Tuesday, October 08, 2013 10:05:17 AM Matthew Booth wrote:
> Feel free to remove ^$path. However, for robustness I ditch the leading
> ^ from your replacement. The difference will be down to a different
> version of file included in the libguestfs appliance on SUSE.

I think I've addressed all the concerns you raised in the following patch. Can 
you take a look and let me know if I've created any new ones?  ;)

I have had to jump through a few hoops with regards to the SUSE -base kernel, 
the kernel flavor, and an irritating build number... But I've been careful to 
avoid changing variables for non-SUSE environments. There are a couple of 
small places that could still happen (in _inspect_linux_kernel), but I think 
there are enough safeguards in place that it shouldn't happen.

Thanks,
Mike


---
 .../VirtConvert/Converter/{RedHat.pm => Linux.pm}  | 797 
++++++++++++++++-----
 1 file changed, 632 insertions(+), 165 deletions(-)
 rename lib/Sys/VirtConvert/Converter/{RedHat.pm => Linux.pm} (75%)

diff --git a/lib/Sys/VirtConvert/Converter/RedHat.pm 
b/lib/Sys/VirtConvert/Converter/Linux.pm
similarity index 75%
rename from lib/Sys/VirtConvert/Converter/RedHat.pm
rename to lib/Sys/VirtConvert/Converter/Linux.pm
index 612ab2e..34752cf 100644
--- a/lib/Sys/VirtConvert/Converter/RedHat.pm
+++ b/lib/Sys/VirtConvert/Converter/Linux.pm
@@ -1,5 +1,6 @@
-# Sys::VirtConvert::Converter::RedHat
+# Sys::VirtConvert::Converter::Linux
 # Copyright (C) 2009-2012 Red Hat Inc.
+# Copyright (C) 2013 SUSE Inc.
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -19,9 +20,9 @@ use strict;
 use warnings;
 
 
-# Functions supported by grubby, and therefore common between gruby legacy 
and
-# grub2
-package Sys::VirtConvert::Converter::RedHat::Grub;
+# Functions supported by grubby or perl-Bootloader, and therefore common
+# between grub legacy grub2
+package Sys::VirtConvert::Converter::Linux::Grub;
 
 use Sys::VirtConvert::Util;
 use Locale::TextDomain 'virt-v2v';
@@ -30,16 +31,85 @@ sub get_initrd
 {
     my $self = shift;
     my ($path) = @_;
+    my $initrd;
 
     my $g = $self->{g};
 
-    foreach my $line ($g->command_lines(['grubby', '--info', $path])) {
-        return $1 if $line =~ /^initrd=(\S+)/;
+    if ($g->exists('/sbin/grubby')) {
+        foreach my $line ($g->command_lines(['grubby', '--info', $path])) {
+            return $1 if $line =~ /^initrd=(\S+)/;
+        }
+    } else {
+        # If grubby did not work, try perl-Bootloader (for SUSE environments)
+        $initrd = eval { $g->command(['/usr/bin/perl',
+           '-MBootloader::Tools',
+           '-e', 'InitLibrary(); '.
+           'my @sections = '.
+           'GetSectionList(type=>image, image=>"'.$path.'"); '.
+           'my $section = GetSection(@sections); '.
+           'my $initrd = $section->{initrd}; '.
+           'print $initrd;']) };
+
+        if (defined($initrd)) {
+            # If the initrd starts with (hdX,X), remove it.
+            $initrd =~ s/^\(hd.*\)//;
+            return $initrd if ($initrd =~ /^\//);
+        }
     }
 
+    # If all else fails, use heuristics if the file exists
+    ($initrd = $path) =~ s/vmlinuz/initrd/;
+    return $initrd if ($g->exists($initrd));
+
     v2vdie __x('Didn\'t find initrd for kernel {path}', path => $path);
 }
 
+sub get_default_image
+{
+    my $self = shift;
+    my $default;
+
+    my $g = $self->{g};
+
+    if ($g->exists('/sbin/grubby')) {
+        $default = $g->command(['grubby', '--default-kernel']);
+    } else {
+        $default = eval { $g->command(['/usr/bin/perl',
+                  '-MBootloader::Tools',
+                  '-e', 'InitLibrary(); '.
+                  'my $default=Bootloader::Tools::GetDefaultSection(); '.
+                  'print $default->{image};']) };
+        # If the image starts with (hdX,X), remove it.
+        $default =~ s/^\(hd.*\)//;
+    }
+
+    chomp($default);
+    return $default;
+}
+
+sub set_default_image
+{
+    my $self = shift;
+    my ($path) = @_;
+
+    my $g = $self->{g};
+
+    if ($g->exists('/sbin/grubby')) {
+        $g->command(['grubby', '--set-default', $path]);
+    } else {
+        # Using the image path to set a default image is not always reliable.
+        # To be safe, get the image name, then set that as the default image.
+        eval { $g->command(['/usr/bin/perl',
+           '-MBootloader::Tools',
+           '-e', 'InitLibrary(); '.
+           'my @sections = '.
+           'GetSectionList(type=>image, image=>"'.$path.'"); '.
+           'my $section = GetSection(@sections); '.
+           'my $newdefault = $section->{name}; '.
+           'SetGlobals(default, "$newdefault");']) };
+   }
+}
+
 sub check_efi
 {
     my $self = shift;
@@ -61,15 +131,15 @@ sub check_efi
 
 
 # Methods for inspecting and manipulating grub legacy
-package Sys::VirtConvert::Converter::RedHat::GrubLegacy;
+package Sys::VirtConvert::Converter::Linux::GrubLegacy;
 
-use Sys::VirtConvert::Util;
+use Sys::VirtConvert::Util qw(:DEFAULT augeas_error);
 
 use File::Basename;
 use Locale::TextDomain 'virt-v2v';
 
- at Sys::VirtConvert::Converter::RedHat::GrubLegacy::ISA =
-    qw(Sys::VirtConvert::Converter::RedHat::Grub);
+ at Sys::VirtConvert::Converter::Linux::GrubLegacy::ISA =
+    qw(Sys::VirtConvert::Converter::Linux::Grub);
 
 sub new
 {
@@ -127,7 +197,8 @@ sub new
 
         # If it wasn't there, add it
         unless ($found) {
-            $g->aug_set("/augeas/load/Grub/incl[last()+1]", $self-
>{grub_conf});
+            $g->aug_set("/augeas/load/Grub/incl[last()+1]",
+                        $self->{grub_conf});
 
             # Make augeas pick up the new configuration
             $g->aug_load();
@@ -236,7 +307,7 @@ sub update_console
 sub check
 {
     my $self = shift;
-    my ($path) = @_;
+    my ($path, $root) = @_;
 
     my $g = $self->{g};
     my $grub_conf = $self->{grub_conf};
@@ -247,19 +318,22 @@ sub check
     my $grub_path = $1;
 
     # Nothing to do if the kernel already has a grub entry
-    my @entries = $g->aug_match("/files$grub_conf/title/kernel[. = 
'$grub_path']");
+    my @entries = $g->aug_match("/files$grub_conf/title/kernel".
+                            "[. = '$grub_path']");
     return if scalar(@entries) > 0;
 
     my $kernel =
-        Sys::VirtConvert::Converter::RedHat::_inspect_linux_kernel($g, 
$path);
+        Sys::VirtConvert::Converter::Linux::_inspect_linux_kernel($g, $path);
     my $version = $kernel->{version};
     my $grub_initrd = dirname($path)."/initrd-$version";
 
-    # No point in dying if /etc/redhat-release can't be read
-    my ($title) = eval { $g->read_lines('/etc/redhat-release') };
+    # No point in dying if /etc/(distro)-release can't be read
+    my ($title) = eval { $g->inspect_get_product_name($root) };
     $title ||= 'Linux';
 
-    # This is how new-kernel-pkg does it
+    # Remove codename or architecture
+    $title =~ s/ \(.*\)//;
+    # Remove release string and add version (like new-kernel-pkg)
     $title =~ s/ release.*//;
     $title .= " ($version)";
 
@@ -365,13 +439,13 @@ sub convert_efi
 # attempt to use grub2's configuration because it's utterly insane. Instead,
 # we reverse engineer the way the config is automatically generated and use
 # that instead.
-package Sys::VirtConvert::Converter::RedHat::Grub2;
+package Sys::VirtConvert::Converter::Linux::Grub2;
 
 use Sys::VirtConvert::Util qw(:DEFAULT augeas_error);
 use Locale::TextDomain 'virt-v2v';
 
- at Sys::VirtConvert::Converter::RedHat::Grub2::ISA =
-    qw(Sys::VirtConvert::Converter::RedHat::Grub);
+ at Sys::VirtConvert::Converter::Linux::Grub2::ISA =
+    qw(Sys::VirtConvert::Converter::Linux::Grub);
 
 sub new
 {
@@ -407,8 +481,7 @@ sub list_kernels
     my @kernels;
 
     # Start by adding the default kernel
-    my $default = $g->command(['grubby', '--default-kernel']);
-    chomp($default);
+    my $default = $self->get_default_image();
     push(@kernels, $default) if length($default) > 0;
 
     # This is how the grub2 config generator enumerates kernels
@@ -430,8 +503,15 @@ sub update_console
 
     my $g = $self->{g};
 
+    my $grub_cmdline;
+    if ($g->exists('/etc/sysconfig/grub')) {
+        $grub_cmdline = '/files/etc/sysconfig/grub/GRUB_CMDLINE_LINUX';
+    } else {
+        $grub_cmdline = '/files/etc/default/grub/GRUB_CMDLINE_LINUX_DEFAULT';
+    }
+
     my $cmdline =
-        eval { $g->aug_get('/files/etc/sysconfig/grub/GRUB_CMDLINE_LINUX') };
+        eval { $g->aug_get($grub_cmdline) };
 
     if (defined($cmdline) && $cmdline =~ /\bconsole=(?:x|h)vc0\b/) {
         if ($remove) {
@@ -441,7 +521,7 @@ sub update_console
         }
 
         eval {
-            $g->aug_set('/files/etc/sysconfig/grub/GRUB_CMDLINE_LINUX', 
$cmdline);
+            $g->aug_set($grub_cmdline, $cmdline);
             $g->aug_save();
         };
         augeas_error($g, $@) if ($@);
@@ -463,11 +543,14 @@ sub write
 
     my $g = $self->{g};
 
-    my $default = $g->command(['grubby', '--default-kernel']);
-    chomp($default);
+    my $default = $self->get_default_image();
 
     if ($default ne $path) {
-        $g->command(['grubby', '--set-default', $path]);
+        eval { $self->set_default_image($path) };
+        if ($@) {
+            logmsg WARN, __x('Unable to set default kernel to {path}',
+                             path => $path);
+        }
     }
 }
 
@@ -486,7 +569,7 @@ sub convert_efi
 
     # EFI systems boot using grub2-efi, and probably don't have the base grub2
     # package installed.
-    Sys::VirtConvert::Convert::RedHat::_install_any
+    Sys::VirtConvert::Convert::Linux::_install_any
         (undef, ['grub2'], undef, $g, $self->{root}, $self->{config}, $self)
         or v2vdie __x('Failed to install non-EFI grub2');
 
@@ -494,7 +577,8 @@ sub convert_efi
     $g->part_set_gpt_type($device, 1, 
'21686148-6449-6E6F-744E-656564454649');
 
     # Delete the fstab entry for the EFI boot partition
-    foreach my $node ($g->aug_match("/files/etc/fstab/*[file = '/boot/efi']")) {
+    foreach my $node ($g->aug_match("/files/etc/fstab/*[file = '/boot/efi']"))
+    {
         $g->aug_rm($node);
     }
     eval { $g->aug_save(); };
@@ -509,7 +593,7 @@ sub convert_efi
 }
 
 
-package Sys::VirtConvert::Converter::RedHat;
+package Sys::VirtConvert::Converter::Linux;
 
 use Sys::VirtConvert::Util qw(:DEFAULT augeas_error scsi_first_cmp);
 use Locale::TextDomain 'virt-v2v';
@@ -518,7 +602,7 @@ use Locale::TextDomain 'virt-v2v';
 
 =head1 NAME
 
-Sys::VirtConvert::Converter::RedHat - Convert a Red Hat based guest to run on 
KVM
+Sys::VirtConvert::Converter::Linux - Convert a Linux based guest to run on 
KVM
 
 =head1 SYNOPSIS
 
@@ -528,7 +612,7 @@ Sys::VirtConvert::Converter::RedHat - Convert a Red Hat 
based guest to run on KV
 
 =head1 DESCRIPTION
 
-Sys::VirtConvert::Converter::RedHat converts a Red Hat based guest to use 
KVM.
+Sys::VirtConvert::Converter::Linux converts a Linux based guest to use KVM.
 
 =head1 METHODS
 
@@ -541,12 +625,21 @@ sub _is_rhel_family
     my ($g, $root) = @_;
 
     return ($g->inspect_get_type($root) eq 'linux') &&
-           ($g->inspect_get_distro($root) =~ /^(rhel|centos|scientificlinux|
redhat-based)$/);
+           ($g->inspect_get_distro($root) =~
+               /^(rhel|centos|scientificlinux|redhat-based)$/);
+}
+
+sub _is_suse_family
+{
+    my ($g, $root) = @_;
+
+    return ($g->inspect_get_type($root) eq 'linux') &&
+           ($g->inspect_get_distro($root) =~ /^(sles|suse-based|opensuse)$/);
 }
 
-=item Sys::VirtConvert::Converter::RedHat->can_handle(g, root)
+=item Sys::VirtConvert::Converter::Linux->can_handle(g, root)
 
-Return 1 if Sys::VirtConvert::Converter::RedHat can convert the given guest
+Return 1 if Sys::VirtConvert::Converter::Linux can convert the given guest
 
 =cut
 
@@ -556,13 +649,17 @@ sub can_handle
 
     my ($g, $root) = @_;
 
-    return ($g->inspect_get_type($root) eq 'linux' &&
-            (_is_rhel_family($g, $root) || $g->inspect_get_distro($root) eq 
'fedora'));
+    if ($g->inspect_get_type($root) eq 'linux') {
+        return (_is_rhel_family($g, $root) ||
+               ($g->inspect_get_distro($root) eq 'fedora') ||
+                _is_suse_family($g, $root));
+    }
 }
 
-=item Sys::VirtConvert::Converter::RedHat->convert(g, root, config, meta, 
options)
+=item Sys::VirtConvert::Converter::Linux->convert(g, root, config, meta,
+                                                  options)
 
-Convert a Red Hat based guest. Assume that can_handle has previously returned 
1.
+Convert a Linux based guest. Assume that can_handle has previously returned 
1.
 
 =over
 
@@ -601,8 +698,10 @@ sub convert
     _init_augeas($g);
 
     my $grub;
-    $grub = eval { Sys::VirtConvert::Converter::RedHat::Grub2->new($g, $root, 
$config) };
-    $grub = eval { Sys::VirtConvert::Converter::RedHat::GrubLegacy->new($g, 
$root) }
+    $grub = eval
+       { Sys::VirtConvert::Converter::Linux::Grub2->new($g, $root, $config) };
+    $grub = eval
+       { Sys::VirtConvert::Converter::Linux::GrubLegacy->new($g, $root) }
         unless defined($grub);
     v2vdie __('No grub configuration found') unless defined($grub);
 
@@ -611,13 +710,15 @@ sub convert
     _unconfigure_hv($g, $root);
 
     # Try to install the virtio capability
-    my $virtio = _install_capability('virtio', $g, $root, $config, $meta, 
$grub);
+    my $virtio =
+        _install_capability('virtio', $g, $root, $config, $meta, $grub);
 
     # Get an appropriate kernel, or install one if none is available
     my $kernel = _configure_kernel($virtio, $g, $root, $config, $meta, $grub);
 
     # Install user custom packages
-    if (! _install_capability('user-custom', $g, $root, $config, $meta, 
$grub)) {
+    if (! _install_capability('user-custom',
+                              $g, $root, $config, $meta, $grub)) {
         logmsg WARN, __('Failed to install user-custom packages');
     }
 
@@ -625,8 +726,9 @@ sub convert
     my $remove_serial_console = exists($options->{NO_SERIAL_CONSOLE});
     _configure_console($g, $grub, $remove_serial_console);
 
-    _configure_display_driver($g, $root, $config, $meta, $grub);
-    _remap_block_devices($meta, $virtio, $g, $root);
+    my $driver = _get_display_driver($g, $root);
+    _configure_display_driver($g, $root, $config, $meta, $grub, $driver);
+    _remap_block_devices($meta, $virtio, $g, $root, $grub);
     _configure_kernel_modules($g, $virtio);
     _configure_boot($kernel, $virtio, $g, $root, $grub);
 
@@ -647,8 +749,9 @@ sub _init_selinux
     # Assume SELinux isn't in use if load_policy isn't available
     return if(!$g->exists('/usr/sbin/load_policy'));
 
-    # Actually loading the policy has proven to be problematic. We make 
whatever
-    # changes are necessary, and make the guest relabel on the next boot.
+    # Actually loading the policy has proven to be problematic. We make
+    # whatever changes are necessary, and make the guest relabel on the
+    # next boot.
     $g->touch('/.autorelabel');
 }
 
@@ -689,8 +792,8 @@ sub _discover_modpath
 
     my $modpath;
 
-    # Note that we're checking in ascending order of preference so that the 
last
-    # discovered method will be chosen
+    # Note that we're checking in ascending order of preference so that the
+    # last discovered method will be chosen
 
     foreach my $file ('/etc/conf.modules', '/etc/modules.conf') {
         if($g->exists($file)) {
@@ -759,7 +862,8 @@ sub _configure_kernel_modules
         my @xen_modules = qw(xennet xen-vnif xenblk xen-vbd);
         my $query = '('.join('|', @xen_modules).')';
 
-        foreach my $path (_aug_modprobe($g, "modulename =~ 
regexp('$query')")) {
+        foreach my $path (_aug_modprobe($g, "modulename =~ 
regexp('$query')"))
+        {
             my $device = $g->aug_get($path);
             my $module = $g->aug_get($path.'/modulename');
 
@@ -783,13 +887,13 @@ sub _configure_kernel_modules
     augeas_error($g, $@) if $@;
 }
 
-# We configure a console on ttyS0. Make sure existing console references use 
it.
+# Configure a console on ttyS0. Make sure existing console references use it.
 # N.B. Note that the RHEL 6 xen guest kernel presents a console device called
-# /dev/hvc0, whereas previous xen guest kernels presented /dev/xvc0. The 
regular
-# kernel running under KVM also presents a virtio console device called
-# /dev/hvc0, so ideally we would just leave it alone. However, RHEL 6 libvirt
-# doesn't yet support this device so we can't attach to it. We therefore use
-# /dev/ttyS0 for RHEL 6 anyway.
+# /dev/hvc0, whereas previous xen guest kernels presented /dev/xvc0. The
+# regular kernel running under KVM also presents a virtio console device
+# called /dev/hvc0, so ideally we would just leave it alone. However, RHEL 6
+# libvirt doesn't yet support this device so we can't attach to it. We
+# therefore use /dev/ttyS0 for RHEL 6 anyway.
 #
 # If the target doesn't support a serial console, we want to remove all
 # references to it instead.
@@ -842,7 +946,7 @@ sub _configure_console
 
 sub _configure_display_driver
 {
-    my ($g, $root, $config, $meta, $grub) = @_;
+    my ($g, $root, $config, $meta, $grub, $driver) = @_;
 
     # Update the display driver if it exists
     my $updated = 0;
@@ -866,7 +970,7 @@ sub _configure_display_driver
         }
 
         foreach my $path ($g->aug_match('/files'.$xorg.'/Device/Driver')) {
-            $g->aug_set($path, 'qxl');
+            $g->aug_set($path, $driver);
             $updated = 1;
         }
 
@@ -884,20 +988,21 @@ sub _configure_display_driver
     # Propagate augeas errors
     augeas_error($g, $@) if ($@);
 
-    # If we updated the X driver, check if X itself is actually installed. If 
it
-    # is, ensure the qxl driver is installed.
+    # If we updated the X driver, check if X itself is actually installed. If
+    # it is, ensure the specified driver is installed.
     if ($updated &&
         ($g->exists('/usr/bin/X') || $g->exists('/usr/bin/X11/X')) &&
-        !_install_capability('qxl', $g, $root, $config, $meta, $grub))
+        !_install_capability($driver, $g, $root, $config, $meta, $grub))
     {
-        logmsg WARN, __('Display driver was updated to qxl, but unable to '.
-                        'install qxl driver. X may not function correctly');
+        logmsg WARN, __x('Display driver was updated to {driver}, but unable 
'.
+                        'to install {driver} driver. X may not function '.
+                        'correctly', driver => $driver);
     }
 }
 
 # If the guest was shutdown uncleanly, it's possible that transient state was
-# left lying around in the rpm database. Given we know that nothing is using 
the
-# rpmdb at this point, it's safe to delete these files.
+# left lying around in the rpm database. Given we know that nothing is using
+# the rpmdb at this point, it's safe to delete these files.
 sub _clean_rpmdb
 {
     my $g = shift;
@@ -925,20 +1030,46 @@ sub _inspect_linux_kernel
     # If this is a packaged kernel, try to work out the name of the package
     # which installed it. This lets us know what to install to replace it 
with,
     # e.g. kernel, kernel-smp, kernel-hugemem, kernel-PAE
-    my $package = eval { $g->command(['rpm', '-qf', '--qf',
-                                      '%{NAME}', $path]) };
-    $kernel{package} = $package if defined($package);;
+    #
+    # Due to the inclusion of a build number in SUSE packages, the version
+    # found through file and the kernel filename does not always match the
+    # version required for installation later. Get the full version-release
+    # here and track it through $kernel{fullversion}.
+    my $package;
+    my $flavor;
+    my $rpminfo = eval { $g->command(['rpm', '-qf', '--qf',
+                                   '%{NAME} %{VERSION}-%{RELEASE}', $path]) 
};
+    if (defined($rpminfo)) {
+        $rpminfo =~ /(\S+)\s(\S+)/;
+        $package = $1;
+        my $fullversion = $2;
+
+        # Some SUSE kernels are from a -base package, but it's more correct 
to
+        # use the non -base package name.
+        $package =~ s/-base$//;
+        $kernel{package} = $package;
+
+        # fullversion must include the flavor (xen, smp, default, etc.) of the
+        # kernel as well (if one exists)
+        ($flavor = $package) =~ s/^kernel//;
+        $fullversion = $fullversion.$flavor if defined($flavor);
+        $kernel{fullversion} = $fullversion;
+    }
 
     # Try to get the kernel version by running file against it
     my $version;
     my $filedesc = $g->file($path);
-    if($filedesc =~ /^$path: Linux kernel .*\bversion\s+(\S+)\b/) {
+    if($filedesc =~ /Linux.* [kK]ernel.*\b[vV]ersion\s+(\S+)\b/) {
         $version = $1;
-    }
-
-    # Sometimes file can't work out the kernel version, for example because 
it's
-    # a Xen PV kernel. In this case try to guess the version from the filename
-    else {
+        # SUSE kernel version strings do not include the flavor
+        $version = $version.$flavor if defined($flavor);
+        # If $version is not correct here, default to the filename method
+        undef $version if(!$g->is_dir("/lib/modules/$version"));
+    }
+    # Sometimes file can't work out the kernel version, for example
+    # because it's a Xen PV kernel. In this case try to guess the version
+    # from the filename
+    if (!defined($version)) {
         if($path =~ m{/boot/vmlinuz-(.*)}) {
             $version = $1;
 
@@ -988,6 +1119,7 @@ sub _configure_kernel
 
     # Pick first appropriate kernel returned by list_kernels
     my $boot_kernel;
+    my $backup_ver;
     foreach my $path ($grub->list_kernels()) {
         my $kernel = _inspect_linux_kernel($g, $path);
         my $version = $kernel->{version};
@@ -998,11 +1130,21 @@ sub _configure_kernel
         # If we're configuring virtio, check this kernel supports it
         next if ($virtio && !_supports_virtio($version, $g));
 
-        $boot_kernel = $version;
+
+        # SUSE kernel installations require the version string which includes
+        # the build number. Change it here, but backup the original version 
as
+        # it must be restored later.
+        if (_is_suse_family($g, $root)) {
+            $boot_kernel = $kernel->{fullversion};
+            $backup_ver = $version;
+        } else {
+            $boot_kernel = $version;
+        }
         last;
     }
 
-    # There should be an installed virtio capable kernel if virtio was 
installed
+    # There should be an installed virtio capable kernel if virtio was
+    # installed
     die("virtio configured, but no virtio kernel found")
         if ($virtio && !defined($boot_kernel));
 
@@ -1013,7 +1155,7 @@ sub _configure_kernel
 
         # If the guest is using a Xen PV kernel, choose an appropriate
         # normal kernel replacement
-        if ($kernel_pkg eq "kernel-xen" || $kernel_pkg eq "kernel-xenU") {
+        if ($kernel_pkg =~ /^kernel-xen/) {
             $kernel_pkg = _get_replacement_kernel_name($g, $root,
                                                        $kernel_arch, $meta);
 
@@ -1055,7 +1197,7 @@ sub _configure_kernel
                     unless defined($boot_kernel);
             } else {
                 v2vdie __x('Failed to find a {name} package to install',
-                           name => "kernel_pkg.$kernel_arch");
+                           name => "$kernel_pkg.$kernel_arch");
             }
         }
     }
@@ -1084,6 +1226,9 @@ sub _configure_kernel
         augeas_error($g, $@) if ($@);
     }
 
+    # If the kernel version was backed up previously (SUSE environments),
+    # restore the original value before returning it.
+    $boot_kernel = $backup_ver if defined($backup_ver);
     return $boot_kernel;
 }
 
@@ -1097,8 +1242,8 @@ sub _configure_boot
         # reason is that the probing order determines the major number of vdX
         # block devices. If we change it, RHEL 3 KVM guests won't boot.
         _prepare_bootable($g, $root, $grub, $kernel, "virtio", "virtio_ring",
-                                                     "virtio_blk", 
"virtio_net",
-                                                     "virtio_pci");
+                                                    "virtio_blk", 
"virtio_net",
+                                                    "virtio_pci");
     } else {
         _prepare_bootable($g, $root, $grub, $kernel, "sym53c8xx");
     }
@@ -1121,8 +1266,8 @@ sub _get_os_arch
     # Default to x86_64 if we still didn't find an architecture
     return 'x86_64' unless defined($arch);
 
-    # We want an i686 guest for i[345]86
-    return 'i686' if($arch =~ /^i[345]86$/);
+    # Change i386 to i[56]86
+    $arch = _set_32bit_arch($g, $root, $arch);
 
     return $arch;
 }
@@ -1168,7 +1313,7 @@ sub _unconfigure_hv
 
     my @apps = $g->inspect_list_applications($root);
 
-    _unconfigure_xen($g, \@apps);
+    _unconfigure_xen($g, $root, \@apps);
     _unconfigure_vbox($g, \@apps);
     _unconfigure_vmware($g, \@apps);
     _unconfigure_citrix($g, \@apps);
@@ -1177,7 +1322,7 @@ sub _unconfigure_hv
 # Unconfigure Xen specific guest modifications
 sub _unconfigure_xen
 {
-    my ($g, $apps) = @_;
+    my ($g, $root, $apps) = @_;
 
     # Look for kmod-xenpv-*, which can be found on RHEL 3 machines
     my @remove;
@@ -1230,6 +1375,27 @@ sub _unconfigure_xen
             $g->write_file('/etc/rc.local', join("\n", @rc_local)."\n", 
$size);
         }
     }
+
+    if (_is_suse_family($g, $root)) {
+        # Remove xen modules from INITRD_MODULES and DOMU_INITRD_MODULES
+        my $sysconfig = '/etc/sysconfig/kernel';
+        my @variables = qw(INITRD_MODULES DOMU_INITRD_MODULES);
+        my @xen_modules = qw(xennet xen-vnif xenblk xen-vbd);
+        my $modified;
+
+        foreach my $var (@variables) {
+            foreach my $xen_mod (@xen_modules) {
+                foreach my $entry
+                    ($g->aug_match("/files$sysconfig/$var/'.
+                                   'value[. = '$xen_mod']"))
+                {
+                    $g->aug_rm($entry);
+                    $modified = 1;
+                }
+            }
+        }
+        $g->aug_save if (defined($modified));
+    }
 }
 
 # Unconfigure VirtualBox specific guest modifications
@@ -1313,8 +1479,8 @@ sub _unconfigure_vmware
 
     # VMware tools includes 'libraries' packages which provide custom 
versions
     # of core functionality. We need to install non-custom versions of
-    # everything provided by these packages before attempting to uninstall 
them,
-    # or we'll hit dependency issues
+    # everything provided by these packages before attempting to uninstall
+    # them, or we'll hit dependency issues
     if (@libraries > 0) {
         # We only support removal of these libraries packages on systems 
which
         # use yum.
@@ -1338,7 +1504,7 @@ sub _unconfigure_vmware
                         # of required dependencies out of our control.
                         my %alts;
                         foreach my $alt ($g->command_lines
-                                       (['yum', '-q', 'resolvedep', 
@provides]))
+                                    (['yum', '-q', 'resolvedep', @provides]))
                         {
                             $alts{$alt} = 1;
                         }
@@ -1404,8 +1570,8 @@ sub _unconfigure_citrix
 
                 # The entries in question are named 1-6, and will normally be
                 # active in runlevels 2-5. They will be gettys. We could be
-                # extremely prescriptive here, but allow for a reasonable 
amount
-                # of variation just in case.
+                # extremely prescriptive here, but allow for a reasonable
+                # amount of variation just in case.
                 next unless $comment =~ /^([1-6]):([2-5]+):respawn:(.*)/;
 
                 my $name = $1;
@@ -1483,8 +1649,8 @@ sub _install_capability
             my ($kernel_pkg, $kernel_arch, $kernel_rpmver) =
                 _discover_kernel($g, $root, $grub);
 
-            # If we didn't establish a kernel version, assume we have to 
upgrade
-            # it.
+            # If we didn't establish a kernel version, assume we have to
+            # upgrade it.
             if (!defined($kernel_rpmver)) {
                 $kernel = [$kernel_pkg, $kernel_arch];
             }
@@ -1506,13 +1672,13 @@ sub _install_capability
 
                 # If the guest is using a Xen PV kernel, choose an 
appropriate
                 # normal kernel replacement
-                if ($kernel_pkg eq "kernel-xen" || $kernel_pkg eq "kernel-
xenU")
+                if ($kernel_pkg =~ /^kernel-xen/)
                 {
                     $kernel_pkg =
                         _get_replacement_kernel_name($g, $root, $kernel_arch,
                                                      $meta);
 
-                    # Check if we've got already got an appropriate kernel
+                    # Check if we've already got an appropriate kernel
                     my ($inst) =
                         _get_installed("$kernel_pkg.$kernel_arch", $g);
 
@@ -1523,7 +1689,7 @@ sub _install_capability
                     {
                         # filter out xen/xenU from release field
                         if (defined($kernel_release) &&
-                            $kernel_release =~ /^(\S+?)(xen)?(U)?$/)
+                            $kernel_release =~ /^(\S+?)(-xen)?(U)?$/)
                         {
                             $kernel_release = $1;
                         }
@@ -1574,7 +1740,8 @@ sub _install_capability
                     my ($epoch, $version, $release) = @$app;
 
                     if (_evr_cmp($app->[0], $app->[1], $app->[2],
-                                 $min_epoch, $min_version, $min_release) >= 
0) {
+                                 $min_epoch, $min_version, $min_release) >= 
0)
+                    {
                         $found = 1;
                         last;
                     }
@@ -1631,14 +1798,22 @@ sub _install_any
     # If we're installing a kernel, check which kernels are there first
     my @k_before = $g->glob_expand('/boot/vmlinuz-*') if defined($kernel);
 
+    # Workaround for SUSE bnc#836521
+    my $pbl_fix = _modify_perlBootloader($g) if (defined($kernel) &&
+                  (_is_suse_family($g, $root)));
+
     my $success = 0;
     _net_run($g, sub {
         eval {
             # Try to fetch these dependencies using the guest's native update
             # tool
-            $success = _install_up2date($kernel, $install, $upgrade, $g);
-            $success = _install_yum($kernel, $install, $upgrade, $g)
-                unless ($success);
+            if (_is_suse_family($g, $root)) {
+                $success = _install_zypper($kernel, $install, $upgrade, $g);
+            } else {
+                $success = _install_up2date($kernel, $install, $upgrade, $g);
+                $success = _install_yum($kernel, $install, $upgrade, $g)
+                    unless ($success);
+            }
 
             # Fall back to local config if the above didn't work
             $success = _install_config($kernel, $install, $upgrade,
@@ -1648,6 +1823,9 @@ sub _install_any
         warn($@) if $@;
     });
 
+    # Undo the previous workaround for SUSE bnc#836521
+    _restore_perlBootloader($g) if ($pbl_fix == 1);
+
     # Make augeas reload to pick up any altered configuration
     eval { $g->aug_load() };
     augeas_error($g, $@) if ($@);
@@ -1657,7 +1835,7 @@ sub _install_any
     if (defined($kernel)) {
         foreach my $k ($g->glob_expand('/boot/vmlinuz-*')) {
             if (!grep(/^$k$/, @k_before)) {
-                $grub->check($k);
+                $grub->check($k, $root);
                 last;
             }
         }
@@ -1767,8 +1945,8 @@ sub _install_yum
             }
 
             foreach my $line (@output) {
-                # Yum probably just isn't configured. Don't bother with an 
error
-                # message
+                # Yum probably just isn't configured. Don't bother with an
+                # error message
                 if ($line =~ /$failure/) {
                     $success = 0;
                     last YUM;
@@ -1780,6 +1958,90 @@ sub _install_yum
     return $success;
 }
 
+sub _install_zypper
+{
+    my ($kernel, $install, $update, $g) = @_;
+
+    # Check this system has zypper
+    return 0 unless ($g->exists('/usr/bin/zypper'));
+
+    # Install or update the kernel?
+    # If it isn't installed (because we're replacing a PV kernel), we need to
+    # install
+    # If we're installing a specific version, we need to install
+    # If the kernel package we're installing is already installed and we're
+    # just upgrading to the latest version, we need to update
+    if (defined($kernel)) {
+        my @installed = _get_installed($kernel->[0], $g);
+
+        # Don't modify the contents of $install and $update in case we fall
+        # through and they're reused in another function
+        if (@installed == 0 || defined($kernel->[2])) {
+            my @tmp = defined($install) ? @$install : ();
+            push(@tmp, $kernel);
+            $install = \@tmp;
+        } else {
+            my @tmp = defined($update) ? @$update : ();
+            push(@tmp, $kernel);
+            $update = \@tmp;
+        }
+    }
+
+    my $success = 1;
+    # Error when installing: "No provider of 'pkg' found."
+    # (Not an) Error when updating: "Package 'pkg' is not available in your
+    # repositories. Cannot reinstall, upgrade, or downgrade."
+    ZYPPER: foreach my $task (
+        [ "install", $install, qr/(^No package|already installed)/ ],
+        [ "update", $update, qr/(^No Packages|not available)/ ]
+    ) {
+        my ($action, $list, $failure) = @$task;
+
+        # Build a list of packages to install
+        my @pkgs;
+        foreach my $entry (@$list) {
+            next unless (defined($entry));
+
+            # zypper doesn't need arch or epoch
+            my ($name, undef, undef, $version, $release) = @$entry;
+
+            # Construct n-v-r
+            my $pkg = $name;
+            $pkg .= "-$version" if (defined($version));
+            $pkg .= "-$release" if (defined($release));
+
+
+            push(@pkgs, "$pkg");
+        }
+
+        if (@pkgs) {
+            my @output =
+                eval { $g->command(['/usr/bin/zypper', '-n', $action,
+                     @pkgs]) };
+            if ($@) {
+                # Ignore 'No provider' errors as an install from the virt-v2v
+                # repo will be attempted next.
+                if ($@ !~ /No provider/) {
+                    logmsg WARN, __x('Failed to install packages. '.
+                                   'Error was: {error}', error => $@);
+                }
+                $success = 0;
+                last ZYPPER;
+            }
+            foreach my $line (@output) {
+                # Don't report an error or results if package is already
+                # installed or not found in a repo
+                if ($line =~ /$failure/) {
+                    $success = 0;
+                    last ZYPPER;
+                }
+            }
+        }
+    }
+
+    return $success;
+}
+
 sub _install_config
 {
     my ($kernel_naevr, $install, $upgrade, $g, $root, $config) = @_;
@@ -1806,19 +2068,42 @@ sub _install_config
         }
     }
 
+
     my @user_paths = _get_deppaths($g, $root, $config,
-                                   \@missing, $g->inspect_get_arch($root), 
@$user);
+                               \@missing, $g->inspect_get_arch($root), 
@$user);
 
     # We can't proceed if there are any files missing
     v2vdie __x('Installation failed because the following '.
                'files referenced in the configuration file are '.
                'required, but missing: {list}',
                list => join(' ', @missing)) if scalar(@missing) > 0;
-    # Install any non-kernel requirements
-    _install_rpms($g, $config, 1, @user_paths);
 
-    if (defined($kernel)) {
-        _install_rpms($g, $config, 0, ($kernel));
+    # If a SUSE kernel is being added, a -base kernel could have been added 
to
+    # to @user_paths (as a dep app). If so, move it to a new list containing
+    # both kernel and kernel-base packages to satisfy dependencies, and
+    # 'install' instead of 'upgrade' the packages.
+    if (_is_suse_family($g, $root) && (defined($kernel))) {
+        my @kernel_paths;
+        push(@kernel_paths, $kernel);
+        if (@user_paths) {
+            for my $index (reverse 0 .. scalar(@user_paths-1)) {
+                if ($user_paths[$index] =~ /(.*\/)kernel/) {
+                    push(@kernel_paths, $user_paths[$index]);
+                    splice(@user_paths, $index, 1);
+                }
+            }
+            # Install any non-kernel requirements
+            _install_rpms($g, $config, 1, @user_paths);
+        }
+        # Install kernel packages
+        _install_rpms($g, $config, 0, @kernel_paths);
+    } else {
+        # Install any non-kernel requirements
+        _install_rpms($g, $config, 1, @user_paths);
+
+        if (defined($kernel)) {
+            _install_rpms($g, $config, 0, ($kernel));
+        }
     }
 
     return 1;
@@ -1872,8 +2157,8 @@ sub _get_deppaths
             }
         }
 
-        # For x86_64, also check if there is any i386 or i686 version 
installed.
-        # If there is, check if it needs to be upgraded.
+        # For x86_64, also check if there is any i386 or i686 version
+        # installed. If there is, check if it needs to be upgraded.
         if ($arch eq 'x86_64') {
             $path = undef;
             $deps = undef;
@@ -1889,7 +2174,7 @@ sub _get_deppaths
                     push(@$missing, $path);
 
                     foreach my $deppath (_get_deppaths($g, $root, $config,
-                                                      $missing, 'i386', 
@$deps))
+                                                     $missing, 'i386', 
@$deps))
                     {
                         $required{$deppath} = 1;
                     }
@@ -1963,7 +2248,12 @@ sub _discover_kernel
         $kernel_pkg = $kernel->{package};
 
         # Get the kernel package version
-        $kernel_ver = $kernel->{version};
+        # SUSE requires the fullversion string
+        if (_is_suse_family($g, $root)) {
+            $kernel_ver = $kernel->{fullversion};
+        } else {
+            $kernel_ver = $kernel->{version};
+        }
 
         last;
     }
@@ -1971,13 +2261,12 @@ sub _discover_kernel
     # Default to 'kernel' if package name wasn't discovered
     $kernel_pkg = "kernel" if (!defined($kernel_pkg));
 
-    # Default the kernel architecture to the userspace architecture if it 
wasn't
-    # directly detected
+    # Default the kernel architecture to the userspace architecture if it
+    # wasn't directly detected
     $kernel_arch = $g->inspect_get_arch($root) unless defined($kernel_arch);
 
-    # We haven't supported anything other than i686 for the kernel on 32 bit 
for
-    # a very long time.
-    $kernel_arch = 'i686' if ('i386' eq $kernel_arch);
+    # Change i386 to i[56]86
+    $kernel_arch = _set_32bit_arch($g, $root, $kernel_arch);
 
     return ($kernel_pkg, $kernel_arch, $kernel_ver);
 }
@@ -1989,57 +2278,115 @@ sub _get_replacement_kernel_name
     # Make an informed choice about a replacement kernel for distros we know
     # about
 
-    # RHEL 5
-    if (_is_rhel_family($g, $root) && $g->inspect_get_major_version($root) eq 
'5') {
-        if ($arch eq 'i686') {
-            # XXX: This assumes that PAE will be available in the hypervisor.
-            # While this is almost certainly true, it's theoretically 
possible
-            # that it isn't. The information we need is available in the
-            # capabilities XML.  If PAE isn't available, we should choose
-            # 'kernel'.
-            return 'kernel-PAE';
+    # RedHat kernels
+    if (_is_rhel_family($g, $root)) {
+        # RHEL 5
+        if ($g->inspect_get_major_version($root) eq '5') {
+            if ($arch eq 'i686') {
+                # XXX: This assumes that PAE will be available in the
+                # hypervisor. While this is almost certainly true, it's
+                # theoretically possible that it isn't. The information we
+                # need is available in the capabilities XML.  If PAE isn't
+                # available, we should choose 'kernel'.
+                return 'kernel-PAE';
+            }
+
+            # There's only 1 kernel package on RHEL 5 x86_64
+            else {
+                return 'kernel';
+            }
         }
 
-        # There's only 1 kernel package on RHEL 5 x86_64
-        else {
-            return 'kernel';
+        # RHEL 4
+        elsif ($g->inspect_get_major_version($root) eq '4') {
+            if ($arch eq 'i686') {
+                # If the guest has > 10G RAM, give it a hugemem kernel
+                if ($meta->{memory} > 10 * 1024 * 1024 * 1024) {
+                    return 'kernel-hugemem';
+                }
+
+                # SMP kernel for guests with >1 CPU
+                elsif ($meta->{cpus} > 1) {
+                    return 'kernel-smp';
+                }
+
+                else {
+                    return 'kernel';
+                }
+            }
+
+            else {
+                if ($meta->{cpus} > 8) {
+                    return 'kernel-largesmp';
+                }
+
+                elsif ($meta->{cpus} > 1) {
+                    return 'kernel-smp';
+                }
+                else {
+                    return 'kernel';
+                }
+            }
         }
+
+        # RHEL 3 didn't have a xen kernel
     }
 
-    # RHEL 4
-    elsif (_is_rhel_family($g, $root) && $g->inspect_get_major_version($root) 
eq '4') {
-        if ($arch eq 'i686') {
-            # If the guest has > 10G RAM, give it a hugemem kernel
-            if ($meta->{memory} > 10 * 1024 * 1024 * 1024) {
-                return 'kernel-hugemem';
-            }
+    # SUSE kernels
+    elsif (_is_suse_family($g, $root)) {
+        # openSUSE should always use kernel-default
+        if ($g->inspect_get_distro($root) eq 'opensuse') {
+            return 'kernel-default';
+        }
+        # SLES 11+
+        elsif ($g->inspect_get_major_version($root) ge '11') {
+            if ($arch eq 'i586') {
+                # If the guest has > 10G RAM, give it a pae kernel
+                if ($meta->{memory} > 10 * 1024 * 1024 * 1024) {
+                    return 'kernel-pae';
+                }
 
-            # SMP kernel for guests with >1 CPU
-            elsif ($meta->{cpus} > 1) {
-                return 'kernel-smp';
+                else {
+                    return 'kernel-default';
+                }
             }
 
+            # There is only 1 kernel which should be used on SLES 11 x86_64
             else {
-                return 'kernel';
+                return 'kernel-default';
             }
         }
 
-        else {
-            if ($meta->{cpus} > 8) {
-                return 'kernel-largesmp';
-            }
+        # SLES 10
+        elsif ($g->inspect_get_major_version($root) eq '10') {
+            if ($arch eq 'i586') {
+                # If the guest has > 10G RAM, give it a bigsmp kernel
+                if ($meta->{memory} > 10 * 1024 * 1024 * 1024) {
+                    return 'kernel-bigsmp';
+                }
+
+                # SMP kernel for guests with >1 CPU
+                elsif ($meta->{cpus} > 1) {
+                    return 'kernel-smp';
+                }
 
-            elsif ($meta->{cpus} > 1) {
-                return 'kernel-smp';
+                else {
+                    return 'kernel-default';
+                }
             }
+
             else {
-                return 'kernel';
+                if ($meta->{cpus} > 1) {
+                    return 'kernel-smp';
+                }
+
+                else {
+                    return 'kernel-default';
+                }
             }
         }
     }
 
-    # RHEL 3 didn't have a xen kernel
-
     # XXX: Could do with a history of Fedora kernels in here
 
     # For other distros, be conservative and just return 'kernel'
@@ -2060,8 +2407,8 @@ sub _get_installed
         # Unfortunately, rpm sent its error to stdout instead of stderr, and
         # command_lines only gives us stderr in $@. To get round this we'll
         # execute the command again, sending all output to stdout and 
ignoring
-        # failure. If the output contains 'not installed', we'll assume it's 
not
-        # a real error.
+        # failure. If the output contains 'not installed', we'll assume it's
+        # not a real error.
         my $error = $g->sh("LANG=C '".join("' '", @$rpmcmd)."' 2>&1 ||:");
 
         return () if ($error =~ /not installed/);
@@ -2076,7 +2423,7 @@ sub _get_installed
             or die("Unexpected return from rpm command: $installed");
         my ($epoch, $version, $release) = ($1, $2, $3);
 
-        # Ensure iepoch is always numeric
+        # Ensure epoch is always numeric
         $epoch = 0 if('(none)' eq $epoch);
 
         push(@installed, [$epoch, $version, $release]);
@@ -2151,7 +2498,7 @@ sub _rpmvercmp
         # of $a.
         @$l = split(/(?<=[[:digit:]])(?=[[:alpha:]]) | # digit<>alpha
                      (?<=[[:alpha:]])(?=[[:digit:]]) | # alpha<>digit
-                     [^[:alnum:]]+                # sequence of non-
alphanumeric
+                     [^[:alnum:]]+               # sequence of non-
alphanumeric
                     /x, $s);
     }
 
@@ -2189,8 +2536,8 @@ sub _rpmvercmp
         return 1 if($acmp gt $bcmp);
     }
 
-    # We got here because all the parts compared so far have been equal, and 
one
-    # or both have run out of parts.
+    # We got here because all the parts compared so far have been equal, and
+    # one or both have run out of parts.
 
     # Whichever has the greatest number of parts is the largest
     return -1 if(scalar(@aparts) < scalar(@bparts));
@@ -2203,7 +2550,7 @@ sub _rpmvercmp
 
 sub _remap_block_devices
 {
-    my ($meta, $virtio, $g, $root) = @_;
+    my ($meta, $virtio, $g, $root, $grub) = @_;
 
     my @devices = map { $_->{device} } @{$meta->{disks}};
     @devices = sort { scsi_first_cmp($a, $b) } @devices;
@@ -2214,8 +2561,8 @@ sub _remap_block_devices
     # libguestfs, which means their device name in the appliance can be
     # inferred.
 
-    # If the guest is using libata, IDE drives could have different names in 
the
-    # guest from their libvirt device names.
+    # If the guest is using libata, IDE drives could have different names in
+    # the guest from their libvirt device names.
 
     # Modern distros use libata, and IDE devices are presented as sdX
     my $libata = 1;
@@ -2235,6 +2582,12 @@ sub _remap_block_devices
     # Fedora has used libata since FC7, which is long out of support. We 
assume
     # that all Fedora distributions in use use libata.
 
+    # SUSE uses libata, but IDE devices can be presented as hdX in some
+    # environments (such as fully virtual machines).
+    if (_is_suse_family($g, $root)) {
+            $libata = 0;
+    }
+
     if ($libata) {
         # If there are any IDE devices, the guest will have named these sdX
         # after any SCSI devices. i.e. If we have disks hda, hdb, sda and 
sdb,
@@ -2280,8 +2633,8 @@ sub _remap_block_devices
         # these as xvd devices. i.e. hdX and xvdX both exist and are the same
         # device.
         # This mapping is also useful for P2V conversion of Citrix Xenserver
-        # guests done in HVM mode. Disks are detected as sdX, although the 
guest
-        # uses xvdX natively.
+        # guests done in HVM mode. Disks are detected as sdX, although the
+        # guest uses xvdX natively.
         if ($device =~ /^(?:h|s)d([a-z]+)/) {
             $map{'xvd'.$1} = $mapped;
         }
@@ -2290,13 +2643,46 @@ sub _remap_block_devices
     }
 
     eval {
-        # Update bare device references in fstab and grub's device.map
-        foreach my $spec ($g->aug_match('/files/etc/fstab/*/spec'),
-                          $g->aug_match('/files/boot/grub/device.map/*'.
-                                            '[label() != "#comment"]'))
+        my @checklist;
+        my @matchlist;
+        my $grub2_remap;
+
+        # Add standard configuration files to the checklist
+        push (@checklist, '/files/etc/fstab/*/spec');
+
+        # Add grub or grub2 files to the checklist
+        if (defined($grub->{grub_conf})) {
+            push (@checklist, "/files$grub->{grub_conf}*/kernel/root");
+            push (@checklist, "/files$grub->{grub_conf}*/kernel/resume");
+            push (@checklist, '/files/boot/grub/device.map/*'.
+                              '[label() != "#comment"]');
+        }
+        elsif (defined($grub->{cfg})) {
+            push (@checklist, '/files/etc/sysconfig/grub/GRUB_CMDLINE_LINUX');
+            push (@checklist, '/files/etc/default/grub/'.
+                              'GRUB_CMDLINE_LINUX_DEFAULT');
+        }
+
+        # Search through all checklist entries and add matches to a matchlist
+        foreach my $entry (@checklist) {
+            push (@matchlist, $g->aug_match($entry));
+        }
+
+        # Update device references for every entry in the matchlist
+        foreach my $spec (@matchlist)
         {
             my $device = $g->aug_get($spec);
 
+            # If this is a grub2 environment, isolate 'resume=' value
+            my $grub2_start;
+            my $grub2_end;
+            if (($spec =~ /.*GRUB_CMDLINE.*/) &&
+                    ($device =~ /(.*resume=)(\S+)(\s.*)/)) {
+                $grub2_start = $1;
+                $device = $2;
+                $grub2_end = $3;
+            }
+
             # Match device names and partition numbers
             my $name; my $part;
             foreach my $r (qr{^/dev/(cciss/c\d+d\d+)(?:p(\d+))?$},
@@ -2318,7 +2704,9 @@ sub _remap_block_devices
             # about. The user will have to fix this post-conversion.
             if (!exists($map{$name})) {
                 my $warned = 0;
-                for my $file ('/etc/fstab', '/boot/grub/device.map') {
+                for my $file ('/etc/fstab', '/boot/grub/device.map',
+                              '/boot/grub/menu.lst', '/etc/sysconfig/grub',
+                              '/etc/default/grub') {
                     if ($spec =~ m{^/files$file}) {
                         logmsg WARN, __x('{file} references unknown device '.
                                          '{device}. This entry must be '.
@@ -2341,10 +2729,22 @@ sub _remap_block_devices
 
             my $mapped = '/dev/'.$map{$name};
             $mapped .= $part if defined($part);
+
+            # If this is a grub2 entry, rebuild the entire entry
+            if ($spec =~ /.*GRUB_CMDLINE.*/) {
+                $mapped = $grub2_start.$mapped.$grub2_end;
+                $grub2_remap = 1;
+            }
+
             $g->aug_set($spec, $mapped);
         }
 
         $g->aug_save();
+
+        # Re-generate the grub2 config if grub2 files were changed
+        if (defined($grub2_remap)) {
+            $g->command(['grub2-mkconfig', '-o', $grub->{cfg}]);
+        }
     };
 
     augeas_error($g, $@) if ($@);
@@ -2372,6 +2772,12 @@ sub _prepare_bootable
                      $grub_initrd, $version]);
     }
 
+    elsif (_is_suse_family($g, $root) && ($g->exists('/sbin/mkinitrd'))) {
+        $g->sh('/sbin/mkinitrd -m "'.join(' ', @modules).'" '.
+               ' -i '.$grub_initrd.' -k /boot/vmlinuz-'.$version);
+    }
+
+    # Default to original mkinitrd, if not SUSE and dracut does not exist
     elsif ($g->exists('/sbin/mkinitrd')) {
         # Create a new initrd which probes the required kernel modules
         my @module_args = ();
@@ -2431,8 +2837,8 @@ sub _supports_acpi
 
     # Blacklist configurations which are known to fail
     # RHEL 3, x86_64
-    if (_is_rhel_family($g, $root) && $g->inspect_get_major_version($root) == 
3 &&
-        $arch eq 'x86_64') {
+    if (_is_rhel_family($g, $root) && $g->inspect_get_major_version($root) == 
3
+        && $arch eq 'x86_64') {
         return 0;
     }
 
@@ -2467,6 +2873,67 @@ sub _supports_virtio
     return 1;
 }
 
+sub _get_display_driver
+{
+    my ($g, $root) = @_;
+
+    if (_is_suse_family($g, $root)) {
+        return 'cirrus';
+    } else {
+        return 'qxl';
+    }
+}
+
+# RedHat and SUSE use different 32bit architectures (i686 -vs- i586)
+sub _set_32bit_arch
+{
+    my ($g, $root, $arch) = @_;
+
+    # We want an i586 or i686 guest for i[345]86
+    if ($arch =~ /^i[345]86$/) {
+        if (_is_sles_family($g, $root)) {
+            $arch = 'i586';
+        } else {
+            $arch = 'i686';
+        }
+    }
+
+    return $arch;
+}
+
+# The next two functions are a SUSE-specific temporary workaround to
+# bnc#836521. This is required to prevent root being set to (hd*) instead
+# of the correct (hd*,*). The actual fix is in a new perl-Bootloader, which
+# will likely not be on most guests.
+sub _modify_perlBootloader
+{
+    my ($g) = @_;
+    my $module = $g->sh('rpm -ql perl-Bootloader | grep GRUB.pm');
+    chomp($module);
+    my $module_bak = "$module.v2vtmp";
+
+    if ($g->grep('/dev/(?:vx', "$module")) {
+        $g->mv($module, $module_bak);
+        $g->sh("/usr/bin/sed -e's/vx/xv/' $module_bak > $module");
+
+        return 1;
+    }
+
+    return 0;
+}
+
+sub _restore_perlBootloader
+{
+    my ($g) = @_;
+    my $module = $g->sh('rpm -ql perl-Bootloader | grep GRUB.pm');
+    chomp($module);
+    my $module_bak = "$module.v2vtmp";
+
+    if ($g->exists($module_bak)) {
+        $g->mv($module_bak, $module);
+    }
+}
+
 =back
 
 =head1 COPYRIGHT
-- 
1.8.1.4





More information about the Libguestfs mailing list