Conversion would fail if it was necessary to install a package, and multiple
architectures of that package were already installed. This was happening
specifically with device-mapper on RHEL 5 conversions.
Unfortunately the flat dependency list in the config file didn't really allow
this to be fixed. The best that could be done is to specify both i386 and x86_64
dependencies, but would mean attempted installation of the i386 version
regardless of whether it was already installed. This would fail if the guest
didn't have i386 dependencies installed, for example glibc.
By making the dependency tree more explicit, we can not only more easily test
for both i386 and x86_64 packages, but also more effectively prune what needs to
be installed.
This fixes RHBZ#583008
---
lib/Sys/VirtV2V/Config.pm | 3 +-
lib/Sys/VirtV2V/GuestOS/RedHat.pm | 134 +++++++++++++++++++++++++++++--------
v2v/virt-v2v.conf | 48 ++++++++++---
3 files changed, 144 insertions(+), 41 deletions(-)
diff --git a/lib/Sys/VirtV2V/Config.pm b/lib/Sys/VirtV2V/Config.pm
index d90c869..117b55b 100644
--- a/lib/Sys/VirtV2V/Config.pm
+++ b/lib/Sys/VirtV2V/Config.pm
@@ -107,8 +107,7 @@ sub get_transfer_iso
# config file
# We use a hash here to avoid duplicates
my %path_args;
- foreach my $path ($dom->findnodes('/virt-v2v/app/path/text() | '.
- '/virt-v2v/app/dep/text()')) {
+ foreach my $path ($dom->findnodes('/virt-v2v/app/path/text()')) {
$path = $path->getData();
# Get the absolute path if iso-root was defined
diff --git a/lib/Sys/VirtV2V/GuestOS/RedHat.pm b/lib/Sys/VirtV2V/GuestOS/RedHat.pm
index b0ef775..a973c19 100644
--- a/lib/Sys/VirtV2V/GuestOS/RedHat.pm
+++ b/lib/Sys/VirtV2V/GuestOS/RedHat.pm
@@ -462,7 +462,7 @@ sub add_kernel
}
}
- my ($app, $deps);
+ my ($app, $depnames);
eval {
my $desc = $self->{desc};
@@ -475,7 +475,8 @@ sub add_kernel
search => $search)));
}
- ($app, $deps) = $config->match_app($desc, $kernel_pkg, $kernel_arch);
+ ($app, $depnames) =
+ $config->match_app($desc, $kernel_pkg, $kernel_arch);
};
# Return undef if we didn't find a kernel
if ($@) {
@@ -483,14 +484,12 @@ sub add_kernel
return undef;
}
- return undef if($self->_is_installed($app));
+ return undef if ($self->_newer_installed($app));
- my @install;
- # Install any kernel dependencies which aren't already installed
- foreach my $dep (@$deps) {
- push(@install, $dep) unless($self->_is_installed($dep));
- }
- $self->_install_rpms(1, @install);
+ my $user_arch = $kernel_arch eq 'i686' ? 'i386' : $kernel_arch;
+
+ # Install any required kernel dependencies
+ $self->_install_rpms(1, $self->_get_deppaths($user_arch, @$depnames));
# Inspect the rpm to work out what kernel version it contains
my $version;
@@ -621,22 +620,17 @@ sub add_application
my ($app, $deps) = $config->match_app($self->{desc}, $label, $user_arch);
# Nothing to do if it's already installed
- return if($self->_is_installed($app));
+ return if ($self->_newer_installed($app));
my @install = ($app);
# Add any dependencies which aren't already installed to the install set
- foreach my $dep (@$deps) {
- push(@install, $dep) unless ($self->_is_installed($dep));
- }
+ push(@install, $self->_get_deppaths($user_arch, @$deps));
$self->_install_rpms(1, @install);
}
-
-# Return 1 if the requested rpm, or a newer version, is installed
-# Return 0 otherwise
-sub _is_installed
+sub _get_nevra
{
my $self = shift;
my ($rpm) = @_;
@@ -657,8 +651,15 @@ sub _is_installed
# Ensure epoch is always numeric
$epoch = 0 if('(none)' eq $epoch);
- # Search installed rpms matching <name>.<arch>
- my $found = 0;
+ return ($name, $epoch, $version, $release, $arch);
+}
+
+sub _get_installed
+{
+ my $self = shift;
+ my ($name, $arch) = @_;
+
+ my $g = $self->{g};
my $rpmcmd = ['rpm', '-q', '--qf', '%{EPOCH} %{VERSION}
%{RELEASE}\n',
"$name.$arch"];
@@ -678,32 +679,60 @@ sub _is_installed
# a real error.
my $error = $g->sh("LANG=C '".join("' '",
@$rpmcmd)."' 2>&1 ||:");
- return 0 if ($error =~ /not installed/);
+ return () if ($error =~ /not installed/);
die(user_message(__x("Error running {command}: {error}",
command => join(' ', @$rpmcmd),
error => $error)));
}
+ my @installed;
foreach my $installed (@output) {
$installed =~ /^(\S+)\s+(\S+)\s+(\S+)$/
or die("Unexpected return from rpm command: $installed");
- my ($iepoch, $iversion, $irelease) = ($1, $2, $3);
+ my ($epoch, $version, $release) = ($1, $2, $3);
# Ensure iepoch is always numeric
- $iepoch = 0 if('(none)' eq $iepoch);
+ $epoch = 0 if('(none)' eq $epoch);
+
+ push(@installed, [$epoch, $version, $release]);
+ }
+
+ return @installed;
+}
+
+
+# Return 1 if the requested rpm, or a newer version, is installed
+# Return 0 otherwise
+sub _newer_installed
+{
+ my $self = shift;
+ my ($rpm) = @_;
+
+ my $g = $self->{g};
+
+ my ($name, $epoch, $version, $release, $arch) = $self->_get_nevra($rpm);
+
+ my @installed = $self->_get_installed($name, $arch);
+
+ # Search installed rpms matching <name>.<arch>
+ my $found = 0;
+ foreach my $pkg (@installed) {
+ my $iepoch = $pkg->[0];
+ my $iversion = $pkg->[1];
+ my $irelease = $pkg->[2];
# Skip if installed epoch less than requested version
- next if($iepoch < $epoch);
+ next if ($iepoch < $epoch);
- if($iepoch eq $epoch) {
+ if ($iepoch eq $epoch) {
# Skip if installed version less than requested version
- next if(_rpmvercmp($iversion, $version) < 0);
+ next if (_rpmvercmp($iversion, $version) < 0);
# Skip if install version == requested version, but release less
# than requested release
- if($iversion eq $version) {
- next if(_rpmvercmp($irelease,$release) < 0);
+ if ($iversion eq $version) {
+ next if (_rpmvercmp($irelease, $release) < 0);
}
}
@@ -713,6 +742,57 @@ sub _is_installed
return $found;
}
+# Return a list of dependency paths which need to be installed for the given
+# apps
+sub _get_deppaths
+{
+ my $self = shift;
+ my ($arch, @apps) = @_;
+
+ my $desc = $self->{desc};
+ my $config = $self->{config};
+
+ my %required;
+ foreach my $app (@apps) {
+ my ($path, $deps) = $config->match_app($desc, $app, $arch);
+
+ if (!$self->_newer_installed($path)) {
+ $required{$path} = 1;
+
+ foreach my $deppath ($self->_get_deppaths($arch, @$deps)) {
+ $required{$deppath} = 1;
+ }
+ }
+
+ # For x86_64, also check if there is any i386 version installed. If
+ # there is, check if it needs to be upgraded.
+ if ($arch eq 'x86_64') {
+ $path = undef;
+ $deps = undef;
+
+ # It's not an error if no i386 package is available
+ eval {
+ ($path, $deps) = $config->match_app($desc, $app, 'i386');
+ };
+
+ if (defined($path) && !$self->_newer_installed($path)) {
+ my ($name, undef, undef, undef, $arch) =
+ $self->_get_nevra($path);
+
+ if ($self->_get_installed($name, $arch) > 0) {
+ $required{$path} = 1;
+
+ foreach my $deppath ($self->_get_deppaths('i386', @$deps))
{
+ $required{$deppath} = 1;
+ }
+ }
+ }
+ }
+ }
+
+ return keys(%required);
+}
+
# An implementation of rpmvercmp. Compares two rpm version/release numbers and
# returns -1/0/1 as appropriate.
# Note that this is intended to have the exact same behaviour as the real
diff --git a/v2v/virt-v2v.conf b/v2v/virt-v2v.conf
index 6b76f79..0ea3c9f 100644
--- a/v2v/virt-v2v.conf
+++ b/v2v/virt-v2v.conf
@@ -7,24 +7,48 @@
5 to support VirtIO -->
<app os='rhel' major='5' arch='i686'
name='kernel'>
<path>rhel/5/kernel-2.6.18-128.el5.i686.rpm</path>
- <dep>rhel/5/ecryptfs-utils-56-8.el5.i386.rpm</dep>
- <dep>rhel/5/lvm2-2.02.40-6.el5.i386.rpm</dep>
- <dep>rhel/5/device-mapper-1.02.28-2.el5.i386.rpm</dep>
- <dep>rhel/5/device-mapper-event-1.02.28-2.el5.i386.rpm</dep>
+ <dep>ecryptfs-utils</dep>
+ <dep>lvm2</dep>
</app>
<app os='rhel' major='5' arch='i686'
name='kernel-PAE'>
<path>rhel/5/kernel-PAE-2.6.18-128.el5.i686.rpm</path>
- <dep>rhel/5/ecryptfs-utils-56-8.el5.i386.rpm</dep>
- <dep>rhel/5/lvm2-2.02.40-6.el5.i386.rpm</dep>
- <dep>rhel/5/device-mapper-1.02.28-2.el5.i386.rpm</dep>
- <dep>rhel/5/device-mapper-event-1.02.28-2.el5.i386.rpm</dep>
+ <dep>ecryptfs-utils</dep>
+ <dep>lvm2</dep>
</app>
<app os='rhel' major='5' arch='x86_64'
name='kernel'>
<path>rhel/5/kernel-2.6.18-128.el5.x86_64.rpm</path>
- <dep>rhel/5/ecryptfs-utils-56-8.el5.x86_64.rpm</dep>
- <dep>rhel/5/lvm2-2.02.40-6.el5.x86_64.rpm</dep>
- <dep>rhel/5/device-mapper-1.02.28-2.el5.x86_64.rpm</dep>
- <dep>rhel/5/device-mapper-event-1.02.28-2.el5.x86_64.rpm</dep>
+ <dep>ecryptfs-utils</dep>
+ <dep>lvm2</dep>
+ </app>
+
+ <!-- RHEL 5 Kernel dependencies -->
+ <app os='rhel' major='5' arch='x86_64'
name='ecryptfs-utils'>
+ <path>rhel/5/ecryptfs-utils-56-8.el5.x86_64.rpm</path>
+ </app>
+ <app os='rhel' major='5' arch='i386'
name='ecryptfs-utils'>
+ <path>rhel/5/ecryptfs-utils-56-8.el5.i386.rpm</path>
+ </app>
+ <app os='rhel' major='5' arch='x86_64'
name='lvm2'>
+ <path>rhel/5/lvm2-2.02.40-6.el5.x86_64.rpm</path>
+ <dep>device-mapper</dep>
+ <dep>device-mapper-event</dep>
+ </app>
+ <app os='rhel' major='5' arch='i386'
name='lvm2'>
+ <path>rhel/5/lvm2-2.02.40-6.el5.i386.rpm</path>
+ <dep>device-mapper</dep>
+ <dep>device-mapper-event</dep>
+ </app>
+ <app os='rhel' major='5' arch='x86_64'
name='device-mapper'>
+ <path>rhel/5/device-mapper-1.02.28-2.el5.x86_64.rpm</path>
+ </app>
+ <app os='rhel' major='5' arch='i386'
name='device-mapper'>
+ <path>rhel/5/device-mapper-1.02.28-2.el5.i386.rpm</path>
+ </app>
+ <app os='rhel' major='5' arch='x86_64'
name='device-mapper-event'>
+ <path>rhel/5/device-mapper-event-1.02.28-2.el5.x86_64.rpm</path>
+ </app>
+ <app os='rhel' major='5' arch='i386'
name='device-mapper-event'>
+ <path>rhel/5/device-mapper-event-1.02.28-2.el5.i386.rpm</path>
</app>
<!-- RHEL 4
--
1.6.6.1