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