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

Mike Latimer mlatimer at suse.com
Thu Oct 3 05:46:58 UTC 2013


This is a proposed patch which changes the RedHat.pm converter to Linux.pm,
and adds support for SUSE guest conversion. This is first approach recommended
by Matt Booth in:

https://www.redhat.com/archives/libguestfs/2013-September/msg00076.html

Some aspects of this patch still need additional testing, and a couple of
changes are not foolproof (such as the lack of grub2 support in the menu.lst
changes in _remap_block_devices). However, it is complete enough that I'd
like some feedback before continuing.

Note - This patch alone is not enough to support SUSE guests, as changes to
virt-v2v.db are also required. After these changes are flushed out, I'll post
all the patches in one thread.

-Mike

---
 lib/Sys/VirtConvert/Converter/Linux.pm  | 2880 +++++++++++++++++++++++++++++++
 lib/Sys/VirtConvert/Converter/RedHat.pm | 2489 --------------------------
 2 files changed, 2880 insertions(+), 2489 deletions(-)
 create mode 100644 lib/Sys/VirtConvert/Converter/Linux.pm
 delete mode 100644 lib/Sys/VirtConvert/Converter/RedHat.pm

diff --git a/lib/Sys/VirtConvert/Converter/Linux.pm b/lib/Sys/VirtConvert/Converter/Linux.pm
new file mode 100644
index 0000000..f3d13df
--- /dev/null
+++ b/lib/Sys/VirtConvert/Converter/Linux.pm
@@ -0,0 +1,2880 @@
+# 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
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+use strict;
+use warnings;
+
+
+# Functions supported by grubby or perl-Bootloader, and therefore common
+# between gruby legacy grub2
+package Sys::VirtConvert::Converter::Linux::Grub;
+
+use Sys::VirtConvert::Util;
+use Locale::TextDomain 'virt-v2v';
+
+sub get_initrd
+{
+    my $self = shift;
+    my ($path) = @_;
+    my $initrd;
+
+    my $g = $self->{g};
+
+    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 (hd*,*), remove it.
+            $initrd =~ s/^\(hd.*\)//;
+            return $initrd;
+        }
+    }
+
+    # If all else fails, use heuristics if the file exists
+    (($initrd = $path) =~ s/vmlinuz/initrd/) if (!defined($initrd));
+    if ($g->exists($initrd)) {
+        return $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 (hd*,*), 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;
+    my $g = $self->{g};
+
+    # Check the first partition of each device looking for an EFI boot
+    # partition. We can't be sure which device is the boot device, so we just
+    # check them all.
+    foreach my $device ($g->list_devices()) {
+        my $guid = eval { $g->part_get_gpt_type($device, 1) };
+        next unless defined($guid);
+
+        if ($guid eq 'C12A7328-F81F-11D2-BA4B-00A0C93EC93B') {
+            $self->convert_efi($device);
+            last;
+        }
+    }
+}
+
+
+# Methods for inspecting and manipulating grub legacy
+package Sys::VirtConvert::Converter::Linux::GrubLegacy;
+
+use Sys::VirtConvert::Util;
+
+use File::Basename;
+use Locale::TextDomain 'virt-v2v';
+
+ at Sys::VirtConvert::Converter::Linux::GrubLegacy::ISA =
+    qw(Sys::VirtConvert::Converter::Linux::Grub);
+
+sub new
+{
+    my $class = shift;
+    my ($g, $root) = @_;
+
+    my $self = {};
+    bless($self, $class);
+
+    $self->{g} = $g;
+    $self->{root} = $root;
+
+    # Look for an EFI configuration
+    foreach my $cfg ($g->glob_expand('/boot/efi/EFI/*/grub.conf')) {
+        $self->check_efi();
+    }
+
+    # Look for the grub configuration file
+    foreach my $path ('/boot/grub/menu.lst', '/boot/grub/grub.conf')
+    {
+        if ($g->exists($path)) {
+            $self->{grub_conf} = $path;
+            last;
+        }
+    }
+
+    # We can't continue without a config file
+    die unless defined ($self->{grub_conf});
+
+    # Find the path which needs to be prepended to paths in grub.conf to
+    # make them absolute
+    # Look for the most specific mount point discovered
+
+    # Default to / (no prefix required)
+    $self->{grub_fs} ||= "";
+
+    my %mounts = $g->inspect_get_mountpoints($root);
+    foreach my $path ('/boot/grub', '/boot') {
+        if (exists($mounts{$path})) {
+            $self->{grub_fs} = $path;
+            last;
+        }
+    }
+
+    # Initialise augeas
+    eval {
+        # Check grub_conf is included by the Grub lens
+        my $found = 0;
+        foreach my $incl ($g->aug_match("/augeas/load/Grub/incl")) {
+            if ($g->aug_get($incl) eq $self->{grub_conf}) {
+                $found = 1;
+                last;
+            }
+        }
+
+        # If it wasn't there, add it
+        unless ($found) {
+            $g->aug_set("/augeas/load/Grub/incl[last()+1]", $self->{grub_conf});
+
+            # Make augeas pick up the new configuration
+            $g->aug_load();
+        }
+    };
+    augeas_error($g, $@) if ($@);
+
+    return $self;
+}
+
+sub list_kernels
+{
+    my $self = shift;
+
+    my $g = $self->{g};
+    my $grub_conf = $self->{grub_conf};
+    my $grub_fs = $self->{grub_fs};
+
+    # Look for a kernel, starting with the default
+    my @paths = eval {
+        # Get a list of all kernels
+        my @ret = $g->aug_match("/files$grub_conf/title/kernel");
+
+        # Get the default kernel from grub if it's set
+        # Doesn't matter if there isn't one (aug_get will fail)
+        my $default = eval {
+            my $idx = $g->aug_get("/files$grub_conf/default");
+
+            # Grub indices are zero-based, augeas is 1-based
+            "/files$grub_conf/title[".($idx + 1)."]/kernel";
+        };
+
+        if (defined($default)) {
+            # Remove the default from the list and put it at the beginning
+            @ret = grep {!m{$default}} @ret;
+            unshift(@ret, $default);
+        }
+
+        @ret;
+    };
+    augeas_error($g, $@) if ($@);
+
+    my @kernels;
+    my %checked;
+    foreach my $path (@paths) {
+        next if $checked{$path};
+        $checked{$path} = 1;
+
+        my $kernel = eval { $g->aug_get($path) };
+        augeas_error($g, $@) if ($@);
+
+        # Prepend the grub filesystem to the kernel path
+        $kernel = "$grub_fs$kernel" if defined $grub_fs;
+
+        # Check the kernel exists
+        if ($g->exists($kernel)) {
+            push(@kernels, $kernel);
+        }
+
+        else {
+            logmsg WARN, __x('grub refers to {path}, which doesn\'t exist.',
+                             path => $kernel);
+        }
+    }
+
+    return @kernels;
+}
+
+sub update_console
+{
+    my $self = shift;
+    my ($remove) = @_;
+
+    my $g = $self->{g};
+    my $grub_conf = $self->{grub_conf};
+
+    eval {
+        # Update any kernel console lines
+        my $deleted = 0;
+        foreach my $augpath
+            ($g->aug_match("/files$grub_conf/title/kernel/console"))
+        {
+            my $console = $g->aug_get($augpath);
+            if ($console =~ /\b(x|h)vc0\b/) {
+                if ($remove) {
+                    $g->aug_defvar("delconsole$deleted", $augpath);
+                    $deleted++;
+                } else {
+                    $console =~ s/\b(x|h)vc0\b/ttyS0/g;
+                    $g->aug_set($augpath, $console);
+                }
+            }
+
+            if ($remove && $console =~ /\bttyS0\b/) {
+                $g->aug_rm($augpath);
+            }
+        }
+
+        for (my $i = 0; $i < $deleted; $i++) {
+            $g->aug_rm('$delconsole'.$i);
+        }
+    };
+    augeas_error($g, $@) if ($@);
+}
+
+sub check
+{
+    my $self = shift;
+    my ($path, $root) = @_;
+
+    my $g = $self->{g};
+    my $grub_conf = $self->{grub_conf};
+    my $grub_fs   = $self->{grub_fs};
+    $path =~ /^$grub_fs(.*)/
+       or v2vdie __x('Kernel {kernel} is not under grub tree {grub}',
+                     kernel => $path, grub => $grub_fs);
+    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']");
+    return if scalar(@entries) > 0;
+
+    my $kernel =
+        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/(distro)-release can't be read
+    my ($title) = eval { $g->inspect_get_product_name($root) };
+    $title ||= 'Linux';
+
+    # Remove codename or architecture
+    $title =~ s/ \(.*\)//;
+    # Remove release string and add version (like new-kernel-pkg)
+    $title =~ s/ release.*//;
+    $title .= " ($version)";
+
+    # Doesn't matter if there's no default
+    my $default = eval { $g->aug_get("/files$grub_conf/default") };
+
+    if (defined($default)) {
+        $g->aug_defvar('template',
+             "/files$grub_conf/title[".($default + 1).']');
+    }
+
+    # If there's no default, take the first entry with a kernel
+    else {
+        my ($match) = $g->aug_match("/files$grub_conf/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$grub_conf/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', $grub_path);
+    $g->aug_set('$new/initrd', $grub_initrd);
+
+    # Copy all kernel command-line arguments
+    foreach my $arg ($g->aug_match('$template/kernel/*')) {
+        # kernel arguments don't necessarily have values
+        my $val = eval { $g->aug_get($arg) };
+
+        $arg =~ /([^\/]*)$/;
+        $arg = $1;
+
+        if (defined($val)) {
+            $g->aug_set('$new/kernel/'.$arg, $val);
+        } else {
+            $g->aug_clear('$new/kernel/'.$arg);
+        }
+    }
+}
+
+sub write
+{
+    my $self = shift;
+    my ($path) = @_;
+
+    my $g = $self->{g};
+    my $grub_conf = $self->{grub_conf};
+    my $grub_fs   = $self->{grub_fs};
+
+    $path =~ /^$grub_fs(.*)/
+       or v2vdie __x('Kernel {kernel} is not under grub tree {grub}',
+                     kernel => $path, grub => $grub_fs);
+    my $grub_path = $1;
+
+    # Find the grub entry for the given kernel
+    eval {
+        my ($aug_path) =
+            $g->aug_match("/files$grub_conf/title/kernel[. = '$grub_path']");
+
+        v2vdie __x('Didn\'t find grub entry for kernel {kernel}',
+                   kernel => $path)
+            unless defined($aug_path);
+
+        $aug_path =~ m{/files$grub_conf/title(?:\[(\d+)\])?/kernel}
+            or die($aug_path);
+        my $grub_index = defined($1) ? $1 - 1 : 0;
+
+        $g->aug_set("/files$grub_conf/default", $grub_index);
+        $g->aug_save();
+    };
+    augeas_error($g, $@) if ($@);
+}
+
+# For Grub legacy, all we have to do is re-install grub in the correct place.
+sub convert_efi
+{
+    my $self = shift;
+    my ($device) = @_;
+
+    my $g = $self->{g};
+
+    $g->cp('/etc/grub.conf', '/boot/grub/grub.conf');
+    $g->ln_sf('/boot/grub/grub.conf', '/etc/grub.conf');
+
+    # Reload to pick up grub.conf in its new location
+    eval { $g->aug_load() };
+    augeas_error($g, $@) if ($@);
+
+    $g->command(['grub-install', $device]);
+}
+
+
+# Methods for inspecting and manipulating grub2. Note that we don't actually
+# 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::Linux::Grub2;
+
+use Sys::VirtConvert::Util qw(:DEFAULT augeas_error);
+use Locale::TextDomain 'virt-v2v';
+
+ at Sys::VirtConvert::Converter::Linux::Grub2::ISA =
+    qw(Sys::VirtConvert::Converter::Linux::Grub);
+
+sub new
+{
+    my $class = shift;
+    my ($g, $root, $config) = @_;
+
+    my $self = {};
+    bless($self, $class);
+
+    $self->{g} = $g;
+    $self->{root} = $root;
+    $self->{config} = $config;
+
+    # Look for an EFI configuration
+    foreach my $cfg ($g->glob_expand('/boot/efi/EFI/*/grub.cfg')) {
+        $self->check_efi();
+    }
+
+    # Check we have a grub2 configuration
+    if ($g->exists('/boot/grub2/grub.cfg')) {
+        $self->{cfg} = '/boot/grub2/grub.cfg';
+    }
+    die unless exists $self->{cfg};
+
+    return $self;
+}
+
+sub list_kernels
+{
+    my $self = shift;
+    my $g = $self->{g};
+
+    my @kernels;
+
+    # Start by adding the default kernel
+    my $default = $self->get_default_image();
+    push(@kernels, $default) if length($default) > 0;
+
+    # This is how the grub2 config generator enumerates kernels
+    foreach my $kernel ($g->glob_expand('/boot/kernel-*'),
+                        $g->glob_expand('/boot/vmlinuz-*'),
+                        $g->glob_expand('/vmlinuz-*'))
+    {
+        push(@kernels, $kernel)
+           unless $kernel =~ /\.(?:dpkg-.*|rpmsave|rpmnew)$/;
+    }
+
+    return @kernels;
+}
+
+sub update_console
+{
+    my $self = shift;
+    my ($remove) = @_;
+
+    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($grub_cmdline) };
+
+    if (defined($cmdline) && $cmdline =~ /\bconsole=(?:x|h)vc0\b/) {
+        if ($remove) {
+            $cmdline =~ s/\bconsole=(?:x|h)vc0\b\s*//g;
+        } else {
+            $cmdline =~ s/\bconsole=(?:x|h)vc0\b/console=ttyS0/g;
+        }
+
+        eval {
+            $g->aug_set($grub_cmdline, $cmdline);
+            $g->aug_save();
+        };
+        augeas_error($g, $@) if ($@);
+
+        # We need to re-generate the grub config if we've updated this file
+        $g->command(['grub2-mkconfig', '-o', $self->{cfg}]);
+    }
+}
+
+sub check
+{
+    # Nothing required for grub2
+}
+
+sub write
+{
+    my $self = shift;
+    my ($path) = @_;
+
+    my $g = $self->{g};
+
+    my $default = $self->get_default_image();
+
+    if ($default ne $path) {
+        eval { $self->set_default_image($path) };
+        if ($@) {
+            logmsg WARN, __x('Unable to set default kernel to {path}',
+                             path => $path);
+        }
+    }
+}
+
+# For grub2, we :
+#   Turn the EFI partition into a BIOS Boot Partition
+#   Remove the former EFI partition from fstab
+#   Install the non-EFI version of grub
+#   Install grub2 in the BIOS Boot Partition
+#   Regenerate grub.cfg
+sub convert_efi
+{
+    my $self = shift;
+    my ($device) = @_;
+
+    my $g = $self->{g};
+
+    # EFI systems boot using grub2-efi, and probably don't have the base grub2
+    # package installed.
+    Sys::VirtConvert::Convert::Linux::_install_any
+        (undef, ['grub2'], undef, $g, $self->{root}, $self->{config}, $self)
+        or v2vdie __x('Failed to install non-EFI grub2');
+
+    # Relabel the EFI boot partition as a BIOS boot partition
+    $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']")) {
+        $g->aug_rm($node);
+    }
+    eval { $g->aug_save(); };
+    augeas_error($g, $@) if $@;
+
+    # Install grub2 in the BIOS beot partition. This overwrites the previous
+    # contents of the EFI boot partition.
+    $g->command(['grub2-install', $device]);
+
+    # Re-generate the grub2 config, and put it in the correct place
+    $g->command(['grub2-mkconfig', '-o', '/boot/grub2/grub.cfg']);
+}
+
+
+package Sys::VirtConvert::Converter::Linux;
+
+use Sys::VirtConvert::Util qw(:DEFAULT augeas_error scsi_first_cmp);
+use Locale::TextDomain 'virt-v2v';
+
+=pod
+
+=head1 NAME
+
+Sys::VirtConvert::Converter::Linux - Convert a Linux based guest to run on KVM
+
+=head1 SYNOPSIS
+
+ use Sys::VirtConvert::Converter;
+
+ Sys::VirtConvert::Converter->convert($g, $root, $meta);
+
+=head1 DESCRIPTION
+
+Sys::VirtConvert::Converter::Linux converts a Linux based guest to use KVM.
+
+=head1 METHODS
+
+=over
+
+=cut
+
+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)$/);
+}
+
+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::Linux->can_handle(g, root)
+
+Return 1 if Sys::VirtConvert::Converter::Linux can convert the given guest
+
+=cut
+
+sub can_handle
+{
+    my $class = shift;
+
+    my ($g, $root) = @_;
+
+    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::Linux->convert(g, root, config, meta, options)
+
+Convert a Linux based guest. Assume that can_handle has previously returned 1.
+
+=over
+
+=item g
+
+An initialised Sys::Guestfs handle
+
+=item root
+
+The root device of this operating system.
+
+=item config
+
+An initialised Sys::VirtConvert::Config
+
+=item meta
+
+Guest metadata.
+
+=item options
+
+A hashref of options which can influence the conversion
+
+=back
+
+=cut
+
+sub convert
+{
+    my $class = shift;
+
+    my ($g, $root, $config, $meta, $options) = @_;
+
+    _clean_rpmdb($g);
+    _init_selinux($g);
+    _init_augeas($g);
+
+    my $grub;
+    $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);
+
+    # Un-configure HV specific attributes which don't require a direct
+    # replacement
+    _unconfigure_hv($g, $root);
+
+    # Try to install the virtio capability
+    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)) {
+        logmsg WARN, __('Failed to install user-custom packages');
+    }
+
+    # Configure the rest of the system
+    my $remove_serial_console = exists($options->{NO_SERIAL_CONSOLE});
+    _configure_console($g, $grub, $remove_serial_console);
+
+    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);
+
+    my %guestcaps;
+
+    $guestcaps{block} = $virtio == 1 ? 'virtio' : 'ide';
+    $guestcaps{net}   = $virtio == 1 ? 'virtio' : 'e1000';
+    $guestcaps{arch}  = _get_os_arch($g, $root, $grub);
+    $guestcaps{acpi}  = _supports_acpi($g, $root, $guestcaps{arch});
+
+    return \%guestcaps;
+}
+
+sub _init_selinux
+{
+    my ($g) = @_;
+
+    # 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.
+    $g->touch('/.autorelabel');
+}
+
+sub _init_augeas
+{
+    my ($g) = @_;
+
+    # Initialise augeas
+    eval { $g->aug_init("/", 1) };
+    augeas_error($g, $@) if ($@);
+}
+
+# Execute an augeas modprobe query against all possible modprobe locations
+sub _aug_modprobe
+{
+    my ($g, $query) = @_;
+
+    my @paths;
+    for my $pattern ('/files/etc/conf.modules/alias',
+                     '/files/etc/modules.conf/alias',
+                     '/files/etc/modprobe.conf/alias',
+                     '/files/etc/modprobe.d/*/alias') {
+        push(@paths, $g->aug_match($pattern.'['.$query.']'));
+    }
+
+    return @paths;
+}
+
+# Check how new modules should be configured. Possibilities, in descending
+# order of preference, are:
+#   modprobe.d/
+#   modprobe.conf
+#   modules.conf
+#   conf.modules
+sub _discover_modpath
+{
+    my ($g) = @_;
+
+    my $modpath;
+
+    # 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)) {
+            $modpath = $file;
+        }
+    }
+
+    if($g->exists("/etc/modprobe.conf")) {
+        $modpath = "modprobe.conf";
+    }
+
+    # If the modprobe.d directory exists, create new entries in
+    # modprobe.d/virtv2v-added.conf
+    if($g->exists("/etc/modprobe.d")) {
+        $modpath = "modprobe.d/virtv2v-added.conf";
+    }
+
+    v2vdie __('Unable to find any valid modprobe configuration')
+        unless defined($modpath);
+
+    return $modpath;
+}
+
+sub _configure_kernel_modules
+{
+    my ($g, $virtio, $modpath) = @_;
+
+    # Make a note of whether we've added scsi_hostadapter
+    # We need this on RHEL 4/virtio because mkinitrd can't detect root on
+    # virtio. For simplicity we always ensure this is set for virtio disks.
+    my $scsi_hostadapter = 0;
+
+    eval {
+        foreach my $path (_aug_modprobe($g, ". =~ regexp('eth[0-9]+')"))
+        {
+            $g->aug_set($path.'/modulename',
+                        $virtio == 1 ? 'virtio_net' : 'e1000');
+        }
+
+        my @paths = _aug_modprobe($g, ". =~ regexp('scsi_hostadapter.*')");
+        if ($virtio) {
+            # There's only 1 scsi controller in the converted guest.
+            # Convert only the first scsi_hostadapter entry to virtio.
+
+            # Note that we delete paths in reverse order. This means we don't
+            # have to worry about alias indices being changed.
+            while (@paths > 1) {
+                $g->aug_rm(pop(@paths));
+            }
+
+            if (@paths == 1) {
+                $g->aug_set(pop(@paths).'/modulename', 'virtio_blk');
+                $scsi_hostadapter = 1;
+            }
+        }
+
+        else {
+            # There's no scsi controller in an IDE guest
+            while (@paths) {
+                $g->aug_rm(pop(@paths));
+            }
+        }
+
+        # Display a warning about any leftover xen modules which we haven't
+        # converted
+        my @xen_modules = qw(xennet xen-vnif xenblk xen-vbd);
+        my $query = '('.join('|', @xen_modules).')';
+
+        foreach my $path (_aug_modprobe($g, "modulename =~ regexp('$query')")) {
+            my $device = $g->aug_get($path);
+            my $module = $g->aug_get($path.'/modulename');
+
+            logmsg WARN, __x("Don't know how to update ".
+                             '{device}, which loads the {module} module.',
+                             device => $device, module => $module);
+        }
+
+        # Add an explicit scsi_hostadapter if it wasn't there before
+        if ($virtio && !$scsi_hostadapter) {
+            my $modpath = _discover_modpath($g);
+
+            $g->aug_set("/files$modpath/alias[last()+1]",
+                        'scsi_hostadapter');
+            $g->aug_set("/files$modpath/alias[last()]/modulename",
+                        'virtio_blk');
+        }
+
+        $g->aug_save();
+    };
+    augeas_error($g, $@) if $@;
+}
+
+# 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.
+#
+# If the target doesn't support a serial console, we want to remove all
+# references to it instead.
+sub _configure_console
+{
+    my ($g, $grub, $remove) = @_;
+
+    # Look for gettys which use xvc0 or hvc0
+    # RHEL 6 doesn't use /etc/inittab, but this doesn't hurt
+    foreach my $augpath ($g->aug_match("/files/etc/inittab/*/process")) {
+        my $proc = $g->aug_get($augpath);
+
+        # If the process mentions xvc0, change it to ttyS0
+        if ($proc =~ /\b(x|h)vc0\b/) {
+            if ($remove) {
+                $g->aug_rm($augpath.'/..');
+            } else {
+                $proc =~ s/\b(x|h)vc0\b/ttyS0/g;
+                $g->aug_set($augpath, $proc);
+            }
+        }
+
+        if ($remove && $proc =~ /\bttyS0\b/) {
+            $g->aug_rm($augpath.'/..');
+        }
+    }
+
+    # Replace any mention of xvc0 or hvc0 in /etc/securetty with ttyS0
+    foreach my $augpath ($g->aug_match('/files/etc/securetty/*')) {
+        my $tty = $g->aug_get($augpath);
+
+        if($tty eq "xvc0" || $tty eq "hvc0") {
+            if ($remove) {
+                $g->aug_rm($augpath);
+            } else {
+                $g->aug_set($augpath, 'ttyS0');
+            }
+        }
+
+        if ($remove && $tty eq 'ttyS0') {
+            $g->aug_rm($augpath);
+        }
+    }
+
+    $grub->update_console($remove);
+
+    eval { $g->aug_save() };
+    augeas_error($g, $@) if ($@);
+}
+
+sub _configure_display_driver
+{
+    my ($g, $root, $config, $meta, $grub, $driver) = @_;
+
+    # Update the display driver if it exists
+    my $updated = 0;
+    eval {
+        my $xorg;
+
+        # Check which X configuration we have, and make augeas load it if
+        # necessary
+        if (! $g->exists('/etc/X11/xorg.conf') &&
+            $g->exists('/etc/X11/XF86Config'))
+        {
+            $g->aug_set('/augeas/load/Xorg/incl[last()+1]',
+                        '/etc/X11/XF86Config');
+
+            # Reload to pick up the new configuration
+            $g->aug_load();
+
+            $xorg = '/etc/X11/XF86Config';
+        } else {
+            $xorg = '/etc/X11/xorg.conf';
+        }
+
+        foreach my $path ($g->aug_match('/files'.$xorg.'/Device/Driver')) {
+            $g->aug_set($path, $driver);
+            $updated = 1;
+        }
+
+        # Remove VendorName and BoardName if present
+        foreach my $path
+            ($g->aug_match('/files'.$xorg.'/Device/VendorName'),
+             $g->aug_match('/files'.$xorg.'/Device/BoardName'))
+        {
+            $g->aug_rm($path);
+        }
+
+        $g->aug_save();
+    };
+
+    # 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 specified driver is installed.
+    if ($updated &&
+        ($g->exists('/usr/bin/X') || $g->exists('/usr/bin/X11/X')) &&
+        !_install_capability($driver, $g, $root, $config, $meta, $grub))
+    {
+        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.
+sub _clean_rpmdb
+{
+    my $g = shift;
+
+    foreach my $f ($g->glob_expand('/var/lib/rpm/__db.00?')) {
+        $g->rm($f);
+    }
+}
+
+# Use various methods to try to work out what Linux kernel we've got.
+# Returns a hashref containing:
+#   path => path to kernel (same as $path variable passed in)
+#   package => base package name (eg. "kernel", "kernel-PAE")
+#   version => version string
+#   modules => array ref list of modules (paths to *.ko files)
+#   arch => architecture of the kernel
+sub _inspect_linux_kernel
+{
+    my ($g, $path) = @_;
+
+    my %kernel = ();
+
+    $kernel{path} = $path;
+
+    # 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);;
+
+    # 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/) {
+        $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 {
+        if($path =~ m{/boot/vmlinuz-(.*)}) {
+            $version = $1;
+
+            # Check /lib/modules/$version exists
+            if(!$g->is_dir("/lib/modules/$version")) {
+                warn __x("Didn't find modules directory {modules} for kernel ".
+                         "{path}\n", modules => "/lib/modules/$version",
+                         path => $path);
+
+                # Give up
+                return undef;
+            }
+        } else {
+            warn __x("Couldn't guess kernel version number from path for ".
+                     "kernel {path}\n", path => $path);
+
+            # Give up
+            return undef;
+        }
+    }
+
+    $kernel{version} = $version;
+
+    # List modules.
+    my @modules;
+    my $any_module;
+    my $prefix = "/lib/modules/$version";
+    foreach my $module ($g->find ($prefix)) {
+        if ($module =~ m{/([^/]+)\.(?:ko|o)$}) {
+            $any_module = "$prefix$module" unless defined $any_module;
+            push @modules, $1;
+        }
+    }
+
+    $kernel{modules} = \@modules;
+
+    # Determine kernel architecture by looking at the arch
+    # of any kernel module.
+    $kernel{arch} = $g->file_architecture ($any_module);
+
+    return \%kernel;
+}
+
+sub _configure_kernel
+{
+    my ($virtio, $g, $root, $config, $meta, $grub) = @_;
+
+    # Pick first appropriate kernel returned by list_kernels
+    my $boot_kernel;
+    foreach my $path ($grub->list_kernels()) {
+        my $kernel = _inspect_linux_kernel($g, $path);
+        my $version = $kernel->{version};
+
+        # Skip foreign kernels
+        next if _is_hv_kernel($g, $version);
+
+        # If we're configuring virtio, check this kernel supports it
+        next if ($virtio && !_supports_virtio($version, $g));
+
+        $boot_kernel = $version;
+        last;
+    }
+
+    # Warn if there is no installed virtio capable kernel. It is safe to
+    # continue and attempt to install a virtio kernel next.
+    logmsg NOTICE, __x('virtio capable guest, but no virtio kernel found.')
+        if ($virtio && !defined($boot_kernel));
+
+    # If none of the installed kernels are appropriate, install a new one
+    if(!defined($boot_kernel)) {
+        my ($kernel_pkg, $kernel_arch, undef) =
+           _discover_kernel($g, $root, $grub);
+
+        # If the guest is using a Xen PV kernel, choose an appropriate
+        # normal kernel replacement
+        if ($kernel_pkg =~ /^kernel-xen/) {
+            $kernel_pkg = _get_replacement_kernel_name($g, $root,
+                                                       $kernel_arch, $meta);
+
+            # Check there isn't already one installed
+            my ($kernel) = _get_installed("$kernel_pkg.$kernel_arch", $g);
+            $boot_kernel = $kernel->[1].'-'.$kernel->[2].'.'.$kernel_arch
+                if defined($kernel);
+        }
+
+        if (!defined($boot_kernel)) {
+            # List of kernels before the new kernel installation
+            my @k_before = $g->glob_expand('/boot/vmlinuz-*');
+
+            if (_install_any([$kernel_pkg, $kernel_arch], undef, undef,
+                             $g, $root, $config, $grub))
+            {
+                # Figure out which kernel has just been installed
+                my $version;
+                foreach my $k ($g->glob_expand('/boot/vmlinuz-*')) {
+                    if (!grep(/^$k$/, @k_before)) {
+                        # Check which directory in /lib/modules the kernel rpm
+                        # creates
+                        foreach my $file
+                                ($g->command_lines(['rpm', '-qlf', $k]))
+                        {
+                            next unless ($file =~ m{^/lib/modules/([^/]+)$});
+
+                            if ($g->is_dir("/lib/modules/$1")) {
+                                $boot_kernel = $1;
+                                last;
+                            }
+                        }
+
+                        last if defined($boot_kernel);
+                    }
+                }
+
+                v2vdie __x('Couldn\'t determine version of installed kernel')
+                    unless defined($boot_kernel);
+            } else {
+                v2vdie __x('Failed to find a {name} package to install',
+                           name => "$kernel_pkg.$kernel_arch");
+            }
+        }
+    }
+
+    # Check we have a bootable kernel.
+    v2vdie __('No bootable kernels installed, and no replacement '.
+              "is available.\nUnable to continue.")
+        unless defined($boot_kernel);
+
+    # Ensure DEFAULTKERNEL is set to boot kernel package name
+    # It's not fatal if this rpm command fails
+    my ($kernel_pkg) =
+        eval { $g->command_lines(['rpm', '-qf', "/lib/modules/$boot_kernel",
+                                         '--qf', '%{NAME}\n']) };
+    if (defined($kernel_pkg) && $g->exists('/etc/sysconfig/kernel')) {
+        eval {
+            foreach my $path ($g->aug_match('/files/etc/sysconfig/kernel'.
+                                            '/DEFAULTKERNEL/value'))
+            {
+                $g->aug_set($path, $kernel_pkg);
+            }
+
+            $g->aug_save();
+        };
+        # Propagate augeas errors
+        augeas_error($g, $@) if ($@);
+    }
+
+    return $boot_kernel;
+}
+
+sub _configure_boot
+{
+    my ($kernel, $virtio, $g, $root, $grub) = @_;
+
+    if($virtio) {
+        # The order of modules here is deliberately the same as the order
+        # specified in the postinstall script of kmod-virtio in RHEL3. The
+        # 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");
+    } else {
+        _prepare_bootable($g, $root, $grub, $kernel, "sym53c8xx");
+    }
+}
+
+# Get the target architecture from the default boot kernel
+sub _get_os_arch
+{
+    my ($g, $root, $grub) = @_;
+
+    # Get the arch of the default kernel
+    my @kernels = $grub->list_kernels();
+    my $path = $kernels[0] if @kernels > 0;
+    my $kernel = _inspect_linux_kernel($g, $path) if defined($path);
+    my $arch = $kernel->{arch} if defined($kernel);
+
+    # Use the libguestfs-detected arch if the above failed
+    $arch = $g->inspect_get_arch($root) unless defined($arch);
+
+    # Default to x86_64 if we still didn't find an architecture
+    return 'x86_64' unless defined($arch);
+
+    # Change i386 to i[56]86
+    $arch = _set_32bit_arch($g, $root, $arch);
+
+    return $arch;
+}
+
+# Determine if a specific kernel is hypervisor-specific
+sub _is_hv_kernel
+{
+    my ($g, $version) = @_;
+
+    # Xen PV kernels can be distinguished from other kernels by their inclusion
+    # of the xennet driver
+    foreach my $entry ($g->find("/lib/modules/$version/")) {
+        return 1 if $entry =~ /(^|\/)xennet\.k?o$/;
+    }
+
+    return 0;
+}
+
+sub _remove_applications
+{
+    my ($g, @apps) = @_;
+
+    # Nothing to do if we were given an empty list
+    return if scalar(@apps) == 0;
+
+    $g->command(['rpm', '-e', @apps]);
+
+    # Make augeas reload in case the removal changed anything
+    eval { $g->aug_load() };
+    augeas_error($g, $@) if ($@);
+}
+
+sub _get_application_owner
+{
+    my ($file, $g) = @_;
+
+    return $g->command(['rpm', '-qf', $file]);
+}
+
+sub _unconfigure_hv
+{
+    my ($g, $root) = @_;
+
+    my @apps = $g->inspect_list_applications($root);
+
+    _unconfigure_xen($g, $root, \@apps);
+    _unconfigure_vbox($g, \@apps);
+    _unconfigure_vmware($g, \@apps);
+    _unconfigure_citrix($g, \@apps);
+}
+
+# Unconfigure Xen specific guest modifications
+sub _unconfigure_xen
+{
+    my ($g, $root, $apps) = @_;
+
+    # Look for kmod-xenpv-*, which can be found on RHEL 3 machines
+    my @remove;
+    foreach my $app (@$apps) {
+        my $name = $app->{app_name};
+
+        if($name =~ /^kmod-xenpv(-.*)?$/) {
+            push(@remove, $name);
+        }
+    }
+    _remove_applications($g, @remove);
+
+    # Undo related nastiness if kmod-xenpv was installed
+    if(scalar(@remove) > 0) {
+        # kmod-xenpv modules may have been manually copied to other kernels.
+        # Hunt them down and destroy them.
+        foreach my $dir (grep(m{/xenpv$}, $g->find('/lib/modules'))) {
+            $dir = '/lib/modules/'.$dir;
+
+            # Check it's a directory
+            next unless($g->is_dir($dir));
+
+            # Check it's not owned by an installed application
+            eval { _get_application_owner($dir, $g) };
+
+            # Remove it if _get_application_owner didn't find an owner
+            if($@) {
+                $g->rm_rf($dir);
+            }
+        }
+
+        # rc.local may contain an insmod or modprobe of the xen-vbd driver
+        my @rc_local = eval { $g->read_lines('/etc/rc.local') };
+        if ($@) {
+            logmsg WARN, __x('Unable to open /etc/rc.local: {error}',
+                             error => $@);
+        }
+
+        else {
+            my $size = 0;
+
+            foreach my $line (@rc_local) {
+                if($line =~ /\b(insmod|modprobe)\b.*\bxen-vbd/) {
+                    $line = '#'.$line;
+                }
+
+                $size += length($line) + 1;
+            }
+
+            $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
+sub _unconfigure_vbox
+{
+    my ($g, $apps) = @_;
+
+    # Uninstall VirtualBox Guest Additions
+    my @remove;
+    foreach my $app (@$apps) {
+        my $name = $app->{app_name};
+
+        if ($name eq "virtualbox-guest-additions") {
+            push(@remove, $name);
+        }
+    }
+    _remove_applications($g, @remove);
+
+    # VirtualBox Guest Additions may have been installed from tarball, in which
+    # case the above won't detect it. Look for the uninstall tool, and run it
+    # if it's present.
+    #
+    # Note that it's important we do this early in the conversion process, as
+    # this uninstallation script naively overwrites configuration files with
+    # versions it cached prior to installation.
+    my $vboxconfig = '/var/lib/VBoxGuestAdditions/config';
+    if ($g->exists($vboxconfig)) {
+        my $vboxuninstall;
+        foreach (split /\n/, $g->cat($vboxconfig)) {
+            if ($_ =~ /^INSTALL_DIR=(.*$)/) {
+                $vboxuninstall = $1 . '/uninstall.sh';
+            }
+        }
+        if ($g->exists($vboxuninstall)) {
+            eval { $g->command([$vboxuninstall]) };
+            logmsg WARN, __x('VirtualBox Guest Additions were detected, but '.
+                             'uninstallation failed. The error message was: '.
+                             '{error}', error => $@) if $@;
+
+            # Reload augeas to detect changes made by vbox tools uninstallation
+            eval { $g->aug_load() };
+            augeas_error($g, $@) if $@;
+        }
+    }
+}
+
+# Unconfigure VMware specific guest modifications
+sub _unconfigure_vmware
+{
+    my ($g, $apps) = @_;
+
+    # Look for any configured vmware yum repos, and disable them
+    foreach my $repo ($g->aug_match(
+        '/files/etc/yum.repos.d/*/*'.
+        '[baseurl =~ regexp(\'https?://([^/]+\.)?vmware\.com/.*\')]'))
+    {
+        eval {
+            $g->aug_set($repo.'/enabled', 0);
+            $g->aug_save();
+        };
+        augeas_error($g, $@) if ($@);
+    }
+
+    # Uninstall VMwareTools
+    my @remove;
+    my @libraries;
+    foreach my $app (@$apps) {
+        my $name = $app->{app_name};
+
+        if ($name =~ /^vmware-tools-/) {
+            if ($name =~ /^vmware-tools-libraries-/) {
+                push(@libraries, $name);
+            } else {
+                push (@remove, $name);
+            }
+        }
+        elsif ($name eq "VMwareTools" || $name =~ /^(?:kmod-)?vmware-tools-/) {
+            push(@remove, $name);
+        }
+    }
+
+    # 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
+    if (@libraries > 0) {
+        # We only support removal of these libraries packages on systems which
+        # use yum.
+        if ($g->exists('/usr/bin/yum')) {
+            _net_run($g, sub {
+                foreach my $library (@libraries) {
+                    eval {
+                        my @provides = $g->command_lines
+                                (['rpm', '-q', '--provides', $library]);
+
+                        # The packages also explicitly provide themselves.
+                        # Filter this out.
+                        @provides = grep {$_ !~ /$library/}
+
+                        # Trim whitespace
+                                    map { s/^\s*(\S+)\s*$/$1/; $_ } @provides;
+
+                        # Install the dependencies with yum. We use yum
+                        # explicitly here, as up2date wouldn't work anyway and
+                        # local install is impractical due to the large number
+                        # of required dependencies out of our control.
+                        my %alts;
+                        foreach my $alt ($g->command_lines
+                                    (['yum', '-q', 'resolvedep', @provides]))
+                        {
+                            $alts{$alt} = 1;
+                        }
+
+                        my @replacements = keys(%alts);
+                        $g->command(['yum', 'install', '-y', @replacements])
+                            if @replacements > 0;
+
+                        push(@remove, $library);
+                    };
+                    logmsg WARN, __x('Failed to install replacement '.
+                                     'dependencies for {lib}. Package will '.
+                                     'not be uninstalled. Error was: {error}',
+                                     lib => $library, error => $@) if $@;
+                }
+            });
+        }
+    }
+
+    _remove_applications($g, @remove);
+
+    # VMwareTools may have been installed from tarball, in which case the above
+    # won't detect it. Look for the uninstall tool, and run it if it's present.
+    #
+    # Note that it's important we do this early in the conversion process, as
+    # this uninstallation script naively overwrites configuration files with
+    # versions it cached prior to installation.
+    my $vmwaretools = '/usr/bin/vmware-uninstall-tools.pl';
+    if ($g->exists($vmwaretools)) {
+        eval { $g->command([$vmwaretools]) };
+        logmsg WARN, __x('VMware Tools was detected, but uninstallation '.
+                         'failed. The error message was: {error}',
+                         error => $@) if $@;
+
+        # Reload augeas to detect changes made by vmware tools uninstallation
+        eval { $g->aug_load() };
+        augeas_error($g, $@) if $@;
+    }
+}
+
+sub _unconfigure_citrix
+{
+    my ($g, $apps) = @_;
+
+    # Look for xe-guest-utilities*
+    my @remove;
+    foreach my $app (@$apps) {
+        my $name = $app->{app_name};
+
+        if($name =~ /^xe-guest-utilities(-.*)?$/) {
+            push(@remove, $name);
+        }
+    }
+    _remove_applications($g, @remove);
+
+    # Installing these guest utilities automatically unconfigures ttys in
+    # /etc/inittab if the system uses it. We need to put them back.
+    if (scalar(@remove) > 0) {
+        eval {
+            my $updated = 0;
+            for my $commentp ($g->aug_match('/files/etc/inittab/#comment')) {
+                my $comment = $g->aug_get($commentp);
+
+                # 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.
+                next unless $comment =~ /^([1-6]):([2-5]+):respawn:(.*)/;
+
+                my $name = $1;
+                my $runlevels = $2;
+                my $process = $3;
+
+                next unless $process =~ /getty/;
+
+                # Create a new entry immediately after the comment
+                $g->aug_insert($commentp, $name, 0);
+                $g->aug_set("/files/etc/inittab/$name/runlevels", $runlevels);
+                $g->aug_set("/files/etc/inittab/$name/action", 'respawn');
+                $g->aug_set("/files/etc/inittab/$name/process", $process);
+
+                # Create a variable to point to the comment node so we can
+                # delete it later. If we deleted it here it would invalidate
+                # subsquent comment paths returned by aug_match.
+                $g->aug_defvar("delete$updated", $commentp);
+
+                $updated++;
+            }
+
+            # Delete all the comments
+            my $i = 0;
+            while ($i < $updated) {
+                $g->aug_rm("\$delete$i");
+                $i++;
+            }
+
+            $g->aug_save();
+        };
+        augeas_error($g, $@) if ($@);
+    }
+}
+
+sub _install_capability
+{
+    my ($name, $g, $root, $config, $meta, $grub) = @_;
+
+    my $cap = eval { $config->match_capability($g, $root, $name) };
+    if ($@) {
+        warn($@);
+        return 0;
+    }
+
+    if (!defined($cap)) {
+        logmsg WARN, __x('{name} capability not found in configuration',
+                         name => $name);
+        return 0;
+    }
+
+    my @install;
+    my @upgrade;
+    my $kernel;
+    foreach my $name (keys(%$cap)) {
+        my $props = $cap->{$name};
+        my $ifinstalled = $props->{ifinstalled};
+
+        # Parse epoch, version and release from minversion
+        my ($min_epoch, $min_version, $min_release);
+        if (exists($props->{minversion})) {
+            eval {
+                ($min_epoch, $min_version, $min_release) =
+                    _parse_evr($props->{minversion});
+            };
+            v2vdie __x('Unrecognised format for {field} in config: '.
+                       '{value}. {field} must be in the format '.
+                       '[epoch:]version[-release].',
+                       field => 'minversion', value => $props->{minversion})
+                if $@;
+        }
+
+        # Kernels are special
+        if ($name eq 'kernel') {
+            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 (!defined($kernel_rpmver)) {
+                $kernel = [$kernel_pkg, $kernel_arch];
+            }
+
+            else {
+                my ($kernel_epoch, $kernel_ver, $kernel_release) =
+                    eval { _parse_evr($kernel_rpmver) };
+                if ($@) {
+                    # Don't die here, just make best effort to do a version
+                    # comparison by directly comparing the full strings
+                    $kernel_epoch = undef;
+                    $kernel_ver = $kernel_rpmver;
+                    $kernel_release = undef;
+
+                    $min_epoch = undef;
+                    $min_version = $props->{minversion};
+                    $min_release = undef;
+                }
+
+                # If the guest is using a Xen PV kernel, choose an appropriate
+                # normal kernel replacement
+                if ($kernel_pkg =~ /^kernel-xen/)
+                {
+                    $kernel_pkg =
+                        _get_replacement_kernel_name($g, $root, $kernel_arch,
+                                                     $meta);
+
+                    # Check if we've already got an appropriate kernel
+                    my ($inst) =
+                        _get_installed("$kernel_pkg.$kernel_arch", $g);
+
+                    if (!defined($inst) ||
+                        (defined($min_version) &&
+                         _evr_cmp($inst->[0], $inst->[1], $inst->[2],
+                                  $min_epoch, $min_version, $min_release) < 0))
+                    {
+                        # filter out xen/xenU from release field
+                        if (defined($kernel_release) &&
+                            $kernel_release =~ /^(\S+?)(xen)?(U)?$/)
+                        {
+                            $kernel_release = $1;
+                        }
+
+                        # If the guest kernel is new enough, but PV, try to
+                        # replace it with an equivalent version FV kernel
+                        if (!defined($min_version) ||
+                            _evr_cmp($kernel_epoch, $kernel_ver,
+                                     $kernel_release,
+                                     $min_epoch, $min_version,
+                                     $min_release) >= 0)
+                        {
+                            $kernel = [$kernel_pkg, $kernel_arch,
+                                       $kernel_epoch, $kernel_ver,
+                                       $kernel_release];
+                        }
+
+                        # Otherwise, just grab the latest
+                        else {
+                            $kernel = [$kernel_pkg, $kernel_arch];
+                        }
+                    }
+                }
+
+                # If the kernel is too old, grab the latest replacement
+                elsif (defined($min_version) &&
+                       _evr_cmp($kernel_epoch, $kernel_ver, $kernel_release,
+                                $min_epoch, $min_version, $min_release) < 0)
+                {
+                    $kernel = [$kernel_pkg, $kernel_arch];
+                }
+            }
+        }
+
+        else {
+            my @installed = _get_installed($name, $g);
+
+            # Ignore an 'ifinstalled' dep if it's not currently installed
+            next if (@installed == 0 && $ifinstalled);
+
+            # Ok if any version is installed and no minversion was specified
+            next if (@installed > 0 && !defined($min_version));
+
+            if (defined($min_version)) {
+                # Check if any installed version meets the minimum version
+                my $found = 0;
+                foreach my $app (@installed) {
+                    my ($epoch, $version, $release) = @$app;
+
+                    if (_evr_cmp($app->[0], $app->[1], $app->[2],
+                                 $min_epoch, $min_version, $min_release) >= 0) {
+                        $found = 1;
+                        last;
+                    }
+                }
+
+                # Install the latest available version of the dep if it wasn't
+                # found
+                if (!$found) {
+                    if (@installed == 0) {
+                        push(@install, [$name]);
+                    } else {
+                        push(@upgrade, [$name]);
+                    }
+                }
+            } else {
+                push(@install, [$name]);
+            }
+        }
+    }
+
+    # Capability is already installed
+    if (!defined($kernel) && @install == 0 && @upgrade == 0) {
+        return 1;
+    }
+
+    my $success = _install_any($kernel, \@install, \@upgrade,
+                               $g, $root, $config, $grub);
+
+    return $success;
+}
+
+sub _net_run {
+    my ($g, $sub) = @_;
+
+    my $resolv_bak = $g->exists('/etc/resolv.conf');
+    $g->mv('/etc/resolv.conf', '/etc/resolv.conf.v2vtmp') if ($resolv_bak);
+
+    # XXX We should get the nameserver from the appliance here, but
+    # there's no current api other than debug to do this.
+    $g->write_file('/etc/resolv.conf', "nameserver 169.254.2.3", 0);
+
+    eval &$sub();
+    my $err = $@;
+
+    $g->mv('/etc/resolv.conf.v2vtmp', '/etc/resolv.conf') if ($resolv_bak);
+
+    die $err if $err;
+}
+
+sub _install_any
+{
+    my ($kernel, $install, $upgrade, $g, $root, $config, $grub) = @_;
+
+    # If we're installing a kernel, check which kernels are there first
+    my @k_before = $g->glob_expand('/boot/vmlinuz-*') if defined($kernel);
+
+    # If a SLES11 kernel is being added, add -base kernel
+    if (defined($kernel)) {
+        if (_is_suse_family($g, $root) &&
+            ($g->inspect_get_major_version($root) == 11)) {
+            my $tmp;
+            push(@$tmp, $kernel);
+            $tmp->[1][0] = $tmp->[0][0].'-base';
+            for my $count (1..4) {
+                if (defined($tmp->[0][$count])) {
+                    $tmp->[1][$count] = $tmp->[0][$count];
+                }
+            }
+            $kernel = $tmp;
+        }
+    }
+
+    # 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
+            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,
+                                       $g, $root, $config)
+                unless ($success);
+        };
+        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 ($@);
+
+    # Installing a new kernel in RHEL 5 under libguestfs fails to add a grub
+    # entry. Find the kernel we installed and ensure it has a grub entry.
+    if (defined($kernel)) {
+        foreach my $k ($g->glob_expand('/boot/vmlinuz-*')) {
+            if (!grep(/^$k$/, @k_before)) {
+                $grub->check($k, $root);
+                last;
+            }
+        }
+    }
+
+    return $success;
+}
+
+sub _install_up2date
+{
+    my ($kernel, $install, $upgrade, $g) = @_;
+
+    # Check this system has actions.packages
+    return 0 unless ($g->exists('/usr/bin/up2date'));
+
+    # Check this system is registered to rhn
+    return 0 unless ($g->exists('/etc/sysconfig/rhn/systemid'));
+
+    my @pkgs;
+    foreach my $pkg ($kernel, @$install, @$upgrade) {
+        next unless defined($pkg);
+
+        # up2date doesn't do arch
+        my ($name, undef, $epoch, $version, $release) = @$pkg;
+
+        $epoch   ||= "";
+        $version ||= "";
+        $release ||= "";
+
+        push(@pkgs, "['$name', '$version', '$release', '$epoch']");
+    }
+
+    eval {
+         $g->command(['/usr/bin/python', '-c',
+                     "import sys; sys.path.append('/usr/share/rhn'); ".
+                     "import actions.packages;                       ".
+                     "actions.packages.cfg['forceInstall'] = 1;      ".
+                     "ret = actions.packages.update([".join(',', @pkgs)."]); ".
+                     "sys.exit(ret[0]);                              "]);
+    };
+    if ($@) {
+        logmsg WARN, __x('Failed to install packages using up2date. '.
+                         'Error message was: {error}', error => $@);
+        return 0;
+    }
+
+    return 1;
+}
+
+sub _install_yum
+{
+    my ($kernel, $install, $upgrade, $g) = @_;
+
+    # Check this system has yum installed
+    return 0 unless ($g->exists('/usr/bin/yum'));
+
+    # Install or upgrade 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 upgrade
+    if (defined($kernel)) {
+        my @installed = _get_installed($kernel->[0], $g);
+
+        # Don't modify the contents of $install and $upgrade 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($upgrade) ? @$upgrade : ();
+            push(@tmp, $kernel);
+            $upgrade = \@tmp;
+        }
+    }
+
+    my $success = 1;
+    YUM: foreach my $task (
+        [ "install", $install, qr/(^No package|already installed)/ ],
+        [ "upgrade", $upgrade, qr/^No Packages/ ]
+    ) {
+        my ($action, $list, $failure) = @$task;
+
+        # We can't do these all in a single transaction, because yum offers us
+        # no way to tell if a transaction partially succeeded
+        foreach my $entry (@$list) {
+            next unless (defined($entry));
+
+            # You can't specify epoch without architecture to yum, so we just
+            # ignore epoch and hope
+            my ($name, undef, undef, $version, $release) = @$entry;
+
+            # Construct n-v-r
+            my $pkg = $name;
+            $pkg .= "-$version" if (defined($version));
+            $pkg .= "-$release" if (defined($release));
+
+            my @output =
+                eval { $g->sh_lines("LANG=C /usr/bin/yum -y $action $pkg") };
+            if ($@) {
+                logmsg WARN, __x('Failed to install packages using yum. '.
+                                 'Output was: {output}', output => $@);
+                $success = 0;
+                last YUM;
+            }
+
+            foreach my $line (@output) {
+                # Yum probably just isn't configured. Don't bother with an error
+                # message
+                if ($line =~ /$failure/) {
+                    $success = 0;
+                    last 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][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->[0][2])) {
+            my @tmp = defined($install) ? @$install : ();
+            for my $count (0..1) {
+                if (defined($kernel->[$count])) {
+                    push(@tmp, $kernel->[$count]);
+                }
+            }
+            $install = \@tmp;
+        } else {
+            my @tmp = defined($update) ? @$update : ();
+            for my $count (0..1) {
+                if (defined($kernel->[$count])) {
+                    push(@tmp, $kernel->[$count]);
+                }
+            }
+            $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 ($@) {
+                # Catch 'No provider' errors and only issue a warning, as
+                # an install from virt-v2v repo will be attempted next.
+                if ($@ =~ /No provider/) {
+                    logmsg WARN, __x('No provider of {package} found. '.
+                                   'Checking local virt-v2v repo.',
+                                   package => @pkgs);
+                } else {
+                    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) = @_;
+
+    my ($kernel, $user);
+    if (defined($kernel_naevr)) {
+        foreach my $kernel_entry (@$kernel_naevr) {
+            my ($kernel_pkg, $kernel_arch) = @$kernel_entry;
+            my ($tmp_kernel, $tmp_user);
+            ($tmp_kernel, $tmp_user) =
+                $config->match_app($g, $root, $kernel_pkg, $kernel_arch);
+            push(@$kernel, $tmp_kernel);
+            foreach my $tmp_app (@$tmp_user) {
+                if (defined($tmp_app)) {
+                    push(@$user, $tmp_app);
+                }
+            }
+        }
+    } else {
+        $user = [];
+    }
+
+    foreach my $pkg (@$install, @$upgrade) {
+        push(@$user, $pkg->[0]);
+    }
+
+    my @missing;
+    if (defined($kernel)) {
+        foreach my $pkg (@$kernel) {
+            my $transfer_path = $config->get_transfer_path($pkg);
+            if (!defined($transfer_path) || !$g->exists($transfer_path)) {
+                push(@missing, $pkg);
+            }
+        }
+    }
+
+    my @user_paths = _get_deppaths($g, $root, $config,
+                                   \@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));
+    }
+
+    return 1;
+}
+
+# Install a set of rpms
+sub _install_rpms
+{
+    local $_;
+    my ($g, $config, $upgrade, @rpms) = @_;
+
+    # Nothing to do if we got an empty set
+    return if(scalar(@rpms) == 0);
+
+    # All paths are relative to the transfer mount. Need to make them absolute.
+    # No need to check get_transfer_path here as all paths have been previously
+    # checked
+    @rpms = map { $_ = $config->get_transfer_path($_) } @rpms;
+
+    $g->command(['rpm', $upgrade == 1 ? '-U' : '-i', @rpms]);
+
+    # Reload augeas in case the rpm installation changed anything
+    eval { $g->aug_load() };
+    augeas_error($g, $@) if($@);
+}
+
+# Return a list of dependency paths which need to be installed for the given
+# apps
+sub _get_deppaths
+{
+    my ($g, $root, $config, $missing, $arch, @apps) = @_;
+
+    my %required;
+    foreach my $app (@apps) {
+        my ($path, $deps) = $config->match_app($g, $root, $app, $arch);
+
+        my $transfer_path = $config->get_transfer_path($path);
+        my $exists = defined($transfer_path) && $g->exists($transfer_path);
+
+        if (!$exists) {
+            push(@$missing, $path);
+        }
+
+        if (!$exists || !_newer_installed($transfer_path, $g, $config)) {
+            $required{$path} = 1;
+
+            foreach my $deppath (_get_deppaths($g, $root, $config,
+                                               $missing, $arch, @$deps))
+            {
+                $required{$deppath} = 1;
+            }
+        }
+
+        # 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;
+
+            # It's not an error if no i386 package is available
+            eval {
+                ($path, $deps) = $config->match_app($g, $root, $app, 'i386');
+            };
+
+            if (defined($path)) {
+                $transfer_path = $config->get_transfer_path($path);
+                if (!defined($transfer_path) || !$g->exists($transfer_path)) {
+                    push(@$missing, $path);
+
+                    foreach my $deppath (_get_deppaths($g, $root, $config,
+                                                      $missing, 'i386', @$deps))
+                    {
+                        $required{$deppath} = 1;
+                    }
+                }
+            }
+        }
+    }
+
+    return keys(%required);
+}
+
+# Return 1 if the requested rpm, or a newer version, is installed
+# Return 0 otherwise
+sub _newer_installed
+{
+    my ($rpm, $g, $config) = @_;
+
+    my ($name, $epoch, $version, $release, $arch) =
+        _get_nevra($rpm, $g, $config);
+
+    my @installed = _get_installed("$name.$arch", $g);
+
+    # Search installed rpms matching <name>.<arch>
+    foreach my $pkg (@installed) {
+        next if _evr_cmp($pkg->[0], $pkg->[1], $pkg->[2],
+                         $epoch, $version, $release) < 0;
+        return 1;
+    }
+
+    return 0;
+}
+
+sub _get_nevra
+{
+    my ($rpm, $g, $config) = @_;
+
+    # Get NEVRA for the rpm to be installed
+    my $nevra = $g->command(['rpm', '-qp', '--qf',
+                             '%{NAME} %{EPOCH} %{VERSION} %{RELEASE} %{ARCH}',
+                             $rpm]);
+
+    $nevra =~ /^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)$/
+        or die("Unexpected return from rpm command: $nevra");
+    my ($name, $epoch, $version, $release, $arch) = ($1, $2, $3, $4, $5);
+
+    # Ensure epoch is always numeric
+    $epoch = 0 if('(none)' eq $epoch);
+
+    return ($name, $epoch, $version, $release, $arch);
+}
+
+# Inspect the guest to work out what kernel package is in use
+# Returns ($kernel_pkg, $kernel_arch)
+sub _discover_kernel
+{
+    my ($g, $root, $grub) = @_;
+
+    # Get a current bootable kernel, preferrably the default
+    my $kernel_pkg;
+    my $kernel_arch;
+    my $kernel_ver;
+
+    foreach my $path ($grub->list_kernels()) {
+        my $kernel = _inspect_linux_kernel($g, $path);
+
+        # Check its architecture is known
+        $kernel_arch = $kernel->{arch};
+        next unless (defined($kernel_arch));
+
+        # Get the kernel package name
+        $kernel_pkg = $kernel->{package};
+
+        # Get the kernel package version
+        $kernel_ver = $kernel->{version};
+
+        last;
+    }
+
+    # 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
+    $kernel_arch = $g->inspect_get_arch($root) unless defined($kernel_arch);
+
+    # Change i386 to i[56]86
+    $kernel_arch = _set_32bit_arch($g, $root, $kernel_arch);
+
+    return ($kernel_pkg, $kernel_arch, $kernel_ver);
+}
+
+sub _get_replacement_kernel_name
+{
+    my ($g, $root, $arch, $meta) = @_;
+
+    # Make an informed choice about a replacement kernel for distros we know
+    # about
+
+    # 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';
+            }
+        }
+
+        # 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
+    }
+
+    # SUSE kernels
+    elsif (_is_suse_family($g, $root)) {
+        # SLES/openSUSE 11+
+        if ($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';
+                }
+
+                else {
+                    return 'kernel-default';
+                }
+            }
+
+            # There's only 1 kernel package on SLES 11 x86_64
+            else {
+                return 'kernel-default';
+            }
+        }
+
+        # SLES/openSUSE 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';
+                }
+
+                else {
+                    return 'kernel-default';
+                }
+            }
+
+            else {
+                if ($meta->{cpus} > 1) {
+                    return 'kernel-smp';
+                }
+
+                else {
+                    return 'kernel-default';
+                }
+            }
+        }
+    }
+
+    # XXX: Could do with a history of Fedora kernels in here
+
+    # For other distros, be conservative and just return 'kernel'
+    return 'kernel';
+}
+
+sub _get_installed
+{
+    my ($name, $g) = @_;
+
+    my $rpmcmd = ['rpm', '-q', '--qf', '%{EPOCH} %{VERSION} %{RELEASE}\n',
+                  $name];
+    my @output = eval { $g->command_lines($rpmcmd) };
+    if ($@) {
+        # RPM command returned non-zero. This might be because there was
+        # actually an error, or might just be because the package isn't
+        # 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.
+        my $error = $g->sh("LANG=C '".join("' '", @$rpmcmd)."' 2>&1 ||:");
+
+        return () if ($error =~ /not installed/);
+
+        v2vdie __x('Error running {command}: {error}',
+                   command => join(' ', @$rpmcmd), error => $error);
+    }
+
+    my @installed = ();
+    foreach my $installed (@output) {
+        $installed =~ /^(\S+)\s+(\S+)\s+(\S+)$/
+            or die("Unexpected return from rpm command: $installed");
+        my ($epoch, $version, $release) = ($1, $2, $3);
+
+        # Ensure epoch is always numeric
+        $epoch = 0 if('(none)' eq $epoch);
+
+        push(@installed, [$epoch, $version, $release]);
+    }
+
+    return sort { _evr_cmp($a->[0], $a->[1], $a->[2],
+                           $b->[0], $b->[1], $b->[2]) } @installed;
+}
+
+sub _parse_evr
+{
+    my ($evr) = @_;
+
+    $evr =~ /^(?:(\d+):)?([^-]+)(?:-(\S+))?$/ or die();
+
+    my $epoch = $1;
+    my $version = $2;
+    my $release = $3;
+
+    return ($epoch, $version, $release);
+}
+
+sub _evr_cmp
+{
+    my ($e1, $v1, $r1, $e2, $v2, $r2) = @_;
+
+    # Treat epoch as zero if undefined
+    $e1 ||= 0;
+    $e2 ||= 0;
+
+    return -1 if ($e1 < $e2);
+    return 1 if ($e1 > $e2);
+
+    # version must be defined
+    my $cmp = _rpmvercmp($v1, $v2);
+    return $cmp if ($cmp != 0);
+
+    # Treat release as the empty string if undefined
+    $r1 ||= "";
+    $r2 ||= "";
+
+    return _rpmvercmp($r1, $r2);
+}
+
+# An implementation of rpmvercmp. Compares two rpm version/release numbers and
+# returns -1/0/1 as appropriate.
+# Note that this is intended to have the exact same behaviour as the real
+# rpmvercmp, not be in any way sane.
+sub _rpmvercmp
+{
+    my ($a, $b) = @_;
+
+    # Simple equality test
+    return 0 if($a eq $b);
+
+    my @aparts;
+    my @bparts;
+
+    # [t]ransformation
+    # [s]tring
+    # [l]ist
+    foreach my $t ([$a => \@aparts],
+                   [$b => \@bparts]) {
+        my $s = $t->[0];
+        my $l = $t->[1];
+
+        # We split not only on non-alphanumeric characters, but also on the
+        # boundary of digits and letters. This corresponds to the behaviour of
+        # rpmvercmp because it does 2 types of iteration over a string. The
+        # first iteration skips non-alphanumeric characters. The second skips
+        # over either digits or letters only, according to the first character
+        # of $a.
+        @$l = split(/(?<=[[:digit:]])(?=[[:alpha:]]) | # digit<>alpha
+                     (?<=[[:alpha:]])(?=[[:digit:]]) | # alpha<>digit
+                     [^[:alnum:]]+                # sequence of non-alphanumeric
+                    /x, $s);
+    }
+
+    # Find the minimun of the number of parts of $a and $b
+    my $parts = scalar(@aparts) < scalar(@bparts) ?
+                scalar(@aparts) : scalar(@bparts);
+
+    for(my $i = 0; $i < $parts; $i++) {
+        my $acmp = $aparts[$i];
+        my $bcmp = $bparts[$i];
+
+        # Return 1 if $a is numeric and $b is not
+        if($acmp =~ /^[[:digit:]]/) {
+            return 1 if($bcmp !~ /^[[:digit:]]/);
+
+            # Drop leading zeroes
+            $acmp =~ /^0*(.*)$/;
+            $acmp = $1;
+            $bcmp =~ /^0*(.*)$/;
+            $bcmp = $1;
+
+            # We do a string comparison of 2 numbers later. At this stage, if
+            # they're of differing lengths, one is larger.
+            return 1 if(length($acmp) > length($bcmp));
+            return -1 if(length($bcmp) > length($acmp));
+        }
+
+        # Return -1 if $a is letters and $b is not
+        else {
+            return -1 if($bcmp !~ /^[[:alpha:]]/);
+        }
+
+        # Return only if they differ
+        return -1 if($acmp lt $bcmp);
+        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.
+
+    # Whichever has the greatest number of parts is the largest
+    return -1 if(scalar(@aparts) < scalar(@bparts));
+    return 1  if(scalar(@aparts) > scalar(@bparts));
+
+    # We can get here if the 2 strings differ only in non-alphanumeric
+    # separators.
+    return 0;
+}
+
+sub _remap_block_devices
+{
+    my ($meta, $virtio, $g, $root, $grub) = @_;
+
+    my @devices = map { $_->{device} } @{$meta->{disks}};
+    @devices = sort { scsi_first_cmp($a, $b) } @devices;
+
+    # @devices contains an ordered list of libvirt device names. Because
+    # libvirt uses a similar naming scheme to Linux, these will mostly be the
+    # same names as used by the guest. They are ordered as they were passed to
+    # 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.
+
+    # Modern distros use libata, and IDE devices are presented as sdX
+    my $libata = 1;
+
+    # RHEL 2, 3 and 4 didn't use libata
+    # RHEL 5 does use libata, but udev rules call IDE devices hdX anyway
+    if (_is_rhel_family($g, $root)) {
+        my $major_version = $g->inspect_get_major_version($root);
+        if ($major_version eq '2' ||
+            $major_version eq '3' ||
+            $major_version eq '4' ||
+            $major_version eq '5')
+        {
+            $libata = 0;
+        }
+    }
+    # Fedora has used libata since FC7, which is long out of support. We assume
+    # that all Fedora distributions in use use libata.
+
+    # Create a hash to track any hd->sd conversions, as any references to
+    # hd devices (in fstab, menu.lst, etc) will need to be converted later.
+    my %idemap;
+
+    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,
+        # these will have been presented to the guest as sdc, sdd, sda and sdb
+        # respectively.
+        #
+        # N.B. I think the IDE/SCSI ordering situation may be somewhat more
+        # complicated than previously thought. This code originally put IDE
+        # drives first, but this hasn't worked for an OVA file. Given that
+        # this is a weird and somewhat unlikely situation I'm going with SCSI
+        # first until we have a more comprehensive solution.
+
+        my $idedev;
+        my @newdevices;
+        my $suffix = 'a';
+        foreach my $device (@devices) {
+            if ($device =~ /(?:h|s)d[a-z]+/) {
+                $idedev = $device;
+                $device = 'sd'.$suffix++;
+                $idemap{$device} = $idedev if ($device ne $idedev);
+            }
+            push(@newdevices, $device);
+        }
+        @devices = @newdevices;
+    }
+
+    # We now assume that @devices contains an ordered list of device names, as
+    # used by the guest. Create a map of old guest device names to new guest
+    # device names.
+    my %map;
+
+    # Everything will be converted to either vdX, sdX or hdX
+    my $prefix;
+    if ($virtio) {
+        $prefix = 'vd';
+    } elsif ($libata) {
+        $prefix = 'sd';
+    } else {
+        $prefix = 'hd'
+    }
+
+    my $letter = 'a';
+    foreach my $device (@devices) {
+        my $mapped = $prefix.$letter;
+
+
+        # If a Xen guest has non-PV devices, Xen also simultaneously presents
+        # 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.
+        if ($device =~ /^(?:h|s)d([a-z]+)/) {
+            $map{'xvd'.$1} = $mapped;
+        }
+        $map{$device} = $mapped;
+        # Also add a mapping for previously saved hd->sd conversions
+        if (defined($idemap{$device})) {
+            my $idedevice;
+            $idedevice = $idemap{$device};
+            $map{$idedevice} = $mapped;
+        }
+
+        $letter++;
+    }
+
+    eval {
+        # Update bare device references in fstab and grub's menu.lst and device.map
+        foreach my $spec ($g->aug_match('/files/etc/fstab/*/spec'),
+                          $g->aug_match("/files$grub->{grub_conf}/*/kernel/root"),
+                          $g->aug_match("/files$grub->{grub_conf}/*/kernel/resume"),
+                          $g->aug_match('/files/boot/grub/device.map/*'.
+                                            '[label() != "#comment"]'))
+        {
+            my $device = $g->aug_get($spec);
+
+            # Match device names and partition numbers
+            my $name; my $part;
+            foreach my $r (qr{^/dev/(cciss/c\d+d\d+)(?:p(\d+))?$},
+                           qr{^/dev/([a-z]+)(\d*)?$}) {
+                if ($device =~ $r) {
+                    $name = $1;
+                    $part = $2;
+                    last;
+                }
+            }
+
+            # Ignore this entry if it isn't a device name
+            next unless defined($name);
+
+            # Ignore md and floppy devices, which don't need to be mapped
+            next if $name =~ /(md|fd)/;
+
+            # Ignore this entry if it refers to a device we don't know anything
+            # 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') {
+                    if ($spec =~ m{^/files$file}) {
+                        logmsg WARN, __x('{file} references unknown device '.
+                                         '{device}. This entry must be '.
+                                         'manually fixed after conversion.',
+                                         file => $file, device => $device);
+                        $warned = 1;
+                    }
+                }
+
+                # Shouldn't happen. Not fatal if it does, though.
+                if (!$warned) {
+                    logmsg WARN, 'Please report this warning as a bug. '.
+                                 "augeas path $spec refers to unknown device ".
+                                 "$device. This entry must be manually fixed ".
+                                 'after conversion.'
+                }
+
+                next;
+            }
+
+            my $mapped = '/dev/'.$map{$name};
+            $mapped .= $part if defined($part);
+            $g->aug_set($spec, $mapped);
+        }
+
+        $g->aug_save();
+    };
+
+    augeas_error($g, $@) if ($@);
+
+    # Delete cached (and now out of date) blkid info if it exists
+    foreach my $blkidtab ('/etc/blkid/blkid.tab', '/etc/blkid.tab') {
+        $g->rm($blkidtab) if ($g->exists($blkidtab));
+    }
+}
+
+sub _prepare_bootable
+{
+    my ($g, $root, $grub, $version, @modules) = @_;
+
+    my $path = "/boot/vmlinuz-$version";
+    $grub->write($path);
+    my $grub_initrd = $grub->get_initrd($path);
+
+    # Backup the original initrd
+    $g->mv($grub_initrd, "$grub_initrd.pre-v2v")
+    if $g->exists($grub_initrd);
+
+    if ($g->exists('/sbin/dracut')) {
+        $g->command(['/sbin/dracut', '--add-drivers', join(" ", @modules),
+                     $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 SLES and dracut does not exist
+    elsif ($g->exists('/sbin/mkinitrd')) {
+        # Create a new initrd which probes the required kernel modules
+        my @module_args = ();
+        foreach my $module (@modules) {
+            push(@module_args, "--with=$module");
+        }
+
+        # We explicitly modprobe ext2 here. This is required by mkinitrd on
+        # RHEL 3, and shouldn't hurt on other OSs. We don't care if this
+        # fails.
+        eval { $g->modprobe('ext2') };
+
+        # loop is a module in RHEL 5. Try to load it. Doesn't matter for
+        # other OSs if it doesn't exist, but RHEL 5 will complain:
+        #   All of your loopback devices are in use.
+        eval { $g->modprobe('loop') };
+
+        my @env;
+
+        # RHEL 4 mkinitrd determines if the root filesystem is on LVM by
+        # checking if the device name (after following symlinks) starts with
+        # /dev/mapper. However, on recent kernels/udevs, /dev/mapper/foo is
+        # just a symlink to /dev/dm-X. This means that RHEL 4 mkinitrd
+        # running in the appliance fails to detect root on LVM. We check
+        # ourselves if root is on LVM, and frig RHEL 4's mkinitrd if it is
+        # by setting root_lvm=1 in its environment. This overrides an
+        # internal variable in mkinitrd, and is therefore extremely nasty
+        # and applicable only to a particular version of mkinitrd.
+        if (_is_rhel_family($g, $root) &&
+            $g->inspect_get_major_version($root) eq '4')
+        {
+            push(@env, 'root_lvm=1') if ($g->is_lv($root));
+        }
+
+        $g->sh(join(' ', @env).' /sbin/mkinitrd '.join(' ', @module_args).
+               " $grub_initrd $version");
+    }
+
+    else {
+        v2vdie __('Didn\'t find mkinitrd or dracut. Unable to update initrd.');
+    }
+
+    # Disable kudzu in the guest
+    # Kudzu will detect the changed network hardware at boot time and either:
+    #   require manual intervention, or
+    #   disable the network interface
+    # Neither of these behaviours is desirable.
+    if ($g->exists('/etc/init.d/kudzu')) {
+        $g->command(['/sbin/chkconfig', 'kudzu', 'off']);
+    }
+}
+
+# Return 1 if the guest supports ACPI, 0 otherwise
+sub _supports_acpi
+{
+    my ($g, $root, $arch) = @_;
+
+    # 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') {
+        return 0;
+    }
+
+    return 1;
+}
+
+sub _supports_virtio
+{
+    my ($kernel, $g) = @_;
+
+    my %checklist = (
+        "virtio_net" => 0,
+        "virtio_blk" => 0
+    );
+
+    # Search the installed kernel's modules for the virtio drivers
+    foreach my $module ($g->find("/lib/modules/$kernel")) {
+        foreach my $driver (keys(%checklist)) {
+            if($module =~ m{/$driver\.(?:o|ko)$}) {
+                $checklist{$driver} = 1;
+            }
+        }
+    }
+
+    # Check we've got all the drivers in the checklist
+    foreach my $driver (keys(%checklist)) {
+        if(!$checklist{$driver}) {
+            return 0;
+        }
+    }
+
+    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
+
+Copyright (C) 2009-2012 Red Hat Inc.
+
+=head1 LICENSE
+
+Please see the file COPYING.LIB for the full license.
+
+=head1 SEE ALSO
+
+L<Sys::VirtConvert::Converter(3pm)>,
+L<Sys::VirtConvert(3pm)>,
+L<virt-v2v(1)>,
+L<http://libguestfs.org/>.
+
+=cut
+
+1;
diff --git a/lib/Sys/VirtConvert/Converter/RedHat.pm b/lib/Sys/VirtConvert/Converter/RedHat.pm
deleted file mode 100644
index 612ab2e..0000000
--- a/lib/Sys/VirtConvert/Converter/RedHat.pm
+++ /dev/null
@@ -1,2489 +0,0 @@
-# Sys::VirtConvert::Converter::RedHat
-# Copyright (C) 2009-2012 Red Hat Inc.
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2 of the License, or (at your option) any later version.
-#
-# This library 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
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-use strict;
-use warnings;
-
-
-# Functions supported by grubby, and therefore common between gruby legacy and
-# grub2
-package Sys::VirtConvert::Converter::RedHat::Grub;
-
-use Sys::VirtConvert::Util;
-use Locale::TextDomain 'virt-v2v';
-
-sub get_initrd
-{
-    my $self = shift;
-    my ($path) = @_;
-
-    my $g = $self->{g};
-
-    foreach my $line ($g->command_lines(['grubby', '--info', $path])) {
-        return $1 if $line =~ /^initrd=(\S+)/;
-    }
-
-    v2vdie __x('Didn\'t find initrd for kernel {path}', path => $path);
-}
-
-sub check_efi
-{
-    my $self = shift;
-    my $g = $self->{g};
-
-    # Check the first partition of each device looking for an EFI boot
-    # partition. We can't be sure which device is the boot device, so we just
-    # check them all.
-    foreach my $device ($g->list_devices()) {
-        my $guid = eval { $g->part_get_gpt_type($device, 1) };
-        next unless defined($guid);
-
-        if ($guid eq 'C12A7328-F81F-11D2-BA4B-00A0C93EC93B') {
-            $self->convert_efi($device);
-            last;
-        }
-    }
-}
-
-
-# Methods for inspecting and manipulating grub legacy
-package Sys::VirtConvert::Converter::RedHat::GrubLegacy;
-
-use Sys::VirtConvert::Util;
-
-use File::Basename;
-use Locale::TextDomain 'virt-v2v';
-
- at Sys::VirtConvert::Converter::RedHat::GrubLegacy::ISA =
-    qw(Sys::VirtConvert::Converter::RedHat::Grub);
-
-sub new
-{
-    my $class = shift;
-    my ($g, $root) = @_;
-
-    my $self = {};
-    bless($self, $class);
-
-    $self->{g} = $g;
-    $self->{root} = $root;
-
-    # Look for an EFI configuration
-    foreach my $cfg ($g->glob_expand('/boot/efi/EFI/*/grub.conf')) {
-        $self->check_efi();
-    }
-
-    # Look for the grub configuration file
-    foreach my $path ('/boot/grub/menu.lst', '/boot/grub/grub.conf')
-    {
-        if ($g->exists($path)) {
-            $self->{grub_conf} = $path;
-            last;
-        }
-    }
-
-    # We can't continue without a config file
-    die unless defined ($self->{grub_conf});
-
-    # Find the path which needs to be prepended to paths in grub.conf to
-    # make them absolute
-    # Look for the most specific mount point discovered
-
-    # Default to / (no prefix required)
-    $self->{grub_fs} ||= "";
-
-    my %mounts = $g->inspect_get_mountpoints($root);
-    foreach my $path ('/boot/grub', '/boot') {
-        if (exists($mounts{$path})) {
-            $self->{grub_fs} = $path;
-            last;
-        }
-    }
-
-    # Initialise augeas
-    eval {
-        # Check grub_conf is included by the Grub lens
-        my $found = 0;
-        foreach my $incl ($g->aug_match("/augeas/load/Grub/incl")) {
-            if ($g->aug_get($incl) eq $self->{grub_conf}) {
-                $found = 1;
-                last;
-            }
-        }
-
-        # If it wasn't there, add it
-        unless ($found) {
-            $g->aug_set("/augeas/load/Grub/incl[last()+1]", $self->{grub_conf});
-
-            # Make augeas pick up the new configuration
-            $g->aug_load();
-        }
-    };
-    augeas_error($g, $@) if ($@);
-
-    return $self;
-}
-
-sub list_kernels
-{
-    my $self = shift;
-
-    my $g = $self->{g};
-    my $grub_conf = $self->{grub_conf};
-    my $grub_fs = $self->{grub_fs};
-
-    # Look for a kernel, starting with the default
-    my @paths = eval {
-        # Get a list of all kernels
-        my @ret = $g->aug_match("/files$grub_conf/title/kernel");
-
-        # Get the default kernel from grub if it's set
-        # Doesn't matter if there isn't one (aug_get will fail)
-        my $default = eval {
-            my $idx = $g->aug_get("/files$grub_conf/default");
-
-            # Grub indices are zero-based, augeas is 1-based
-            "/files$grub_conf/title[".($idx + 1)."]/kernel";
-        };
-
-        if (defined($default)) {
-            # Remove the default from the list and put it at the beginning
-            @ret = grep {!m{$default}} @ret;
-            unshift(@ret, $default);
-        }
-
-        @ret;
-    };
-    augeas_error($g, $@) if ($@);
-
-    my @kernels;
-    my %checked;
-    foreach my $path (@paths) {
-        next if $checked{$path};
-        $checked{$path} = 1;
-
-        my $kernel = eval { $g->aug_get($path) };
-        augeas_error($g, $@) if ($@);
-
-        # Prepend the grub filesystem to the kernel path
-        $kernel = "$grub_fs$kernel" if defined $grub_fs;
-
-        # Check the kernel exists
-        if ($g->exists($kernel)) {
-            push(@kernels, $kernel);
-        }
-
-        else {
-            logmsg WARN, __x('grub refers to {path}, which doesn\'t exist.',
-                             path => $kernel);
-        }
-    }
-
-    return @kernels;
-}
-
-sub update_console
-{
-    my $self = shift;
-    my ($remove) = @_;
-
-    my $g = $self->{g};
-    my $grub_conf = $self->{grub_conf};
-
-    eval {
-        # Update any kernel console lines
-        my $deleted = 0;
-        foreach my $augpath
-            ($g->aug_match("/files$grub_conf/title/kernel/console"))
-        {
-            my $console = $g->aug_get($augpath);
-            if ($console =~ /\b(x|h)vc0\b/) {
-                if ($remove) {
-                    $g->aug_defvar("delconsole$deleted", $augpath);
-                    $deleted++;
-                } else {
-                    $console =~ s/\b(x|h)vc0\b/ttyS0/g;
-                    $g->aug_set($augpath, $console);
-                }
-            }
-
-            if ($remove && $console =~ /\bttyS0\b/) {
-                $g->aug_rm($augpath);
-            }
-        }
-
-        for (my $i = 0; $i < $deleted; $i++) {
-            $g->aug_rm('$delconsole'.$i);
-        }
-    };
-    augeas_error($g, $@) if ($@);
-}
-
-sub check
-{
-    my $self = shift;
-    my ($path) = @_;
-
-    my $g = $self->{g};
-    my $grub_conf = $self->{grub_conf};
-    my $grub_fs   = $self->{grub_fs};
-    $path =~ /^$grub_fs(.*)/
-       or v2vdie __x('Kernel {kernel} is not under grub tree {grub}',
-                     kernel => $path, grub => $grub_fs);
-    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']");
-    return if scalar(@entries) > 0;
-
-    my $kernel =
-        Sys::VirtConvert::Converter::RedHat::_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') };
-    $title ||= 'Linux';
-
-    # This is how new-kernel-pkg does it
-    $title =~ s/ release.*//;
-    $title .= " ($version)";
-
-    # Doesn't matter if there's no default
-    my $default = eval { $g->aug_get("/files$grub_conf/default") };
-
-    if (defined($default)) {
-        $g->aug_defvar('template',
-             "/files$grub_conf/title[".($default + 1).']');
-    }
-
-    # If there's no default, take the first entry with a kernel
-    else {
-        my ($match) = $g->aug_match("/files$grub_conf/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$grub_conf/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', $grub_path);
-    $g->aug_set('$new/initrd', $grub_initrd);
-
-    # Copy all kernel command-line arguments
-    foreach my $arg ($g->aug_match('$template/kernel/*')) {
-        # kernel arguments don't necessarily have values
-        my $val = eval { $g->aug_get($arg) };
-
-        $arg =~ /([^\/]*)$/;
-        $arg = $1;
-
-        if (defined($val)) {
-            $g->aug_set('$new/kernel/'.$arg, $val);
-        } else {
-            $g->aug_clear('$new/kernel/'.$arg);
-        }
-    }
-}
-
-sub write
-{
-    my $self = shift;
-    my ($path) = @_;
-
-    my $g = $self->{g};
-    my $grub_conf = $self->{grub_conf};
-    my $grub_fs   = $self->{grub_fs};
-
-    $path =~ /^$grub_fs(.*)/
-       or v2vdie __x('Kernel {kernel} is not under grub tree {grub}',
-                     kernel => $path, grub => $grub_fs);
-    my $grub_path = $1;
-
-    # Find the grub entry for the given kernel
-    eval {
-        my ($aug_path) =
-            $g->aug_match("/files$grub_conf/title/kernel[. = '$grub_path']");
-
-        v2vdie __x('Didn\'t find grub entry for kernel {kernel}',
-                   kernel => $path)
-            unless defined($aug_path);
-
-        $aug_path =~ m{/files$grub_conf/title(?:\[(\d+)\])?/kernel}
-            or die($aug_path);
-        my $grub_index = defined($1) ? $1 - 1 : 0;
-
-        $g->aug_set("/files$grub_conf/default", $grub_index);
-        $g->aug_save();
-    };
-    augeas_error($g, $@) if ($@);
-}
-
-# For Grub legacy, all we have to do is re-install grub in the correct place.
-sub convert_efi
-{
-    my $self = shift;
-    my ($device) = @_;
-
-    my $g = $self->{g};
-
-    $g->cp('/etc/grub.conf', '/boot/grub/grub.conf');
-    $g->ln_sf('/boot/grub/grub.conf', '/etc/grub.conf');
-
-    # Reload to pick up grub.conf in its new location
-    eval { $g->aug_load() };
-    augeas_error($g, $@) if ($@);
-
-    $g->command(['grub-install', $device]);
-}
-
-
-# Methods for inspecting and manipulating grub2. Note that we don't actually
-# 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;
-
-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);
-
-sub new
-{
-    my $class = shift;
-    my ($g, $root, $config) = @_;
-
-    my $self = {};
-    bless($self, $class);
-
-    $self->{g} = $g;
-    $self->{root} = $root;
-    $self->{config} = $config;
-
-    # Look for an EFI configuration
-    foreach my $cfg ($g->glob_expand('/boot/efi/EFI/*/grub.cfg')) {
-        $self->check_efi();
-    }
-
-    # Check we have a grub2 configuration
-    if ($g->exists('/boot/grub2/grub.cfg')) {
-        $self->{cfg} = '/boot/grub2/grub.cfg';
-    }
-    die unless exists $self->{cfg};
-
-    return $self;
-}
-
-sub list_kernels
-{
-    my $self = shift;
-    my $g = $self->{g};
-
-    my @kernels;
-
-    # Start by adding the default kernel
-    my $default = $g->command(['grubby', '--default-kernel']);
-    chomp($default);
-    push(@kernels, $default) if length($default) > 0;
-
-    # This is how the grub2 config generator enumerates kernels
-    foreach my $kernel ($g->glob_expand('/boot/kernel-*'),
-                        $g->glob_expand('/boot/vmlinuz-*'),
-                        $g->glob_expand('/vmlinuz-*'))
-    {
-        push(@kernels, $kernel)
-           unless $kernel =~ /\.(?:dpkg-.*|rpmsave|rpmnew)$/;
-    }
-
-    return @kernels;
-}
-
-sub update_console
-{
-    my $self = shift;
-    my ($remove) = @_;
-
-    my $g = $self->{g};
-
-    my $cmdline =
-        eval { $g->aug_get('/files/etc/sysconfig/grub/GRUB_CMDLINE_LINUX') };
-
-    if (defined($cmdline) && $cmdline =~ /\bconsole=(?:x|h)vc0\b/) {
-        if ($remove) {
-            $cmdline =~ s/\bconsole=(?:x|h)vc0\b\s*//g;
-        } else {
-            $cmdline =~ s/\bconsole=(?:x|h)vc0\b/console=ttyS0/g;
-        }
-
-        eval {
-            $g->aug_set('/files/etc/sysconfig/grub/GRUB_CMDLINE_LINUX', $cmdline);
-            $g->aug_save();
-        };
-        augeas_error($g, $@) if ($@);
-
-        # We need to re-generate the grub config if we've updated this file
-        $g->command(['grub2-mkconfig', '-o', $self->{cfg}]);
-    }
-}
-
-sub check
-{
-    # Nothing required for grub2
-}
-
-sub write
-{
-    my $self = shift;
-    my ($path) = @_;
-
-    my $g = $self->{g};
-
-    my $default = $g->command(['grubby', '--default-kernel']);
-    chomp($default);
-
-    if ($default ne $path) {
-        $g->command(['grubby', '--set-default', $path]);
-    }
-}
-
-# For grub2, we :
-#   Turn the EFI partition into a BIOS Boot Partition
-#   Remove the former EFI partition from fstab
-#   Install the non-EFI version of grub
-#   Install grub2 in the BIOS Boot Partition
-#   Regenerate grub.cfg
-sub convert_efi
-{
-    my $self = shift;
-    my ($device) = @_;
-
-    my $g = $self->{g};
-
-    # EFI systems boot using grub2-efi, and probably don't have the base grub2
-    # package installed.
-    Sys::VirtConvert::Convert::RedHat::_install_any
-        (undef, ['grub2'], undef, $g, $self->{root}, $self->{config}, $self)
-        or v2vdie __x('Failed to install non-EFI grub2');
-
-    # Relabel the EFI boot partition as a BIOS boot partition
-    $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']")) {
-        $g->aug_rm($node);
-    }
-    eval { $g->aug_save(); };
-    augeas_error($g, $@) if $@;
-
-    # Install grub2 in the BIOS boot partition. This overwrites the previous
-    # contents of the EFI boot partition.
-    $g->command(['grub2-install', $device]);
-
-    # Re-generate the grub2 config, and put it in the correct place
-    $g->command(['grub2-mkconfig', '-o', '/boot/grub2/grub.cfg']);
-}
-
-
-package Sys::VirtConvert::Converter::RedHat;
-
-use Sys::VirtConvert::Util qw(:DEFAULT augeas_error scsi_first_cmp);
-use Locale::TextDomain 'virt-v2v';
-
-=pod
-
-=head1 NAME
-
-Sys::VirtConvert::Converter::RedHat - Convert a Red Hat based guest to run on KVM
-
-=head1 SYNOPSIS
-
- use Sys::VirtConvert::Converter;
-
- Sys::VirtConvert::Converter->convert($g, $root, $meta);
-
-=head1 DESCRIPTION
-
-Sys::VirtConvert::Converter::RedHat converts a Red Hat based guest to use KVM.
-
-=head1 METHODS
-
-=over
-
-=cut
-
-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)$/);
-}
-
-=item Sys::VirtConvert::Converter::RedHat->can_handle(g, root)
-
-Return 1 if Sys::VirtConvert::Converter::RedHat can convert the given guest
-
-=cut
-
-sub can_handle
-{
-    my $class = shift;
-
-    my ($g, $root) = @_;
-
-    return ($g->inspect_get_type($root) eq 'linux' &&
-            (_is_rhel_family($g, $root) || $g->inspect_get_distro($root) eq 'fedora'));
-}
-
-=item Sys::VirtConvert::Converter::RedHat->convert(g, root, config, meta, options)
-
-Convert a Red Hat based guest. Assume that can_handle has previously returned 1.
-
-=over
-
-=item g
-
-An initialised Sys::Guestfs handle
-
-=item root
-
-The root device of this operating system.
-
-=item config
-
-An initialised Sys::VirtConvert::Config
-
-=item meta
-
-Guest metadata.
-
-=item options
-
-A hashref of options which can influence the conversion
-
-=back
-
-=cut
-
-sub convert
-{
-    my $class = shift;
-
-    my ($g, $root, $config, $meta, $options) = @_;
-
-    _clean_rpmdb($g);
-    _init_selinux($g);
-    _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) }
-        unless defined($grub);
-    v2vdie __('No grub configuration found') unless defined($grub);
-
-    # Un-configure HV specific attributes which don't require a direct
-    # replacement
-    _unconfigure_hv($g, $root);
-
-    # Try to install the virtio capability
-    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)) {
-        logmsg WARN, __('Failed to install user-custom packages');
-    }
-
-    # Configure the rest of the system
-    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);
-    _configure_kernel_modules($g, $virtio);
-    _configure_boot($kernel, $virtio, $g, $root, $grub);
-
-    my %guestcaps;
-
-    $guestcaps{block} = $virtio == 1 ? 'virtio' : 'ide';
-    $guestcaps{net}   = $virtio == 1 ? 'virtio' : 'e1000';
-    $guestcaps{arch}  = _get_os_arch($g, $root, $grub);
-    $guestcaps{acpi}  = _supports_acpi($g, $root, $guestcaps{arch});
-
-    return \%guestcaps;
-}
-
-sub _init_selinux
-{
-    my ($g) = @_;
-
-    # 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.
-    $g->touch('/.autorelabel');
-}
-
-sub _init_augeas
-{
-    my ($g) = @_;
-
-    # Initialise augeas
-    eval { $g->aug_init("/", 1) };
-    augeas_error($g, $@) if ($@);
-}
-
-# Execute an augeas modprobe query against all possible modprobe locations
-sub _aug_modprobe
-{
-    my ($g, $query) = @_;
-
-    my @paths;
-    for my $pattern ('/files/etc/conf.modules/alias',
-                     '/files/etc/modules.conf/alias',
-                     '/files/etc/modprobe.conf/alias',
-                     '/files/etc/modprobe.d/*/alias') {
-        push(@paths, $g->aug_match($pattern.'['.$query.']'));
-    }
-
-    return @paths;
-}
-
-# Check how new modules should be configured. Possibilities, in descending
-# order of preference, are:
-#   modprobe.d/
-#   modprobe.conf
-#   modules.conf
-#   conf.modules
-sub _discover_modpath
-{
-    my ($g) = @_;
-
-    my $modpath;
-
-    # 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)) {
-            $modpath = $file;
-        }
-    }
-
-    if($g->exists("/etc/modprobe.conf")) {
-        $modpath = "modprobe.conf";
-    }
-
-    # If the modprobe.d directory exists, create new entries in
-    # modprobe.d/virtv2v-added.conf
-    if($g->exists("/etc/modprobe.d")) {
-        $modpath = "modprobe.d/virtv2v-added.conf";
-    }
-
-    v2vdie __('Unable to find any valid modprobe configuration')
-        unless defined($modpath);
-
-    return $modpath;
-}
-
-sub _configure_kernel_modules
-{
-    my ($g, $virtio, $modpath) = @_;
-
-    # Make a note of whether we've added scsi_hostadapter
-    # We need this on RHEL 4/virtio because mkinitrd can't detect root on
-    # virtio. For simplicity we always ensure this is set for virtio disks.
-    my $scsi_hostadapter = 0;
-
-    eval {
-        foreach my $path (_aug_modprobe($g, ". =~ regexp('eth[0-9]+')"))
-        {
-            $g->aug_set($path.'/modulename',
-                        $virtio == 1 ? 'virtio_net' : 'e1000');
-        }
-
-        my @paths = _aug_modprobe($g, ". =~ regexp('scsi_hostadapter.*')");
-        if ($virtio) {
-            # There's only 1 scsi controller in the converted guest.
-            # Convert only the first scsi_hostadapter entry to virtio.
-
-            # Note that we delete paths in reverse order. This means we don't
-            # have to worry about alias indices being changed.
-            while (@paths > 1) {
-                $g->aug_rm(pop(@paths));
-            }
-
-            if (@paths == 1) {
-                $g->aug_set(pop(@paths).'/modulename', 'virtio_blk');
-                $scsi_hostadapter = 1;
-            }
-        }
-
-        else {
-            # There's no scsi controller in an IDE guest
-            while (@paths) {
-                $g->aug_rm(pop(@paths));
-            }
-        }
-
-        # Display a warning about any leftover xen modules which we haven't
-        # converted
-        my @xen_modules = qw(xennet xen-vnif xenblk xen-vbd);
-        my $query = '('.join('|', @xen_modules).')';
-
-        foreach my $path (_aug_modprobe($g, "modulename =~ regexp('$query')")) {
-            my $device = $g->aug_get($path);
-            my $module = $g->aug_get($path.'/modulename');
-
-            logmsg WARN, __x("Don't know how to update ".
-                             '{device}, which loads the {module} module.',
-                             device => $device, module => $module);
-        }
-
-        # Add an explicit scsi_hostadapter if it wasn't there before
-        if ($virtio && !$scsi_hostadapter) {
-            my $modpath = _discover_modpath($g);
-
-            $g->aug_set("/files$modpath/alias[last()+1]",
-                        'scsi_hostadapter');
-            $g->aug_set("/files$modpath/alias[last()]/modulename",
-                        'virtio_blk');
-        }
-
-        $g->aug_save();
-    };
-    augeas_error($g, $@) if $@;
-}
-
-# We 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.
-#
-# If the target doesn't support a serial console, we want to remove all
-# references to it instead.
-sub _configure_console
-{
-    my ($g, $grub, $remove) = @_;
-
-    # Look for gettys which use xvc0 or hvc0
-    # RHEL 6 doesn't use /etc/inittab, but this doesn't hurt
-    foreach my $augpath ($g->aug_match("/files/etc/inittab/*/process")) {
-        my $proc = $g->aug_get($augpath);
-
-        # If the process mentions xvc0, change it to ttyS0
-        if ($proc =~ /\b(x|h)vc0\b/) {
-            if ($remove) {
-                $g->aug_rm($augpath.'/..');
-            } else {
-                $proc =~ s/\b(x|h)vc0\b/ttyS0/g;
-                $g->aug_set($augpath, $proc);
-            }
-        }
-
-        if ($remove && $proc =~ /\bttyS0\b/) {
-            $g->aug_rm($augpath.'/..');
-        }
-    }
-
-    # Replace any mention of xvc0 or hvc0 in /etc/securetty with ttyS0
-    foreach my $augpath ($g->aug_match('/files/etc/securetty/*')) {
-        my $tty = $g->aug_get($augpath);
-
-        if($tty eq "xvc0" || $tty eq "hvc0") {
-            if ($remove) {
-                $g->aug_rm($augpath);
-            } else {
-                $g->aug_set($augpath, 'ttyS0');
-            }
-        }
-
-        if ($remove && $tty eq 'ttyS0') {
-            $g->aug_rm($augpath);
-        }
-    }
-
-    $grub->update_console($remove);
-
-    eval { $g->aug_save() };
-    augeas_error($g, $@) if ($@);
-}
-
-sub _configure_display_driver
-{
-    my ($g, $root, $config, $meta, $grub) = @_;
-
-    # Update the display driver if it exists
-    my $updated = 0;
-    eval {
-        my $xorg;
-
-        # Check which X configuration we have, and make augeas load it if
-        # necessary
-        if (! $g->exists('/etc/X11/xorg.conf') &&
-            $g->exists('/etc/X11/XF86Config'))
-        {
-            $g->aug_set('/augeas/load/Xorg/incl[last()+1]',
-                        '/etc/X11/XF86Config');
-
-            # Reload to pick up the new configuration
-            $g->aug_load();
-
-            $xorg = '/etc/X11/XF86Config';
-        } else {
-            $xorg = '/etc/X11/xorg.conf';
-        }
-
-        foreach my $path ($g->aug_match('/files'.$xorg.'/Device/Driver')) {
-            $g->aug_set($path, 'qxl');
-            $updated = 1;
-        }
-
-        # Remove VendorName and BoardName if present
-        foreach my $path
-            ($g->aug_match('/files'.$xorg.'/Device/VendorName'),
-             $g->aug_match('/files'.$xorg.'/Device/BoardName'))
-        {
-            $g->aug_rm($path);
-        }
-
-        $g->aug_save();
-    };
-
-    # 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 ($updated &&
-        ($g->exists('/usr/bin/X') || $g->exists('/usr/bin/X11/X')) &&
-        !_install_capability('qxl', $g, $root, $config, $meta, $grub))
-    {
-        logmsg WARN, __('Display driver was updated to qxl, but unable to '.
-                        'install qxl driver. X may not function correctly');
-    }
-}
-
-# 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.
-sub _clean_rpmdb
-{
-    my $g = shift;
-
-    foreach my $f ($g->glob_expand('/var/lib/rpm/__db.00?')) {
-        $g->rm($f);
-    }
-}
-
-# Use various methods to try to work out what Linux kernel we've got.
-# Returns a hashref containing:
-#   path => path to kernel (same as $path variable passed in)
-#   package => base package name (eg. "kernel", "kernel-PAE")
-#   version => version string
-#   modules => array ref list of modules (paths to *.ko files)
-#   arch => architecture of the kernel
-sub _inspect_linux_kernel
-{
-    my ($g, $path) = @_;
-
-    my %kernel = ();
-
-    $kernel{path} = $path;
-
-    # 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);;
-
-    # 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/) {
-        $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 {
-        if($path =~ m{/boot/vmlinuz-(.*)}) {
-            $version = $1;
-
-            # Check /lib/modules/$version exists
-            if(!$g->is_dir("/lib/modules/$version")) {
-                warn __x("Didn't find modules directory {modules} for kernel ".
-                         "{path}\n", modules => "/lib/modules/$version",
-                         path => $path);
-
-                # Give up
-                return undef;
-            }
-        } else {
-            warn __x("Couldn't guess kernel version number from path for ".
-                     "kernel {path}\n", path => $path);
-
-            # Give up
-            return undef;
-        }
-    }
-
-    $kernel{version} = $version;
-
-    # List modules.
-    my @modules;
-    my $any_module;
-    my $prefix = "/lib/modules/$version";
-    foreach my $module ($g->find ($prefix)) {
-        if ($module =~ m{/([^/]+)\.(?:ko|o)$}) {
-            $any_module = "$prefix$module" unless defined $any_module;
-            push @modules, $1;
-        }
-    }
-
-    $kernel{modules} = \@modules;
-
-    # Determine kernel architecture by looking at the arch
-    # of any kernel module.
-    $kernel{arch} = $g->file_architecture ($any_module);
-
-    return \%kernel;
-}
-
-sub _configure_kernel
-{
-    my ($virtio, $g, $root, $config, $meta, $grub) = @_;
-
-    # Pick first appropriate kernel returned by list_kernels
-    my $boot_kernel;
-    foreach my $path ($grub->list_kernels()) {
-        my $kernel = _inspect_linux_kernel($g, $path);
-        my $version = $kernel->{version};
-
-        # Skip foreign kernels
-        next if _is_hv_kernel($g, $version);
-
-        # If we're configuring virtio, check this kernel supports it
-        next if ($virtio && !_supports_virtio($version, $g));
-
-        $boot_kernel = $version;
-        last;
-    }
-
-    # 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));
-
-    # If none of the installed kernels are appropriate, install a new one
-    if(!defined($boot_kernel)) {
-        my ($kernel_pkg, $kernel_arch, undef) =
-           _discover_kernel($g, $root, $grub);
-
-        # 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") {
-            $kernel_pkg = _get_replacement_kernel_name($g, $root,
-                                                       $kernel_arch, $meta);
-
-            # Check there isn't already one installed
-            my ($kernel) = _get_installed("$kernel_pkg.$kernel_arch", $g);
-            $boot_kernel = $kernel->[1].'-'.$kernel->[2].'.'.$kernel_arch
-                if defined($kernel);
-        }
-
-        if (!defined($boot_kernel)) {
-            # List of kernels before the new kernel installation
-            my @k_before = $g->glob_expand('/boot/vmlinuz-*');
-
-            if (_install_any([$kernel_pkg, $kernel_arch], undef, undef,
-                             $g, $root, $config, $grub))
-            {
-                # Figure out which kernel has just been installed
-                my $version;
-                foreach my $k ($g->glob_expand('/boot/vmlinuz-*')) {
-                    if (!grep(/^$k$/, @k_before)) {
-                        # Check which directory in /lib/modules the kernel rpm
-                        # creates
-                        foreach my $file
-                                ($g->command_lines(['rpm', '-qlf', $k]))
-                        {
-                            next unless ($file =~ m{^/lib/modules/([^/]+)$});
-
-                            if ($g->is_dir("/lib/modules/$1")) {
-                                $boot_kernel = $1;
-                                last;
-                            }
-                        }
-
-                        last if defined($boot_kernel);
-                    }
-                }
-
-                v2vdie __x('Couldn\'t determine version of installed kernel')
-                    unless defined($boot_kernel);
-            } else {
-                v2vdie __x('Failed to find a {name} package to install',
-                           name => "kernel_pkg.$kernel_arch");
-            }
-        }
-    }
-
-    # Check we have a bootable kernel.
-    v2vdie __('No bootable kernels installed, and no replacement '.
-              "is available.\nUnable to continue.")
-        unless defined($boot_kernel);
-
-    # Ensure DEFAULTKERNEL is set to boot kernel package name
-    # It's not fatal if this rpm command fails
-    my ($kernel_pkg) =
-        eval { $g->command_lines(['rpm', '-qf', "/lib/modules/$boot_kernel",
-                                         '--qf', '%{NAME}\n']) };
-    if (defined($kernel_pkg) && $g->exists('/etc/sysconfig/kernel')) {
-        eval {
-            foreach my $path ($g->aug_match('/files/etc/sysconfig/kernel'.
-                                            '/DEFAULTKERNEL/value'))
-            {
-                $g->aug_set($path, $kernel_pkg);
-            }
-
-            $g->aug_save();
-        };
-        # Propagate augeas errors
-        augeas_error($g, $@) if ($@);
-    }
-
-    return $boot_kernel;
-}
-
-sub _configure_boot
-{
-    my ($kernel, $virtio, $g, $root, $grub) = @_;
-
-    if($virtio) {
-        # The order of modules here is deliberately the same as the order
-        # specified in the postinstall script of kmod-virtio in RHEL3. The
-        # 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");
-    } else {
-        _prepare_bootable($g, $root, $grub, $kernel, "sym53c8xx");
-    }
-}
-
-# Get the target architecture from the default boot kernel
-sub _get_os_arch
-{
-    my ($g, $root, $grub) = @_;
-
-    # Get the arch of the default kernel
-    my @kernels = $grub->list_kernels();
-    my $path = $kernels[0] if @kernels > 0;
-    my $kernel = _inspect_linux_kernel($g, $path) if defined($path);
-    my $arch = $kernel->{arch} if defined($kernel);
-
-    # Use the libguestfs-detected arch if the above failed
-    $arch = $g->inspect_get_arch($root) unless defined($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$/);
-
-    return $arch;
-}
-
-# Determine if a specific kernel is hypervisor-specific
-sub _is_hv_kernel
-{
-    my ($g, $version) = @_;
-
-    # Xen PV kernels can be distinguished from other kernels by their inclusion
-    # of the xennet driver
-    foreach my $entry ($g->find("/lib/modules/$version/")) {
-        return 1 if $entry =~ /(^|\/)xennet\.k?o$/;
-    }
-
-    return 0;
-}
-
-sub _remove_applications
-{
-    my ($g, @apps) = @_;
-
-    # Nothing to do if we were given an empty list
-    return if scalar(@apps) == 0;
-
-    $g->command(['rpm', '-e', @apps]);
-
-    # Make augeas reload in case the removal changed anything
-    eval { $g->aug_load() };
-    augeas_error($g, $@) if ($@);
-}
-
-sub _get_application_owner
-{
-    my ($file, $g) = @_;
-
-    return $g->command(['rpm', '-qf', $file]);
-}
-
-sub _unconfigure_hv
-{
-    my ($g, $root) = @_;
-
-    my @apps = $g->inspect_list_applications($root);
-
-    _unconfigure_xen($g, \@apps);
-    _unconfigure_vbox($g, \@apps);
-    _unconfigure_vmware($g, \@apps);
-    _unconfigure_citrix($g, \@apps);
-}
-
-# Unconfigure Xen specific guest modifications
-sub _unconfigure_xen
-{
-    my ($g, $apps) = @_;
-
-    # Look for kmod-xenpv-*, which can be found on RHEL 3 machines
-    my @remove;
-    foreach my $app (@$apps) {
-        my $name = $app->{app_name};
-
-        if($name =~ /^kmod-xenpv(-.*)?$/) {
-            push(@remove, $name);
-        }
-    }
-    _remove_applications($g, @remove);
-
-    # Undo related nastiness if kmod-xenpv was installed
-    if(scalar(@remove) > 0) {
-        # kmod-xenpv modules may have been manually copied to other kernels.
-        # Hunt them down and destroy them.
-        foreach my $dir (grep(m{/xenpv$}, $g->find('/lib/modules'))) {
-            $dir = '/lib/modules/'.$dir;
-
-            # Check it's a directory
-            next unless($g->is_dir($dir));
-
-            # Check it's not owned by an installed application
-            eval { _get_application_owner($dir, $g) };
-
-            # Remove it if _get_application_owner didn't find an owner
-            if($@) {
-                $g->rm_rf($dir);
-            }
-        }
-
-        # rc.local may contain an insmod or modprobe of the xen-vbd driver
-        my @rc_local = eval { $g->read_lines('/etc/rc.local') };
-        if ($@) {
-            logmsg WARN, __x('Unable to open /etc/rc.local: {error}',
-                             error => $@);
-        }
-
-        else {
-            my $size = 0;
-
-            foreach my $line (@rc_local) {
-                if($line =~ /\b(insmod|modprobe)\b.*\bxen-vbd/) {
-                    $line = '#'.$line;
-                }
-
-                $size += length($line) + 1;
-            }
-
-            $g->write_file('/etc/rc.local', join("\n", @rc_local)."\n", $size);
-        }
-    }
-}
-
-# Unconfigure VirtualBox specific guest modifications
-sub _unconfigure_vbox
-{
-    my ($g, $apps) = @_;
-
-    # Uninstall VirtualBox Guest Additions
-    my @remove;
-    foreach my $app (@$apps) {
-        my $name = $app->{app_name};
-
-        if ($name eq "virtualbox-guest-additions") {
-            push(@remove, $name);
-        }
-    }
-    _remove_applications($g, @remove);
-
-    # VirtualBox Guest Additions may have been installed from tarball, in which
-    # case the above won't detect it. Look for the uninstall tool, and run it
-    # if it's present.
-    #
-    # Note that it's important we do this early in the conversion process, as
-    # this uninstallation script naively overwrites configuration files with
-    # versions it cached prior to installation.
-    my $vboxconfig = '/var/lib/VBoxGuestAdditions/config';
-    if ($g->exists($vboxconfig)) {
-        my $vboxuninstall;
-        foreach (split /\n/, $g->cat($vboxconfig)) {
-            if ($_ =~ /^INSTALL_DIR=(.*$)/) {
-                $vboxuninstall = $1 . '/uninstall.sh';
-            }
-        }
-        if ($g->exists($vboxuninstall)) {
-            eval { $g->command([$vboxuninstall]) };
-            logmsg WARN, __x('VirtualBox Guest Additions were detected, but '.
-                             'uninstallation failed. The error message was: '.
-                             '{error}', error => $@) if $@;
-
-            # Reload augeas to detect changes made by vbox tools uninstallation
-            eval { $g->aug_load() };
-            augeas_error($g, $@) if $@;
-        }
-    }
-}
-
-# Unconfigure VMware specific guest modifications
-sub _unconfigure_vmware
-{
-    my ($g, $apps) = @_;
-
-    # Look for any configured vmware yum repos, and disable them
-    foreach my $repo ($g->aug_match(
-        '/files/etc/yum.repos.d/*/*'.
-        '[baseurl =~ regexp(\'https?://([^/]+\.)?vmware\.com/.*\')]'))
-    {
-        eval {
-            $g->aug_set($repo.'/enabled', 0);
-            $g->aug_save();
-        };
-        augeas_error($g, $@) if ($@);
-    }
-
-    # Uninstall VMwareTools
-    my @remove;
-    my @libraries;
-    foreach my $app (@$apps) {
-        my $name = $app->{app_name};
-
-        if ($name =~ /^vmware-tools-/) {
-            if ($name =~ /^vmware-tools-libraries-/) {
-                push(@libraries, $name);
-            } else {
-                push (@remove, $name);
-            }
-        }
-        elsif ($name eq "VMwareTools" || $name =~ /^(?:kmod-)?vmware-tools-/) {
-            push(@remove, $name);
-        }
-    }
-
-    # 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
-    if (@libraries > 0) {
-        # We only support removal of these libraries packages on systems which
-        # use yum.
-        if ($g->exists('/usr/bin/yum')) {
-            _net_run($g, sub {
-                foreach my $library (@libraries) {
-                    eval {
-                        my @provides = $g->command_lines
-                                (['rpm', '-q', '--provides', $library]);
-
-                        # The packages also explicitly provide themselves.
-                        # Filter this out.
-                        @provides = grep {$_ !~ /$library/}
-
-                        # Trim whitespace
-                                    map { s/^\s*(\S+)\s*$/$1/; $_ } @provides;
-
-                        # Install the dependencies with yum. We use yum
-                        # explicitly here, as up2date wouldn't work anyway and
-                        # local install is impractical due to the large number
-                        # of required dependencies out of our control.
-                        my %alts;
-                        foreach my $alt ($g->command_lines
-                                       (['yum', '-q', 'resolvedep', @provides]))
-                        {
-                            $alts{$alt} = 1;
-                        }
-
-                        my @replacements = keys(%alts);
-                        $g->command(['yum', 'install', '-y', @replacements])
-                            if @replacements > 0;
-
-                        push(@remove, $library);
-                    };
-                    logmsg WARN, __x('Failed to install replacement '.
-                                     'dependencies for {lib}. Package will '.
-                                     'not be uninstalled. Error was: {error}',
-                                     lib => $library, error => $@) if $@;
-                }
-            });
-        }
-    }
-
-    _remove_applications($g, @remove);
-
-    # VMwareTools may have been installed from tarball, in which case the above
-    # won't detect it. Look for the uninstall tool, and run it if it's present.
-    #
-    # Note that it's important we do this early in the conversion process, as
-    # this uninstallation script naively overwrites configuration files with
-    # versions it cached prior to installation.
-    my $vmwaretools = '/usr/bin/vmware-uninstall-tools.pl';
-    if ($g->exists($vmwaretools)) {
-        eval { $g->command([$vmwaretools]) };
-        logmsg WARN, __x('VMware Tools was detected, but uninstallation '.
-                         'failed. The error message was: {error}',
-                         error => $@) if $@;
-
-        # Reload augeas to detect changes made by vmware tools uninstallation
-        eval { $g->aug_load() };
-        augeas_error($g, $@) if $@;
-    }
-}
-
-sub _unconfigure_citrix
-{
-    my ($g, $apps) = @_;
-
-    # Look for xe-guest-utilities*
-    my @remove;
-    foreach my $app (@$apps) {
-        my $name = $app->{app_name};
-
-        if($name =~ /^xe-guest-utilities(-.*)?$/) {
-            push(@remove, $name);
-        }
-    }
-    _remove_applications($g, @remove);
-
-    # Installing these guest utilities automatically unconfigures ttys in
-    # /etc/inittab if the system uses it. We need to put them back.
-    if (scalar(@remove) > 0) {
-        eval {
-            my $updated = 0;
-            for my $commentp ($g->aug_match('/files/etc/inittab/#comment')) {
-                my $comment = $g->aug_get($commentp);
-
-                # 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.
-                next unless $comment =~ /^([1-6]):([2-5]+):respawn:(.*)/;
-
-                my $name = $1;
-                my $runlevels = $2;
-                my $process = $3;
-
-                next unless $process =~ /getty/;
-
-                # Create a new entry immediately after the comment
-                $g->aug_insert($commentp, $name, 0);
-                $g->aug_set("/files/etc/inittab/$name/runlevels", $runlevels);
-                $g->aug_set("/files/etc/inittab/$name/action", 'respawn');
-                $g->aug_set("/files/etc/inittab/$name/process", $process);
-
-                # Create a variable to point to the comment node so we can
-                # delete it later. If we deleted it here it would invalidate
-                # subsquent comment paths returned by aug_match.
-                $g->aug_defvar("delete$updated", $commentp);
-
-                $updated++;
-            }
-
-            # Delete all the comments
-            my $i = 0;
-            while ($i < $updated) {
-                $g->aug_rm("\$delete$i");
-                $i++;
-            }
-
-            $g->aug_save();
-        };
-        augeas_error($g, $@) if ($@);
-    }
-}
-
-sub _install_capability
-{
-    my ($name, $g, $root, $config, $meta, $grub) = @_;
-
-    my $cap = eval { $config->match_capability($g, $root, $name) };
-    if ($@) {
-        warn($@);
-        return 0;
-    }
-
-    if (!defined($cap)) {
-        logmsg WARN, __x('{name} capability not found in configuration',
-                         name => $name);
-        return 0;
-    }
-
-    my @install;
-    my @upgrade;
-    my $kernel;
-    foreach my $name (keys(%$cap)) {
-        my $props = $cap->{$name};
-        my $ifinstalled = $props->{ifinstalled};
-
-        # Parse epoch, version and release from minversion
-        my ($min_epoch, $min_version, $min_release);
-        if (exists($props->{minversion})) {
-            eval {
-                ($min_epoch, $min_version, $min_release) =
-                    _parse_evr($props->{minversion});
-            };
-            v2vdie __x('Unrecognised format for {field} in config: '.
-                       '{value}. {field} must be in the format '.
-                       '[epoch:]version[-release].',
-                       field => 'minversion', value => $props->{minversion})
-                if $@;
-        }
-
-        # Kernels are special
-        if ($name eq 'kernel') {
-            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 (!defined($kernel_rpmver)) {
-                $kernel = [$kernel_pkg, $kernel_arch];
-            }
-
-            else {
-                my ($kernel_epoch, $kernel_ver, $kernel_release) =
-                    eval { _parse_evr($kernel_rpmver) };
-                if ($@) {
-                    # Don't die here, just make best effort to do a version
-                    # comparison by directly comparing the full strings
-                    $kernel_epoch = undef;
-                    $kernel_ver = $kernel_rpmver;
-                    $kernel_release = undef;
-
-                    $min_epoch = undef;
-                    $min_version = $props->{minversion};
-                    $min_release = undef;
-                }
-
-                # 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")
-                {
-                    $kernel_pkg =
-                        _get_replacement_kernel_name($g, $root, $kernel_arch,
-                                                     $meta);
-
-                    # Check if we've got already got an appropriate kernel
-                    my ($inst) =
-                        _get_installed("$kernel_pkg.$kernel_arch", $g);
-
-                    if (!defined($inst) ||
-                        (defined($min_version) &&
-                         _evr_cmp($inst->[0], $inst->[1], $inst->[2],
-                                  $min_epoch, $min_version, $min_release) < 0))
-                    {
-                        # filter out xen/xenU from release field
-                        if (defined($kernel_release) &&
-                            $kernel_release =~ /^(\S+?)(xen)?(U)?$/)
-                        {
-                            $kernel_release = $1;
-                        }
-
-                        # If the guest kernel is new enough, but PV, try to
-                        # replace it with an equivalent version FV kernel
-                        if (!defined($min_version) ||
-                            _evr_cmp($kernel_epoch, $kernel_ver,
-                                     $kernel_release,
-                                     $min_epoch, $min_version,
-                                     $min_release) >= 0)
-                        {
-                            $kernel = [$kernel_pkg, $kernel_arch,
-                                       $kernel_epoch, $kernel_ver,
-                                       $kernel_release];
-                        }
-
-                        # Otherwise, just grab the latest
-                        else {
-                            $kernel = [$kernel_pkg, $kernel_arch];
-                        }
-                    }
-                }
-
-                # If the kernel is too old, grab the latest replacement
-                elsif (defined($min_version) &&
-                       _evr_cmp($kernel_epoch, $kernel_ver, $kernel_release,
-                                $min_epoch, $min_version, $min_release) < 0)
-                {
-                    $kernel = [$kernel_pkg, $kernel_arch];
-                }
-            }
-        }
-
-        else {
-            my @installed = _get_installed($name, $g);
-
-            # Ignore an 'ifinstalled' dep if it's not currently installed
-            next if (@installed == 0 && $ifinstalled);
-
-            # Ok if any version is installed and no minversion was specified
-            next if (@installed > 0 && !defined($min_version));
-
-            if (defined($min_version)) {
-                # Check if any installed version meets the minimum version
-                my $found = 0;
-                foreach my $app (@installed) {
-                    my ($epoch, $version, $release) = @$app;
-
-                    if (_evr_cmp($app->[0], $app->[1], $app->[2],
-                                 $min_epoch, $min_version, $min_release) >= 0) {
-                        $found = 1;
-                        last;
-                    }
-                }
-
-                # Install the latest available version of the dep if it wasn't
-                # found
-                if (!$found) {
-                    if (@installed == 0) {
-                        push(@install, [$name]);
-                    } else {
-                        push(@upgrade, [$name]);
-                    }
-                }
-            } else {
-                push(@install, [$name]);
-            }
-        }
-    }
-
-    # Capability is already installed
-    if (!defined($kernel) && @install == 0 && @upgrade == 0) {
-        return 1;
-    }
-
-    my $success = _install_any($kernel, \@install, \@upgrade,
-                               $g, $root, $config, $grub);
-
-    return $success;
-}
-
-sub _net_run {
-    my ($g, $sub) = @_;
-
-    my $resolv_bak = $g->exists('/etc/resolv.conf');
-    $g->mv('/etc/resolv.conf', '/etc/resolv.conf.v2vtmp') if ($resolv_bak);
-
-    # XXX We should get the nameserver from the appliance here, but
-    # there's no current api other than debug to do this.
-    $g->write_file('/etc/resolv.conf', "nameserver 169.254.2.3", 0);
-
-    eval &$sub();
-    my $err = $@;
-
-    $g->mv('/etc/resolv.conf.v2vtmp', '/etc/resolv.conf') if ($resolv_bak);
-
-    die $err if $err;
-}
-
-sub _install_any
-{
-    my ($kernel, $install, $upgrade, $g, $root, $config, $grub) = @_;
-
-    # If we're installing a kernel, check which kernels are there first
-    my @k_before = $g->glob_expand('/boot/vmlinuz-*') if defined($kernel);
-
-    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);
-
-            # Fall back to local config if the above didn't work
-            $success = _install_config($kernel, $install, $upgrade,
-                                       $g, $root, $config)
-                unless ($success);
-        };
-        warn($@) if $@;
-    });
-
-    # Make augeas reload to pick up any altered configuration
-    eval { $g->aug_load() };
-    augeas_error($g, $@) if ($@);
-
-    # Installing a new kernel in RHEL 5 under libguestfs fails to add a grub
-    # entry. Find the kernel we installed and ensure it has a grub entry.
-    if (defined($kernel)) {
-        foreach my $k ($g->glob_expand('/boot/vmlinuz-*')) {
-            if (!grep(/^$k$/, @k_before)) {
-                $grub->check($k);
-                last;
-            }
-        }
-    }
-
-    return $success;
-}
-
-sub _install_up2date
-{
-    my ($kernel, $install, $upgrade, $g) = @_;
-
-    # Check this system has actions.packages
-    return 0 unless ($g->exists('/usr/bin/up2date'));
-
-    # Check this system is registered to rhn
-    return 0 unless ($g->exists('/etc/sysconfig/rhn/systemid'));
-
-    my @pkgs;
-    foreach my $pkg ($kernel, @$install, @$upgrade) {
-        next unless defined($pkg);
-
-        # up2date doesn't do arch
-        my ($name, undef, $epoch, $version, $release) = @$pkg;
-
-        $epoch   ||= "";
-        $version ||= "";
-        $release ||= "";
-
-        push(@pkgs, "['$name', '$version', '$release', '$epoch']");
-    }
-
-    eval {
-         $g->command(['/usr/bin/python', '-c',
-                     "import sys; sys.path.append('/usr/share/rhn'); ".
-                     "import actions.packages;                       ".
-                     "actions.packages.cfg['forceInstall'] = 1;      ".
-                     "ret = actions.packages.update([".join(',', @pkgs)."]); ".
-                     "sys.exit(ret[0]);                              "]);
-    };
-    if ($@) {
-        logmsg WARN, __x('Failed to install packages using up2date. '.
-                         'Error message was: {error}', error => $@);
-        return 0;
-    }
-
-    return 1;
-}
-
-sub _install_yum
-{
-    my ($kernel, $install, $upgrade, $g) = @_;
-
-    # Check this system has yum installed
-    return 0 unless ($g->exists('/usr/bin/yum'));
-
-    # Install or upgrade 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 upgrade
-    if (defined($kernel)) {
-        my @installed = _get_installed($kernel->[0], $g);
-
-        # Don't modify the contents of $install and $upgrade 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($upgrade) ? @$upgrade : ();
-            push(@tmp, $kernel);
-            $upgrade = \@tmp;
-        }
-    }
-
-    my $success = 1;
-    YUM: foreach my $task (
-        [ "install", $install, qr/(^No package|already installed)/ ],
-        [ "upgrade", $upgrade, qr/^No Packages/ ]
-    ) {
-        my ($action, $list, $failure) = @$task;
-
-        # We can't do these all in a single transaction, because yum offers us
-        # no way to tell if a transaction partially succeeded
-        foreach my $entry (@$list) {
-            next unless (defined($entry));
-
-            # You can't specify epoch without architecture to yum, so we just
-            # ignore epoch and hope
-            my ($name, undef, undef, $version, $release) = @$entry;
-
-            # Construct n-v-r
-            my $pkg = $name;
-            $pkg .= "-$version" if (defined($version));
-            $pkg .= "-$release" if (defined($release));
-
-            my @output =
-                eval { $g->sh_lines("LANG=C /usr/bin/yum -y $action $pkg") };
-            if ($@) {
-                logmsg WARN, __x('Failed to install packages using yum. '.
-                                 'Output was: {output}', output => $@);
-                $success = 0;
-                last YUM;
-            }
-
-            foreach my $line (@output) {
-                # Yum probably just isn't configured. Don't bother with an error
-                # message
-                if ($line =~ /$failure/) {
-                    $success = 0;
-                    last YUM;
-                }
-            }
-        }
-    }
-
-    return $success;
-}
-
-sub _install_config
-{
-    my ($kernel_naevr, $install, $upgrade, $g, $root, $config) = @_;
-
-    my ($kernel, $user);
-    if (defined($kernel_naevr)) {
-        my ($kernel_pkg, $kernel_arch) = @$kernel_naevr;
-
-        ($kernel, $user) =
-            $config->match_app($g, $root, $kernel_pkg, $kernel_arch);
-    } else {
-        $user = [];
-    }
-
-    foreach my $pkg (@$install, @$upgrade) {
-        push(@$user, $pkg->[0]);
-    }
-
-    my @missing;
-    if (defined($kernel)) {
-        my $transfer_path = $config->get_transfer_path($kernel);
-        if (!defined($transfer_path) || !$g->exists($transfer_path)) {
-            push(@missing, $kernel);
-        }
-    }
-
-    my @user_paths = _get_deppaths($g, $root, $config,
-                                   \@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));
-    }
-
-    return 1;
-}
-
-# Install a set of rpms
-sub _install_rpms
-{
-    local $_;
-    my ($g, $config, $upgrade, @rpms) = @_;
-
-    # Nothing to do if we got an empty set
-    return if(scalar(@rpms) == 0);
-
-    # All paths are relative to the transfer mount. Need to make them absolute.
-    # No need to check get_transfer_path here as all paths have been previously
-    # checked
-    @rpms = map { $_ = $config->get_transfer_path($_) } @rpms;
-
-    $g->command(['rpm', $upgrade == 1 ? '-U' : '-i', @rpms]);
-
-    # Reload augeas in case the rpm installation changed anything
-    eval { $g->aug_load() };
-    augeas_error($g, $@) if($@);
-}
-
-# Return a list of dependency paths which need to be installed for the given
-# apps
-sub _get_deppaths
-{
-    my ($g, $root, $config, $missing, $arch, @apps) = @_;
-
-    my %required;
-    foreach my $app (@apps) {
-        my ($path, $deps) = $config->match_app($g, $root, $app, $arch);
-
-        my $transfer_path = $config->get_transfer_path($path);
-        my $exists = defined($transfer_path) && $g->exists($transfer_path);
-
-        if (!$exists) {
-            push(@$missing, $path);
-        }
-
-        if (!$exists || !_newer_installed($transfer_path, $g, $config)) {
-            $required{$path} = 1;
-
-            foreach my $deppath (_get_deppaths($g, $root, $config,
-                                               $missing, $arch, @$deps))
-            {
-                $required{$deppath} = 1;
-            }
-        }
-
-        # 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;
-
-            # It's not an error if no i386 package is available
-            eval {
-                ($path, $deps) = $config->match_app($g, $root, $app, 'i386');
-            };
-
-            if (defined($path)) {
-                $transfer_path = $config->get_transfer_path($path);
-                if (!defined($transfer_path) || !$g->exists($transfer_path)) {
-                    push(@$missing, $path);
-
-                    foreach my $deppath (_get_deppaths($g, $root, $config,
-                                                      $missing, 'i386', @$deps))
-                    {
-                        $required{$deppath} = 1;
-                    }
-                }
-            }
-        }
-    }
-
-    return keys(%required);
-}
-
-# Return 1 if the requested rpm, or a newer version, is installed
-# Return 0 otherwise
-sub _newer_installed
-{
-    my ($rpm, $g, $config) = @_;
-
-    my ($name, $epoch, $version, $release, $arch) =
-        _get_nevra($rpm, $g, $config);
-
-    my @installed = _get_installed("$name.$arch", $g);
-
-    # Search installed rpms matching <name>.<arch>
-    foreach my $pkg (@installed) {
-        next if _evr_cmp($pkg->[0], $pkg->[1], $pkg->[2],
-                         $epoch, $version, $release) < 0;
-        return 1;
-    }
-
-    return 0;
-}
-
-sub _get_nevra
-{
-    my ($rpm, $g, $config) = @_;
-
-    # Get NEVRA for the rpm to be installed
-    my $nevra = $g->command(['rpm', '-qp', '--qf',
-                             '%{NAME} %{EPOCH} %{VERSION} %{RELEASE} %{ARCH}',
-                             $rpm]);
-
-    $nevra =~ /^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)$/
-        or die("Unexpected return from rpm command: $nevra");
-    my ($name, $epoch, $version, $release, $arch) = ($1, $2, $3, $4, $5);
-
-    # Ensure epoch is always numeric
-    $epoch = 0 if('(none)' eq $epoch);
-
-    return ($name, $epoch, $version, $release, $arch);
-}
-
-# Inspect the guest to work out what kernel package is in use
-# Returns ($kernel_pkg, $kernel_arch)
-sub _discover_kernel
-{
-    my ($g, $root, $grub) = @_;
-
-    # Get a current bootable kernel, preferrably the default
-    my $kernel_pkg;
-    my $kernel_arch;
-    my $kernel_ver;
-
-    foreach my $path ($grub->list_kernels()) {
-        my $kernel = _inspect_linux_kernel($g, $path);
-
-        # Check its architecture is known
-        $kernel_arch = $kernel->{arch};
-        next unless (defined($kernel_arch));
-
-        # Get the kernel package name
-        $kernel_pkg = $kernel->{package};
-
-        # Get the kernel package version
-        $kernel_ver = $kernel->{version};
-
-        last;
-    }
-
-    # 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
-    $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);
-
-    return ($kernel_pkg, $kernel_arch, $kernel_ver);
-}
-
-sub _get_replacement_kernel_name
-{
-    my ($g, $root, $arch, $meta) = @_;
-
-    # 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';
-        }
-
-        # There's only 1 kernel package on RHEL 5 x86_64
-        else {
-            return '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';
-            }
-
-            # 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
-
-    # XXX: Could do with a history of Fedora kernels in here
-
-    # For other distros, be conservative and just return 'kernel'
-    return 'kernel';
-}
-
-sub _get_installed
-{
-    my ($name, $g) = @_;
-
-    my $rpmcmd = ['rpm', '-q', '--qf', '%{EPOCH} %{VERSION} %{RELEASE}\n',
-                  $name];
-    my @output = eval { $g->command_lines($rpmcmd) };
-    if ($@) {
-        # RPM command returned non-zero. This might be because there was
-        # actually an error, or might just be because the package isn't
-        # 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.
-        my $error = $g->sh("LANG=C '".join("' '", @$rpmcmd)."' 2>&1 ||:");
-
-        return () if ($error =~ /not installed/);
-
-        v2vdie __x('Error running {command}: {error}',
-                   command => join(' ', @$rpmcmd), error => $error);
-    }
-
-    my @installed = ();
-    foreach my $installed (@output) {
-        $installed =~ /^(\S+)\s+(\S+)\s+(\S+)$/
-            or die("Unexpected return from rpm command: $installed");
-        my ($epoch, $version, $release) = ($1, $2, $3);
-
-        # Ensure iepoch is always numeric
-        $epoch = 0 if('(none)' eq $epoch);
-
-        push(@installed, [$epoch, $version, $release]);
-    }
-
-    return sort { _evr_cmp($a->[0], $a->[1], $a->[2],
-                           $b->[0], $b->[1], $b->[2]) } @installed;
-}
-
-sub _parse_evr
-{
-    my ($evr) = @_;
-
-    $evr =~ /^(?:(\d+):)?([^-]+)(?:-(\S+))?$/ or die();
-
-    my $epoch = $1;
-    my $version = $2;
-    my $release = $3;
-
-    return ($epoch, $version, $release);
-}
-
-sub _evr_cmp
-{
-    my ($e1, $v1, $r1, $e2, $v2, $r2) = @_;
-
-    # Treat epoch as zero if undefined
-    $e1 ||= 0;
-    $e2 ||= 0;
-
-    return -1 if ($e1 < $e2);
-    return 1 if ($e1 > $e2);
-
-    # version must be defined
-    my $cmp = _rpmvercmp($v1, $v2);
-    return $cmp if ($cmp != 0);
-
-    # Treat release as the empty string if undefined
-    $r1 ||= "";
-    $r2 ||= "";
-
-    return _rpmvercmp($r1, $r2);
-}
-
-# An implementation of rpmvercmp. Compares two rpm version/release numbers and
-# returns -1/0/1 as appropriate.
-# Note that this is intended to have the exact same behaviour as the real
-# rpmvercmp, not be in any way sane.
-sub _rpmvercmp
-{
-    my ($a, $b) = @_;
-
-    # Simple equality test
-    return 0 if($a eq $b);
-
-    my @aparts;
-    my @bparts;
-
-    # [t]ransformation
-    # [s]tring
-    # [l]ist
-    foreach my $t ([$a => \@aparts],
-                   [$b => \@bparts]) {
-        my $s = $t->[0];
-        my $l = $t->[1];
-
-        # We split not only on non-alphanumeric characters, but also on the
-        # boundary of digits and letters. This corresponds to the behaviour of
-        # rpmvercmp because it does 2 types of iteration over a string. The
-        # first iteration skips non-alphanumeric characters. The second skips
-        # over either digits or letters only, according to the first character
-        # of $a.
-        @$l = split(/(?<=[[:digit:]])(?=[[:alpha:]]) | # digit<>alpha
-                     (?<=[[:alpha:]])(?=[[:digit:]]) | # alpha<>digit
-                     [^[:alnum:]]+                # sequence of non-alphanumeric
-                    /x, $s);
-    }
-
-    # Find the minimun of the number of parts of $a and $b
-    my $parts = scalar(@aparts) < scalar(@bparts) ?
-                scalar(@aparts) : scalar(@bparts);
-
-    for(my $i = 0; $i < $parts; $i++) {
-        my $acmp = $aparts[$i];
-        my $bcmp = $bparts[$i];
-
-        # Return 1 if $a is numeric and $b is not
-        if($acmp =~ /^[[:digit:]]/) {
-            return 1 if($bcmp !~ /^[[:digit:]]/);
-
-            # Drop leading zeroes
-            $acmp =~ /^0*(.*)$/;
-            $acmp = $1;
-            $bcmp =~ /^0*(.*)$/;
-            $bcmp = $1;
-
-            # We do a string comparison of 2 numbers later. At this stage, if
-            # they're of differing lengths, one is larger.
-            return 1 if(length($acmp) > length($bcmp));
-            return -1 if(length($bcmp) > length($acmp));
-        }
-
-        # Return -1 if $a is letters and $b is not
-        else {
-            return -1 if($bcmp !~ /^[[:alpha:]]/);
-        }
-
-        # Return only if they differ
-        return -1 if($acmp lt $bcmp);
-        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.
-
-    # Whichever has the greatest number of parts is the largest
-    return -1 if(scalar(@aparts) < scalar(@bparts));
-    return 1  if(scalar(@aparts) > scalar(@bparts));
-
-    # We can get here if the 2 strings differ only in non-alphanumeric
-    # separators.
-    return 0;
-}
-
-sub _remap_block_devices
-{
-    my ($meta, $virtio, $g, $root) = @_;
-
-    my @devices = map { $_->{device} } @{$meta->{disks}};
-    @devices = sort { scsi_first_cmp($a, $b) } @devices;
-
-    # @devices contains an ordered list of libvirt device names. Because
-    # libvirt uses a similar naming scheme to Linux, these will mostly be the
-    # same names as used by the guest. They are ordered as they were passed to
-    # 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.
-
-    # Modern distros use libata, and IDE devices are presented as sdX
-    my $libata = 1;
-
-    # RHEL 2, 3 and 4 didn't use libata
-    # RHEL 5 does use libata, but udev rules call IDE devices hdX anyway
-    if (_is_rhel_family($g, $root)) {
-        my $major_version = $g->inspect_get_major_version($root);
-        if ($major_version eq '2' ||
-            $major_version eq '3' ||
-            $major_version eq '4' ||
-            $major_version eq '5')
-        {
-            $libata = 0;
-        }
-    }
-    # Fedora has used libata since FC7, which is long out of support. We assume
-    # that all Fedora distributions in use use libata.
-
-    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,
-        # these will have been presented to the guest as sdc, sdd, sda and sdb
-        # respectively.
-        #
-        # N.B. I think the IDE/SCSI ordering situation may be somewhat more
-        # complicated than previously thought. This code originally put IDE
-        # drives first, but this hasn't worked for an OVA file. Given that
-        # this is a weird and somewhat unlikely situation I'm going with SCSI
-        # first until we have a more comprehensive solution.
-
-        my @newdevices;
-        my $suffix = 'a';
-        foreach my $device (@devices) {
-            $device = 'sd'.$suffix++ if ($device =~ /(?:h|s)d[a-z]+/);
-            push(@newdevices, $device);
-        }
-        @devices = @newdevices;
-    }
-
-    # We now assume that @devices contains an ordered list of device names, as
-    # used by the guest. Create a map of old guest device names to new guest
-    # device names.
-    my %map;
-
-    # Everything will be converted to either vdX, sdX or hdX
-    my $prefix;
-    if ($virtio) {
-        $prefix = 'vd';
-    } elsif ($libata) {
-        $prefix = 'sd';
-    } else {
-        $prefix = 'hd'
-    }
-
-    my $letter = 'a';
-    foreach my $device (@devices) {
-        my $mapped = $prefix.$letter;
-
-
-        # If a Xen guest has non-PV devices, Xen also simultaneously presents
-        # 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.
-        if ($device =~ /^(?:h|s)d([a-z]+)/) {
-            $map{'xvd'.$1} = $mapped;
-        }
-        $map{$device} = $mapped;
-        $letter++;
-    }
-
-    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 $device = $g->aug_get($spec);
-
-            # Match device names and partition numbers
-            my $name; my $part;
-            foreach my $r (qr{^/dev/(cciss/c\d+d\d+)(?:p(\d+))?$},
-                           qr{^/dev/([a-z]+)(\d*)?$}) {
-                if ($device =~ $r) {
-                    $name = $1;
-                    $part = $2;
-                    last;
-                }
-            }
-
-            # Ignore this entry if it isn't a device name
-            next unless defined($name);
-
-            # Ignore md and floppy devices, which don't need to be mapped
-            next if $name =~ /(md|fd)/;
-
-            # Ignore this entry if it refers to a device we don't know anything
-            # 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') {
-                    if ($spec =~ m{^/files$file}) {
-                        logmsg WARN, __x('{file} references unknown device '.
-                                         '{device}. This entry must be '.
-                                         'manually fixed after conversion.',
-                                         file => $file, device => $device);
-                        $warned = 1;
-                    }
-                }
-
-                # Shouldn't happen. Not fatal if it does, though.
-                if (!$warned) {
-                    logmsg WARN, 'Please report this warning as a bug. '.
-                                 "augeas path $spec refers to unknown device ".
-                                 "$device. This entry must be manually fixed ".
-                                 'after conversion.'
-                }
-
-                next;
-            }
-
-            my $mapped = '/dev/'.$map{$name};
-            $mapped .= $part if defined($part);
-            $g->aug_set($spec, $mapped);
-        }
-
-        $g->aug_save();
-    };
-
-    augeas_error($g, $@) if ($@);
-
-    # Delete cached (and now out of date) blkid info if it exists
-    foreach my $blkidtab ('/etc/blkid/blkid.tab', '/etc/blkid.tab') {
-        $g->rm($blkidtab) if ($g->exists($blkidtab));
-    }
-}
-
-sub _prepare_bootable
-{
-    my ($g, $root, $grub, $version, @modules) = @_;
-
-    my $path = "/boot/vmlinuz-$version";
-    $grub->write($path);
-    my $grub_initrd = $grub->get_initrd($path);
-
-    # Backup the original initrd
-    $g->mv($grub_initrd, "$grub_initrd.pre-v2v")
-    if $g->exists($grub_initrd);
-
-    if ($g->exists('/sbin/dracut')) {
-        $g->command(['/sbin/dracut', '--add-drivers', join(" ", @modules),
-                     $grub_initrd, $version]);
-    }
-
-    elsif ($g->exists('/sbin/mkinitrd')) {
-        # Create a new initrd which probes the required kernel modules
-        my @module_args = ();
-        foreach my $module (@modules) {
-            push(@module_args, "--with=$module");
-        }
-
-        # We explicitly modprobe ext2 here. This is required by mkinitrd on
-        # RHEL 3, and shouldn't hurt on other OSs. We don't care if this
-        # fails.
-        eval { $g->modprobe('ext2') };
-
-        # loop is a module in RHEL 5. Try to load it. Doesn't matter for
-        # other OSs if it doesn't exist, but RHEL 5 will complain:
-        #   All of your loopback devices are in use.
-        eval { $g->modprobe('loop') };
-
-        my @env;
-
-        # RHEL 4 mkinitrd determines if the root filesystem is on LVM by
-        # checking if the device name (after following symlinks) starts with
-        # /dev/mapper. However, on recent kernels/udevs, /dev/mapper/foo is
-        # just a symlink to /dev/dm-X. This means that RHEL 4 mkinitrd
-        # running in the appliance fails to detect root on LVM. We check
-        # ourselves if root is on LVM, and frig RHEL 4's mkinitrd if it is
-        # by setting root_lvm=1 in its environment. This overrides an
-        # internal variable in mkinitrd, and is therefore extremely nasty
-        # and applicable only to a particular version of mkinitrd.
-        if (_is_rhel_family($g, $root) &&
-            $g->inspect_get_major_version($root) eq '4')
-        {
-            push(@env, 'root_lvm=1') if ($g->is_lv($root));
-        }
-
-        $g->sh(join(' ', @env).' /sbin/mkinitrd '.join(' ', @module_args).
-               " $grub_initrd $version");
-    }
-
-    else {
-        v2vdie __('Didn\'t find mkinitrd or dracut. Unable to update initrd.');
-    }
-
-    # Disable kudzu in the guest
-    # Kudzu will detect the changed network hardware at boot time and either:
-    #   require manual intervention, or
-    #   disable the network interface
-    # Neither of these behaviours is desirable.
-    if ($g->exists('/etc/init.d/kudzu')) {
-        $g->command(['/sbin/chkconfig', 'kudzu', 'off']);
-    }
-}
-
-# Return 1 if the guest supports ACPI, 0 otherwise
-sub _supports_acpi
-{
-    my ($g, $root, $arch) = @_;
-
-    # 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') {
-        return 0;
-    }
-
-    return 1;
-}
-
-sub _supports_virtio
-{
-    my ($kernel, $g) = @_;
-
-    my %checklist = (
-        "virtio_net" => 0,
-        "virtio_blk" => 0
-    );
-
-    # Search the installed kernel's modules for the virtio drivers
-    foreach my $module ($g->find("/lib/modules/$kernel")) {
-        foreach my $driver (keys(%checklist)) {
-            if($module =~ m{/$driver\.(?:o|ko)$}) {
-                $checklist{$driver} = 1;
-            }
-        }
-    }
-
-    # Check we've got all the drivers in the checklist
-    foreach my $driver (keys(%checklist)) {
-        if(!$checklist{$driver}) {
-            return 0;
-        }
-    }
-
-    return 1;
-}
-
-=back
-
-=head1 COPYRIGHT
-
-Copyright (C) 2009-2012 Red Hat Inc.
-
-=head1 LICENSE
-
-Please see the file COPYING.LIB for the full license.
-
-=head1 SEE ALSO
-
-L<Sys::VirtConvert::Converter(3pm)>,
-L<Sys::VirtConvert(3pm)>,
-L<virt-v2v(1)>,
-L<http://libguestfs.org/>.
-
-=cut
-
-1;
-- 
1.8.1.4




More information about the Libguestfs mailing list