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
Mike Latimer
2013-Oct-12 01:05 UTC
Re: [Libguestfs] [PATCH] virt-v2v: Convert RedHat.pm to Linux.pm - for SUSE support
On Friday, October 11, 2013 04:57:32 PM Mike Latimer wrote:> 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? ;)Other than the concern about being a little too obsessed about the 79 character line length. ;) This version of the patch has all the unecessary cleanup removed, and only the necessary cleanup left. Should be easier to deal with. -Mike --- .../VirtConvert/Converter/{RedHat.pm => Linux.pm} | 679 +++++++++++++++++---- 1 file changed, 567 insertions(+), 112 deletions(-) rename lib/Sys/VirtConvert/Converter/{RedHat.pm => Linux.pm} (77%) diff --git a/lib/Sys/VirtConvert/Converter/RedHat.pm b/lib/Sys/VirtConvert/Converter/Linux.pm similarity index 77% rename from lib/Sys/VirtConvert/Converter/RedHat.pm rename to lib/Sys/VirtConvert/Converter/Linux.pm index 612ab2e..808d90f 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 and 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 { @@ -236,7 +306,7 @@ sub update_console sub check { my $self = shift; - my ($path) = @_; + my ($path, $root) = @_; my $g = $self->{g}; my $grub_conf = $self->{grub_conf}; @@ -251,15 +321,17 @@ sub check 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 +437,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 +479,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 +501,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 +519,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 +541,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 +567,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'); @@ -509,7 +590,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 +599,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 +609,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 @@ -544,9 +625,17 @@ sub _is_rhel_family ($g->inspect_get_distro($root) =~ /^(rhel|centos|scientificlinux| redhat-based)$/); } -=item Sys::VirtConvert::Converter::RedHat->can_handle(g, root) +sub _is_suse_family +{ + my ($g, $root) = @_; + + return ($g->inspect_get_type($root) eq 'linux') && + ($g->inspect_get_distro($root) =~ /^(sles|suse-based|opensuse)$/); +} -Return 1 if Sys::VirtConvert::Converter::RedHat can convert the given guest +=item Sys::VirtConvert::Converter::Linux->can_handle(g, root) + +Return 1 if Sys::VirtConvert::Converter::Linux can convert the given guest =cut @@ -556,13 +645,16 @@ 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 +693,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); @@ -625,8 +719,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); @@ -842,7 +937,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 +961,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; } @@ -885,13 +980,14 @@ sub _configure_display_driver 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. + # 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); } } @@ -925,20 +1021,45 @@ 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; + # 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 - else { + if (!defined($version)) { if($path =~ m{/boot/vmlinuz-(.*)}) { $version = $1; @@ -988,6 +1109,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,7 +1120,16 @@ 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; } @@ -1013,7 +1144,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 +1186,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 +1215,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; } @@ -1121,8 +1255,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 +1302,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 +1311,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 +1364,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 @@ -1506,13 +1661,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 +1678,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; } @@ -1631,14 +1786,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 +1811,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 +1823,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; } } @@ -1780,6 +1946,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,6 +2056,7 @@ sub _install_config } } + my @user_paths = _get_deppaths($g, $root, $config, \@missing, $g->inspect_get_arch($root), @$user); @@ -1814,11 +2065,33 @@ sub _install_config '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; @@ -1963,7 +2236,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; } @@ -1975,9 +2253,8 @@ sub _discover_kernel # 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 +2266,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'; + } - elsif ($meta->{cpus} > 1) { - return 'kernel-smp'; + # SMP kernel for guests with >1 CPU + 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' @@ -2076,7 +2411,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]); @@ -2203,7 +2538,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; @@ -2235,6 +2570,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, @@ -2290,13 +2631,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 +2692,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 +2717,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 +2760,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 = (); @@ -2467,6 +2861,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
Mike Latimer
2013-Oct-16 17:58 UTC
Re: [Libguestfs] [PATCH] virt-v2v: Convert RedHat.pm to Linux.pm - for SUSE support
On Friday, October 11, 2013 07:05:25 PM Mike Latimer wrote:> + # 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");The above two lines should actually be: + push (@checklist, "/files$grub->{grub_conf}/*/kernel/root"); + push (@checklist, "/files$grub->{grub_conf}/*/kernel/resume"); Let me know any thoughts you might have on this patch in general. I'll post the full patchset (including changes to virt-v2v.db), once we work out any additional issues. Thanks, Mike
Apparently Analagous Threads
- 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 3/4] Add SUSE converter
- Re: [PATCH 3/4] Add SUSE converter
- [PATCH] virt-v2v: Add use augeas_error to GrubLegacy, plus typo fixup