[PATCH] Change the date format used in RHEV OVF output
by Matthew Booth
Date formats were previously undefined in RHEV's OVF output. They are now
defined as: 'yyyy/mm/dd hh:mm:ss'.
This change updates dates output by virt-v2v to use the new format.
---
lib/Sys/VirtV2V/Target/RHEV.pm | 19 ++++++++++---------
1 files changed, 10 insertions(+), 9 deletions(-)
diff --git a/lib/Sys/VirtV2V/Target/RHEV.pm b/lib/Sys/VirtV2V/Target/RHEV.pm
index 911e5c6..f14c54f 100644
--- a/lib/Sys/VirtV2V/Target/RHEV.pm
+++ b/lib/Sys/VirtV2V/Target/RHEV.pm
@@ -543,10 +543,7 @@ sub create_guest
$memsize = int($memsize / 1024);
# Generate a creation date
- my $now = gmtime();
- my $vmcreation = sprintf("%02d/%02d/%d %02d:%02d:%02d",
- $now->mday(), $now->mon() + 1, $now->year() + 1900,
- $now->hour(), $now->min(), $now->sec());
+ my $vmcreation = _format_time(gmtime());
my $osuuid = Sys::VirtV2V::Target::RHEV::UUIDHelper::get_uuid();
@@ -651,6 +648,14 @@ EOF
$nfs->check_exit();
}
+sub _format_time
+{
+ my ($time) = @_;
+ return sprintf("%04d/%02d/%02d %02d:%02d:%02d",
+ $time->year() + 1900, $time->mon() + 1, $time->mday(),
+ $time->hour(), $time->min(), $time->sec());
+}
+
sub _disks
{
my $self = shift;
@@ -756,11 +761,7 @@ sub _disks
$e->addText('00000000-0000-0000-0000-000000000000');
$item->appendChild($e);
- my $volcreation = gmtime($vol->_get_creation());
- my $voldate = sprintf("%02d/%02d/%d %02d:%02d:%02d",
- $volcreation->mday(), $volcreation->mon() + 1,
- $volcreation->year() + 1900, $volcreation->hour(),
- $volcreation->min(), $volcreation->sec());
+ my $voldate = _format_time(gmtime($vol->_get_creation()));
$e = $ovf->createElement('rasd:CreationDate');
$e->addText($voldate);
--
1.6.6.1
14 years, 7 months
[PATCH] Workaround issue where grubby fails when run under libguestfs
by Matthew Booth
When installing kernel-2.6.18-128.el5 in a guest with
kernel-xen-2.6.18-164.el5xen already installed, and 'root=LABEL=/' on the kernel
command line, grubby fails to install a grub entry for the new kernel. It only
fails when run under libguestfs. Installing in the original Xen guest works
fine. The underlying cause appears to be related to blkid, although I haven't
managed to pin it down exactly.
This change works around the issue. If virt-v2v encounters a problem with grub,
it will install a new kernel entry itself using augeas.
Note that this change introduces a dependency on the new aug_clear libguestfs
command.
---
lib/Sys/VirtV2V/GuestOS/RedHat.pm | 119 ++++++++++++++++++++++++++++++++++---
1 files changed, 110 insertions(+), 9 deletions(-)
diff --git a/lib/Sys/VirtV2V/GuestOS/RedHat.pm b/lib/Sys/VirtV2V/GuestOS/RedHat.pm
index 0e469f5..036f4eb 100644
--- a/lib/Sys/VirtV2V/GuestOS/RedHat.pm
+++ b/lib/Sys/VirtV2V/GuestOS/RedHat.pm
@@ -1043,10 +1043,17 @@ sub prepare_bootable
my $initrd;
my $found = 0;
eval {
+ my $prefix;
+ if ($self->{desc}->{boot}->{grub_fs} eq "/boot") {
+ $prefix = '';
+ } else {
+ $prefix = '/boot';
+ }
+
foreach my $kernel
($g->aug_match('/files/boot/grub/menu.lst/title/kernel')) {
- if($g->aug_get($kernel) eq "/vmlinuz-$version") {
+ if($g->aug_get($kernel) eq "$prefix/vmlinuz-$version") {
# Ensure it's the default
$kernel =~ m{/files/boot/grub/menu.lst/title(?:\[(\d+)\])?/kernel}
or die($kernel);
@@ -1068,24 +1075,118 @@ sub prepare_bootable
last;
}
}
- $g->aug_save();
+
+ # grubby can sometimes fail to correctly update grub.conf when run from
+ # libguestfs. If it looks like this happened, install a new grub config
+ # here.
+ if (!$found) {
+ # Check that an appropriately named kernel and initrd exist
+ if ($g->exists("/boot/vmlinuz-$version") &&
+ $g->exists("/boot/initrd-$version.img"))
+ {
+ $initrd = "$prefix/initrd-$version.img";
+
+ my $title;
+ # No point in dying if /etc/redhat-release can't be read
+ eval {
+ ($title) = $g->read_lines('/etc/redhat-release');
+ };
+ $title ||= 'Linux';
+
+ # This is how new-kernel-pkg does it
+ $title =~ s/ release.*//;
+ $title .= " ($version)";
+
+ my $default;
+ eval {
+ $default = $g->aug_get('/files/boot/grub/menu.lst/default');
+ };
+
+ if (defined($default)) {
+ $g->aug_defvar('template',
+ '/files/boot/grub/menu.lst/title['.($default + 1).']');
+ }
+
+ # If there's no default, take the first entry with a kernel
+ else {
+ my ($match) =
+ $g->aug_match('/files/boot/grub/menu.lst/title/kernel');
+
+ die("No template kernel found in grub")
+ unless(defined($match));
+
+ $match =~ s/\/kernel$//;
+ $g->aug_defvar('template', $match);
+ }
+
+ # Add a new title node at the end
+ $g->aug_defnode('new',
+ '/files/boot/grub/menu.lst/title[last()+1]',
+ $title);
+
+ # N.B. Don't change the order of root, kernel and initrd below,
+ # or the guest will not boot.
+
+ # Copy root from the template
+ $g->aug_set('$new/root', $g->aug_get('$template/root'));
+
+ # Set kernel and initrd to the new values
+ $g->aug_set('$new/kernel', "$prefix/vmlinuz-$version");
+ $g->aug_set('$new/initrd', "$prefix/initrd-$version.img");
+
+ # Copy all kernel command-line arguments
+ foreach my $arg ($g->aug_match('$template/kernel/*')) {
+ # kernel arguments don't necessarily have values
+ my $val;
+ eval {
+ $val = $g->aug_get($arg);
+ };
+
+ $arg =~ /([^\/]*)$/;
+ $arg = $1;
+
+ if (defined($val)) {
+ $g->aug_set('$new/kernel/'.$arg, $val);
+ } else {
+ $g->aug_clear('$new/kernel/'.$arg);
+ }
+ }
+
+ my ($new) = $g->aug_match('$new');
+ $new =~ /\[(\d+)\]$/;
+
+ $g->aug_set('/files/boot/grub/menu.lst/default',
+ defined($1) ? $1 - 1 : 0);
+ }
+
+ else {
+ die("Didn't find a grub entry for kernel version $version");
+ }
+ }
+
+ eval {
+ $g->aug_save();
+ };
+
+ if ($@) {
+ my $msg = '';
+ foreach my $error ($g->aug_match('/augeas//error')) {
+ $msg .= $error.': '.$g->aug_get($error)."\n";
+ }
+ die($msg);
+ }
};
# Propagate augeas failure
die($@) if($@);
- if(!$found) {
- die(user_message(__x("Didn't find a grub entry for kernel version ".
- "{version}", version => $version)));
- }
-
if(!defined($initrd)) {
print STDERR user_message(__x("WARNING: Kernel version {version} ".
"doesn't have an initrd entry in grub",
version => $version));
} else {
- # Initrd as returned by grub is relative to /boot
- $initrd = "/boot$initrd";
+ # Initrd as returned by grub may be relative to /boot
+ $initrd = $self->{desc}->{boot}->{grub_fs}.$initrd;
# Backup the original initrd
$g->mv("$initrd", "$initrd.pre-v2v");
--
1.6.6.1
14 years, 7 months
[PATCH] Fix build error in fuse/guestmount.c
by Matthew Booth
guestmount.c: In function 'main':
guestmount.c:899: error: implicit declaration of function 'setlocale'
[-Wimplicit-function-declaration]
guestmount.c wasn't including locale.h.
---
fuse/guestmount.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/fuse/guestmount.c b/fuse/guestmount.c
index b4a917f..27a4cef 100644
--- a/fuse/guestmount.c
+++ b/fuse/guestmount.c
@@ -40,6 +40,7 @@
#include <assert.h>
#include <sys/time.h>
#include <sys/types.h>
+#include <locale.h>
#include <fuse.h>
#include <guestfs.h>
--
1.6.6.1
14 years, 7 months
[PATCH] Converter: Update xvc0 console to ttyS0
by Matthew Booth
This change updates a guest which uses Xen's xvc0 to use ttyS0 instead. In a
related change, it also makes a console a 'standard device' will always be
configured.
---
lib/Sys/VirtV2V/Converter.pm | 5 +++-
lib/Sys/VirtV2V/Converter/Linux.pm | 46 ++++++++++++++++++++++++++++++++++++
2 files changed, 50 insertions(+), 1 deletions(-)
diff --git a/lib/Sys/VirtV2V/Converter.pm b/lib/Sys/VirtV2V/Converter.pm
index 8d46502..45038ee 100644
--- a/lib/Sys/VirtV2V/Converter.pm
+++ b/lib/Sys/VirtV2V/Converter.pm
@@ -75,6 +75,7 @@ use constant KVM_XML_VIRTIO => "
<video>
<model type='cirrus' vram='9216' heads='1'/>
</video>
+ <console type='pty'/>
</devices>
</domain>
";
@@ -98,6 +99,7 @@ use constant KVM_XML_NOVIRTIO => "
<video>
<model type='cirrus' vram='9216' heads='1'/>
</video>
+ <console type='pty'/>
</devices>
</domain>
";
@@ -240,7 +242,8 @@ sub _configure_default_devices
my ($input_devices) = $default_dom->findnodes('/domain/devices');
# Add new default devices from default XML
- foreach my $input ($input_devices->findnodes('input | video | graphics')) {
+ foreach my $input ($input_devices->findnodes('input | video | '.
+ 'graphics | console')) {
my $new = $input->cloneNode(1);
$new->setOwnerDocument($devices->getOwnerDocument());
$devices->appendChild($new);
diff --git a/lib/Sys/VirtV2V/Converter/Linux.pm b/lib/Sys/VirtV2V/Converter/Linux.pm
index 96e37a4..df7c969 100644
--- a/lib/Sys/VirtV2V/Converter/Linux.pm
+++ b/lib/Sys/VirtV2V/Converter/Linux.pm
@@ -112,6 +112,7 @@ sub convert
my $virtio = $guestos->supports_virtio($kernel);
# Configure the rest of the system
+ _configure_console($g);
_configure_display_driver($guestos, $virtio);
$guestos->remap_block_devices($devices, $virtio);
_configure_kernel_modules($guestos, $desc, $virtio);
@@ -193,6 +194,51 @@ sub _configure_kernel_modules
}
}
+# We configure a console on ttyS0. Make sure existing console references use it.
+sub _configure_console
+{
+ my ($g) = @_;
+
+ # Look for gettys which use xvc0
+ foreach my $augpath ($g->aug_match("/files/etc/inittab/*/process")) {
+ my $proc = $g->aug_get($augpath);
+
+ # If the process mentions xvc0, change it to ttyS0
+ if ($proc =~ /\bxvc0\b/) {
+ $proc =~ s/\bxvc0\b/ttyS0/g;
+ $g->aug_set($augpath, $proc);
+ }
+ }
+
+ # Replace any mention of xvc0 in /etc/securetty with ttyS0
+ my $size = 0;
+ my @lines = ();
+
+ foreach my $line ($g->read_lines('/etc/securetty')) {
+ if($line eq "xvc0") {
+ $line = "ttyS0";
+ }
+
+ $size += length($line) + 1;
+ push(@lines, $line);
+ }
+
+ $g->write_file('/etc/securetty', join("\n", @lines)."\n", $size);
+
+ # Update any kernel console lines
+ foreach my $augpath
+ ($g->aug_match("/files/boot/grub/menu.lst/title/kernel/console"))
+ {
+ my $console = $g->aug_get($augpath);
+ if ($console =~ /\bxvc0\b/) {
+ $console =~ s/\bxvc0\b/ttyS0/g;
+ $g->aug_set($augpath, $console);
+ }
+ }
+
+ $g->aug_save();
+}
+
sub _configure_display_driver
{
my ($guestos, $virtio) = @_;
--
1.6.6.1
14 years, 7 months
[PATCH] Add SSH transfer method
by Matthew Booth
Support obtaining guest storage over ssh. The following now works:
virt-v2v -f v2v/virt-v2v.conf -ic 'xen+ssh://xen.example.com/system' \
-op transfer rhel54pv64
---
MANIFEST | 1 +
lib/Sys/VirtV2V/Connection.pm | 1 +
lib/Sys/VirtV2V/Connection/LibVirt.pm | 4 +
lib/Sys/VirtV2V/Transfer/SSH.pm | 208 +++++++++++++++++++++++++++++++++
v2v/virt-v2v.pl | 15 ++-
5 files changed, 222 insertions(+), 7 deletions(-)
create mode 100644 lib/Sys/VirtV2V/Transfer/SSH.pm
diff --git a/MANIFEST b/MANIFEST
index 65b5a8e..5fd38c6 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -17,6 +17,7 @@ lib/Sys/VirtV2V/Target/LibVirt.pm
lib/Sys/VirtV2V/Target/RHEV.pm
lib/Sys/VirtV2V/Transfer/ESX.pm
lib/Sys/VirtV2V/Transfer/LocalCopy.pm
+lib/Sys/VirtV2V/Transfer/SSH.pm
MANIFEST This list of files
MANIFEST.SKIP
META.yml
diff --git a/lib/Sys/VirtV2V/Connection.pm b/lib/Sys/VirtV2V/Connection.pm
index c901b90..da6a44b 100644
--- a/lib/Sys/VirtV2V/Connection.pm
+++ b/lib/Sys/VirtV2V/Connection.pm
@@ -24,6 +24,7 @@ use Sys::Virt;
use Sys::VirtV2V::Transfer::ESX;
use Sys::VirtV2V::Transfer::LocalCopy;
+use Sys::VirtV2V::Transfer::SSH;
use Sys::VirtV2V::UserMessage qw(user_message);
use Locale::TextDomain 'virt-v2v';
diff --git a/lib/Sys/VirtV2V/Connection/LibVirt.pm b/lib/Sys/VirtV2V/Connection/LibVirt.pm
index 0a0330e..5e06356 100644
--- a/lib/Sys/VirtV2V/Connection/LibVirt.pm
+++ b/lib/Sys/VirtV2V/Connection/LibVirt.pm
@@ -150,6 +150,10 @@ sub new
$transfer = "Sys::VirtV2V::Transfer::ESX";
}
+ elsif ($self->{uri}->scheme =~ /\+ssh$/) {
+ $transfer = "Sys::VirtV2V::Transfer::SSH";
+ }
+
# Default to LocalCopy
# XXX: Need transfer methods for remote libvirt connections, e.g. scp
else {
diff --git a/lib/Sys/VirtV2V/Transfer/SSH.pm b/lib/Sys/VirtV2V/Transfer/SSH.pm
new file mode 100644
index 0000000..3b08716
--- /dev/null
+++ b/lib/Sys/VirtV2V/Transfer/SSH.pm
@@ -0,0 +1,208 @@
+# Sys::VirtV2V::Transfer::SSH
+# Copyright (C) 2010 Red Hat Inc.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+package Sys::VirtV2V::Transfer::SSH;
+
+use POSIX;
+use File::Spec;
+use File::stat;
+
+use Sys::VirtV2V::UserMessage qw(user_message);
+
+use Locale::TextDomain 'virt-v2v';
+
+=pod
+
+=head1 NAME
+
+Sys::VirtV2V::Transfer::SSH - Copy a remote guest's storage via ssh
+
+=head1 SYNOPSIS
+
+ use Sys::VirtV2V::Transfer::SSH;
+
+ $vol = Sys::VirtV2V::Transfer::SSH->transfer($conn, $path, $target);
+
+=head1 DESCRIPTION
+
+Sys::VirtV2V::Transfer::SSH retrieves guest storage devices from a remote server
+via SSH.
+
+=head1 METHODS
+
+=over
+
+=item transfer(conn, path, target)
+
+Transfer <path> from the remove server. Storage will be created using <target>.
+
+=cut
+
+sub transfer
+{
+ my $class = shift;
+
+ my ($conn, $path, $target) = @_;
+
+ my (undef, undef, $name) = File::Spec->splitpath($path);
+
+ if ($target->volume_exists($name)) {
+ print STDERR user_message(__x("WARNING: storage volume {name} ".
+ "already exists on the target. NOT ".
+ "copying it again. Delete the volume ".
+ "and retry to copy again.",
+ name => $name));
+ return $target->get_volume($name);
+ }
+
+ my $uri = $conn->{uri};
+ my $username = $conn->{username};
+ my $password = $conn->{password};
+ my $host = $conn->{hostname};
+
+ die("URI not defined for connection") unless (defined($uri));
+
+ my ($pid, $size, $fh, $error) =
+ _connect($host, $username, $path);
+
+ my $vol = $target->create_volume($name, $size);
+ $vol->open();
+
+ for (;;) {
+ my $buffer;
+ # Transfer in 8k chunks
+ my $in = sysread($fh, $buffer, 8 * 1024);
+ die(user_message(__x("Error reading data from {path}: {error}",
+ path => $path,
+ error => $!))) if (!defined($in));
+
+ last if ($in == 0);
+
+ $vol->write($buffer);
+ }
+
+ $vol->close();
+
+ waitpid($pid, 0) == $pid or die("error reaping child: $!");
+ # If the child returned an error, check for anything on its stderr
+ if ($? != 0) {
+ my $msg = "";
+ while (<$error>) {
+ $msg .= $_;
+ }
+ die(user_message(__x("Unexpected error copying {path} from {host}. ".
+ "Command output: {output}",
+ path => $path,
+ host => $uri->host,
+ output => $msg)));
+ }
+
+ return $vol;
+}
+
+sub _connect
+{
+ my ($host, $username, $path) = @_;
+
+ my ($stdin_read, $stdin_write);
+ my ($stdout_read, $stdout_write);
+ my ($stderr_read, $stderr_write);
+
+ pipe($stdin_read, $stdin_write);
+ pipe($stdout_read, $stdout_write);
+ pipe($stderr_read, $stderr_write);
+
+ my $pid = fork();
+ if ($pid == 0) {
+ my @command;
+ push(@command, 'ssh');
+ push(@command, '-l', $username) if (defined($username));
+ push(@command, $host);
+ push(@command, "stat -c %s $path; cat $path");
+
+ # Close the ends of the pipes we don't need
+ close($stdin_write);
+ close($stdout_read);
+ close($stderr_read);
+
+ # dup2() stdin, stdout and stderr to pipes
+ open(STDIN, "<&".fileno($stdin_read))
+ or die("dup stdin failed: $!");
+ open(STDOUT, "<&".fileno($stdout_write))
+ or die("dup stdout failed: $!");
+ open(STDERR, "<&".fileno($stderr_write))
+ or die("dup stderr failed: $!");
+
+ # We parse stderr, so make sure it's in English
+ $ENV{LANG} = 'C';
+ exec(@command);
+ }
+
+ close($stdin_read);
+ close($stdout_write);
+ close($stderr_write);
+
+ # Check that we don't get output on stderr before we read the file size
+ for(;;) {
+ my ($rin, $rout);
+ $rin = '';
+ vec($rin, fileno($stdout_read), 1) = 1;
+ vec($rin, fileno($stderr_read), 1) = 1;
+
+ my $nfound = select($rout=$rin, undef, undef, undef);
+ die("select failed: $!") if ($nfound < 0);
+
+ if (vec($rout, fileno($stderr_read), 1) == 1) {
+ my $stderr = '';
+ while(<$stderr_read>) {
+ $stderr .= $_;
+ }
+
+ die(user_message(__x("Unexpected error getting {path}: ".
+ "{output}",
+ path => $path, output => $stderr)));
+ }
+
+ if (vec($rout, fileno($stdout_read), 1) == 1) {
+ last;
+ }
+ }
+
+ # First line returned is the output of stat
+ my $size = <$stdout_read>;
+
+ return ($pid, $size, $stdout_read, $stderr_read);
+}
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2010 Red Hat Inc.
+
+=head1 LICENSE
+
+Please see the file COPYING.LIB for the full license.
+
+=head1 SEE ALSO
+
+L<virt-v2v(1)>,
+L<http://libguestfs.org/>.
+
+=cut
+
+1;
diff --git a/v2v/virt-v2v.pl b/v2v/virt-v2v.pl
index 7d30856..3dd1b74 100755
--- a/v2v/virt-v2v.pl
+++ b/v2v/virt-v2v.pl
@@ -96,6 +96,10 @@ my $input_uri = "qemu:///system";
Specifies the connection to use when using the libvirt input method. If omitted,
this defaults to qemu:///system.
+B<N.B.> virt-v2v can currently automatically obtain guest storage from local
+libvirt connections, ESX connections, and connections over SSH. Other types of
+connection are not supported.
+
=cut
my $input_transport;
@@ -429,14 +433,11 @@ sub inspect_guest
=head1 PREPARING TO CONVERT A GUEST
-=head2 Xen guests
-
-The following steps are required before converting a Xen guest. Note that only
-local Xen guests are currently supported. These steps are not required for
-conversions from ESX, and will not be required for remote Xen guests when we
-support that.
+=head2 Local Xen guests
-=head3 Obtain domain XML for the guest domain
+B<N.B.> The following is required when converting guests on a host which used to
+run Xen, but has been updated to run KVM. It is not required when converting a
+Xen guest imported directly from a running libvirt/Xen instance.
virt-v2v uses a libvirt domain description to determine the current
configuration of the guest, including the location of its storage. This should
--
1.6.6.1
14 years, 7 months
[PATCH] Move all interaction with the config file into Sys::VirtV2V::Config
by Matthew Booth
This removes another chunk of functionality from GuestOS.
---
MANIFEST | 1 +
lib/Sys/VirtV2V/Config.pm | 339 +++++++++++++++++++++++++++++++++++++
lib/Sys/VirtV2V/Converter.pm | 56 +-----
lib/Sys/VirtV2V/GuestOS.pm | 94 ----------
lib/Sys/VirtV2V/GuestOS/RedHat.pm | 46 ++++--
v2v/virt-v2v.pl | 96 +----------
6 files changed, 385 insertions(+), 247 deletions(-)
create mode 100644 lib/Sys/VirtV2V/Config.pm
diff --git a/MANIFEST b/MANIFEST
index a83d682..65b5a8e 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -6,6 +6,7 @@ lib/Sys/VirtV2V.pm
lib/Sys/VirtV2V/ExecHelper.pm
lib/Sys/VirtV2V/GuestOS.pm
lib/Sys/VirtV2V/GuestOS/RedHat.pm
+lib/Sys/VirtV2V/Config.pm
lib/Sys/VirtV2V/Converter.pm
lib/Sys/VirtV2V/Converter/Linux.pm
lib/Sys/VirtV2V/Connection.pm
diff --git a/lib/Sys/VirtV2V/Config.pm b/lib/Sys/VirtV2V/Config.pm
new file mode 100644
index 0000000..b9e4d37
--- /dev/null
+++ b/lib/Sys/VirtV2V/Config.pm
@@ -0,0 +1,339 @@
+# Sys::VirtV2V::Config
+# Copyright (C) 2009 Red Hat Inc.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+package Sys::VirtV2V::Config;
+
+use strict;
+use warnings;
+
+use File::Spec;
+use File::stat;
+use XML::DOM;
+use XML::DOM::XPath;
+
+use Sys::VirtV2V::ExecHelper;
+use Sys::VirtV2V::UserMessage qw(user_message);
+
+use Locale::TextDomain 'virt-v2v';
+
+=pod
+
+=head1 NAME
+
+Sys::VirtV2V::Config - Manage virt-v2v's configuration file
+
+=head1 SYNOPSIS
+
+ use Sys::VirtV2V::Config;
+
+ $eh = Sys::VirtV2V::Config->new($config_path);
+
+ my $isopath = $config->get_transfer_iso();
+ my ($path, $deps) = $config->match_app($desc, $name, $arch);
+ my ($name, $type) = $config->map_network($oldname, $oldtype);
+
+=head1 DESCRIPTION
+
+Sys::VirtV2V::Config parses and queries the virt-v2v config file.
+
+=head1 METHODS
+
+=over
+
+=item new(path)
+
+Create a new Sys::VirtV2V::Config object to operate on the config file at
+I<path>.
+
+=cut
+
+sub new
+{
+ my $class = shift;
+ my ($path) = @_;
+
+ my $self = {};
+ bless($self, $class);
+
+ die(user_message(__x("Config file {path} doesn't exist",
+ path => $path))) unless (-e $path);
+
+ die(user_message(__x("Don't have permissions to read {path}",
+ path => $path))) unless (-r $path);
+
+ eval {
+ $self->{dom} = new XML::DOM::Parser->parsefile($path);
+ };
+
+ die(user_message(__x("Unable to parse config file {path}: {error}",
+ path => $path, error => $@))) if ($@);
+
+ $self->{path} = $path;
+ return $self;
+}
+
+=item get_transfer_iso
+
+Return the path to an iso image containing all software defined in the config
+file. Returns undef if no transfer iso is required.
+
+=cut
+
+sub get_transfer_iso
+{
+ my $self = shift;
+
+ my $dom = $self->{dom};
+
+ # path-root doesn't have to be defined
+ my ($root) = $dom->findnodes('/virt-v2v/path-root/text()');
+ $root = $root->getData() if (defined($root));
+
+ # Construct a list of path arguments to mkisofs from paths referenced in the
+ # 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()')) {
+ $path = $path->getData();
+
+ # Get the absolute path if iso-root was defined
+ my $abs;
+ if (defined($root)) {
+ $abs = File::Spec->catfile($root, $path);
+ } else {
+ $abs = $path;
+ }
+
+ # Check the referenced path is accessible
+ die(user_message(__x("Unable to access {path} referenced in ".
+ "the config file",
+ path => $path))) unless (-r $abs);
+
+ $path_args{"$path=$abs"} = 1;
+ }
+
+ # Nothing further to do if there are no paths
+ return if (keys(%path_args) == 0);
+
+ # Get the path of the transfer iso
+ my ($iso_path) = $dom->findnodes('/virt-v2v/iso-path/text()');
+
+ # We need this
+ die(user_message(__"<iso-path> must be specified in the configuration ".
+ "file")) unless (defined($iso_path));
+ $iso_path = $iso_path->getData();
+
+ # Check if the transfer iso exists, and is newer than the config file
+ if (-e $iso_path) {
+ my $iso_st = stat($iso_path)
+ or die(user_message(__x("Unable to stat iso file {path}: {error}",
+ path => $iso_path, error => $!)));
+
+ my $config_st = stat($self->{path})
+ or die(user_message(__x("Unable to stat config file {path}: ".
+ "{error}",
+ path => $self->{path}, error => $!)));
+
+ # Don't need to re-create if the iso file is newer than the config file
+ return $iso_path if ($iso_st->mtime > $config_st->mtime);
+ }
+
+ # Re-create the transfer iso
+ my $eh = Sys::VirtV2V::ExecHelper->run
+ ('mkisofs', '-o', $iso_path,
+ '-r', '-J',
+ '-V', '__virt-v2v_transfer__',
+ '-graft-points', keys(%path_args));
+ die(user_message(__x("Failed to create transfer iso. ".
+ "Command output was:\n{output}",
+ output => $eh->output()))) unless ($eh->status() == 0);
+
+ return $iso_path;
+}
+
+=item get_app_search(desc, name, arch)
+
+Return a string describing what v2v is looking for in the config file. The
+string is intended to be presented to the user to help improve the configuration
+file.
+
+=cut
+
+sub get_app_search
+{
+ my ($desc, $name, $arch) = @_;
+
+ my $distro = $desc->{distro};
+ my $major = $desc->{major_version};
+ my $minor = $desc->{minor_version};
+
+ my $search = "distro='$distro' name='$name'";
+ $search .= " major='$major'" if (defined($major));
+ $search .= " minor='$minor'" if (defined($minor));
+ $search .= " arch='$arch'";
+
+ return $search;
+}
+
+=item match_app
+
+Return a matching app entry from the virt-v2v configuration. The entry is
+returned as a list containing 2 values. The first contains the path to the
+application itself. The second contains an arrayref containing the paths of all
+the app's listed dependencies.
+
+=cut
+
+sub match_app
+{
+ my $self = shift;
+
+ my ($desc, $name, $arch) = @_;
+
+ my $dom = $self->{dom};
+
+ my $distro = $desc->{distro};
+ my $major = $desc->{major_version};
+ my $minor = $desc->{minor_version};
+
+ # Check we've got at least a distro from OS detection
+ die(user_message(__"Didn't detect OS distribution"))
+ unless (defined($distro));
+
+ # Create a list of xpath queries against the config which look for a
+ # matching <app> config entry in descending order of specificity
+ my @queries;
+ if (defined($major)) {
+ if (defined($minor)) {
+ push(@queries, _app_query($name, $distro, $major, $minor, $arch));
+ push(@queries, _app_query($name, $distro, $major, $minor, undef));
+ }
+
+ push(@queries, _app_query($name, $distro, $major, undef, $arch));
+ push(@queries, _app_query($name, $distro, $major, undef, undef));
+ }
+
+ push(@queries, _app_query($name, $distro, undef, undef, $arch));
+ push(@queries, _app_query($name, $distro, undef, undef, undef));
+
+ # Use the results of the first query which returns a result
+ my $app;
+ foreach my $query (@queries) {
+ ($app) = $dom->findnodes($query);
+ last if (defined($app));
+ }
+
+ die(user_message(__x("No app in config matches {search}",
+ search => get_app_search($desc, $name, $arch))))
+ unless (defined($app));
+
+ my %app;
+ my ($path) = $app->findnodes('path/text()');
+ die(user_message(__x("app entry in config doesn't contain a path: {xml}",
+ xml => $app->toString()))) unless (defined($path));
+ $path = $path->getData();
+
+ my @deps;
+ foreach my $dep ($app->findnodes('dep/text()')) {
+ push(@deps, $dep->getData());
+ }
+
+ return ($path, \@deps);
+}
+
+sub _app_query
+{
+ my ($name, $distro, $major, $minor, $arch) = @_;
+
+ my $query = "/virt-v2v/app[\@name='$name' and \@os='$distro' and ";
+ $query .= defined($major) ? "\@major='$major'" : 'not(@major)';
+ $query .= ' and ';
+ $query .= defined($minor) ? "\@minor='$minor'" : 'not(@minor)';
+ $query .= ' and ';
+ $query .= defined($arch) ? "\@arch='$arch'" : 'not(@arch)';
+ $query .= ']';
+
+ return $query;
+}
+
+=item map_network(oldname, oldtype)
+
+Return a new network name/type for I<oldname> and I<oldtype> from the config.
+Returns a list of 2 values: (I<name>, I<type>)
+
+=cut
+
+sub map_network
+{
+ my $self = shift;
+ my ($oldname, $oldtype) = @_;
+
+ my ($mapping) = $self->{dom}->findnodes
+ ("/virt-v2v/network[\@type='$oldtype' and \@name='$oldname']".
+ "/network");
+
+ unless (defined($mapping)) {
+ print STDERR user_message(__x("No mapping found for '{type}' ".
+ "interface: {name}",
+ type => $oldtype,
+ name => $oldname));
+ return;
+ }
+
+ my $newtype = $mapping->getAttributeNode('type');
+ $newtype &&= $newtype->getValue();
+ my $newname = $mapping->getAttributeNode('name');
+ $newname &&= $newname->getValue();
+
+ # Check type and name are defined for the mapping
+ unless (defined($newtype) && defined($newname)) {
+ print STDERR user_message(__x("WARNING: Invalid network ".
+ "mapping in config: {config}",
+ config => $mapping->toString()));
+ return;
+ }
+
+ # Check type is something we recognise
+ unless ($newtype eq 'network' || $newtype eq 'bridge') {
+ print STDERR user_message(__x("WARNING: Unknown interface type ".
+ "{type} in network mapping: {config}",
+ type => $newtype,
+ config => $mapping->toString()));
+ }
+
+ return ($newname, $newtype);
+}
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2010 Red Hat Inc.
+
+=head1 LICENSE
+
+Please see the file COPYING.LIB for the full license.
+
+=head1 SEE ALSO
+
+L<virt-v2v(1)>,
+L<http://libguestfs.org/>.
+
+=cut
+
+1;
diff --git a/lib/Sys/VirtV2V/Converter.pm b/lib/Sys/VirtV2V/Converter.pm
index 5dc8550..08f1e8f 100644
--- a/lib/Sys/VirtV2V/Converter.pm
+++ b/lib/Sys/VirtV2V/Converter.pm
@@ -442,57 +442,19 @@ sub _map_networks
exit(1);
}
- _update_interface($if, $name, $type, $config);
- }
-}
+ my ($newname, $newtype) = $config->map_network($name->getValue(),
+ $type);
-sub _update_interface
-{
- my ($if, $oldname, $oldtype, $config) = @_;
-
- my $oldnameval = $oldname->getValue();
- my ($mapping) = $config->findnodes
- ("/virt-v2v/network[\@type='$oldtype' and \@name='$oldnameval']".
- "/network");
-
- unless (defined($mapping)) {
- print STDERR user_message(__x("No mapping found for '{type}' ".
- "interface: {name}",
- type => $oldtype,
- name => $oldnameval));
- return;
- }
+ my ($source) = $if->findnodes('source');
- my $newtype = $mapping->getAttributeNode('type');
- $newtype &&= $newtype->getValue();
- my $newname = $mapping->getAttributeNode('name');
- $newname &&= $newname->getValue();
-
- # Check type and name are defined for the mapping
- unless (defined($newtype) && defined($newname)) {
- print STDERR user_message(__x("WARNING: Invalid network ".
- "mapping in config: {config}",
- config => $mapping->toString()));
- return;
- }
+ # Replace @bridge or @network in the source element with the correct
+ # mapped attribute name and value
+ $source->removeAttributeNode($name);
+ $source->setAttribute($newtype, $newname);
- # Check type is something we recognise
- unless ($newtype eq 'network' || $newtype eq 'bridge') {
- print STDERR user_message(__x("WARNING: Unknown interface type ".
- "{type} in network mapping: {config}",
- type => $newtype,
- config => $mapping->toString()));
+ # Update the type of the interface
+ $if->setAttribute('type', $newtype);
}
-
- my ($source) = $if->findnodes('source');
-
- # Replace @bridge or @network in the source element with the correct mapped
- # attribute name and value
- $source->removeAttributeNode($oldname);
- $source->setAttribute($newtype, $newname);
-
- # Update the type of the interface
- $if->setAttribute('type', $newtype);
}
=back
diff --git a/lib/Sys/VirtV2V/GuestOS.pm b/lib/Sys/VirtV2V/GuestOS.pm
index 3fb59ce..6165edd 100644
--- a/lib/Sys/VirtV2V/GuestOS.pm
+++ b/lib/Sys/VirtV2V/GuestOS.pm
@@ -150,100 +150,6 @@ sub get_memory_kb
return $mem_kb->getData();
}
-=item match_app
-
-Return a matching app entry from the virt-v2v configuration. The entry is
-returned as a hashref containing 2 entries. I<path> contains the path to the
-application itself. I<deps> contains an arrayref containing the paths of all the
-app's listed dependencies.
-
-=cut
-
-sub match_app
-{
- my $self = shift;
-
- my ($name, $arch) = @_;
-
- my $config = $self->{config};
-
- my $desc = $self->{desc};
- my $distro = $desc->{distro};
- my $major = $desc->{major_version};
- my $minor = $desc->{minor_version};
-
- # Check we've got at least a distro from OS detection
- die(user_message(__"Didn't detect OS distribution"))
- unless (defined($distro));
-
- my $search = "distro='$distro' name='$name'";
- $search .= " major='$major'" if (defined($major));
- $search .= " minor='$minor'" if (defined($minor));
- $search .= " arch='$arch'";
-
- die(user_message(__x("No config specified. No app match for {search}",
- search => $search))) unless (defined($config));
-
- # Create a list of xpath queries against the config which look for a
- # matching <app> config entry in descending order of specificity
- my @queries;
- if (defined($major)) {
- if (defined($minor)) {
- push(@queries, _app_query($name, $distro, $major, $minor, $arch));
- push(@queries, _app_query($name, $distro, $major, $minor, undef));
- }
-
- push(@queries, _app_query($name, $distro, $major, undef, $arch));
- push(@queries, _app_query($name, $distro, $major, undef, undef));
- }
-
- push(@queries, _app_query($name, $distro, undef, undef, $arch));
- push(@queries, _app_query($name, $distro, undef, undef, undef));
-
- # Use the results of the first query which returns a result
- my $app;
- foreach my $query (@queries) {
- ($app) = $config->findnodes($query);
- last if (defined($app));
- }
-
- die(user_message(__x("No app in config matches {search}",
- search => $search))) unless (defined($app));
-
- my %app;
- my ($path) = $app->findnodes('path/text()');
- die(user_message(__x("app entry in config doesn't contain a path: {xml}",
- xml => $app->toString()))) unless (defined($path));
- $path = $path->getData();
-
- my @deps;
- foreach my $dep ($app->findnodes('dep/text()')) {
- push(@deps, $dep->getData());
- }
-
- # Return a hash containing the application path and its dependencies
- my %ret;
- $ret{path} = $path;
- $ret{deps} = \@deps;
-
- return \%ret;
-}
-
-sub _app_query
-{
- my ($name, $distro, $major, $minor, $arch) = @_;
-
- my $query = "/virt-v2v/app[\@name='$name' and \@os='$distro' and ";
- $query .= defined($major) ? "\@major='$major'" : 'not(@major)';
- $query .= ' and ';
- $query .= defined($minor) ? "\@minor='$minor'" : 'not(@minor)';
- $query .= ' and ';
- $query .= defined($arch) ? "\@arch='$arch'" : 'not(@arch)';
- $query .= ']';
-
- return $query;
-}
-
=back
=head1 BACKEND INTERFACE
diff --git a/lib/Sys/VirtV2V/GuestOS/RedHat.pm b/lib/Sys/VirtV2V/GuestOS/RedHat.pm
index d062da1..f945847 100644
--- a/lib/Sys/VirtV2V/GuestOS/RedHat.pm
+++ b/lib/Sys/VirtV2V/GuestOS/RedHat.pm
@@ -475,9 +475,20 @@ sub add_kernel
}
}
- my $app;
+ my ($app, $deps);
eval {
- $app = $self->match_app($kernel_pkg, $kernel_arch);
+ my $desc = $self->{desc};
+
+ my $config = $self->{config};
+ unless (defined($config)) {
+ my $search = Sys::VirtV2V::Config::get_app_search
+ ($desc, $kernel_pkg, $kernel_arch);
+ die(user_message(__x("No config specified. No app match for ".
+ "{search}",
+ search => $search)));
+ }
+
+ ($app, $deps) = $config->match_app($desc, $kernel_pkg, $kernel_arch);
};
# Return undef if we didn't find a kernel
if ($@) {
@@ -485,13 +496,11 @@ sub add_kernel
return undef;
}
- my $path = $app->{path};
-
- return undef if($self->_is_installed($path));
+ return undef if($self->_is_installed($app));
my @install;
# Install any kernel dependencies which aren't already installed
- foreach my $dep (@{$app->{deps}}) {
+ foreach my $dep (@$deps) {
push(@install, $dep) unless($self->_is_installed($dep));
}
$self->_install_rpms(1, @install);
@@ -500,7 +509,7 @@ sub add_kernel
my $version;
my $g = $self->{g};
foreach my $file ($g->command_lines
- (["rpm", "-qlp", $self->_transfer_path($path)]))
+ (["rpm", "-qlp", $self->_transfer_path($app)]))
{
if($file =~ m{^/boot/vmlinuz-(.*)$}) {
$version = $1;
@@ -509,9 +518,9 @@ sub add_kernel
}
die(user_message(__x("{path} doesn't contain a valid kernel",
- path => $path))) if(!defined($version));
+ path => $app))) if(!defined($version));
- $self->_install_rpms(0, ($path));
+ $self->_install_rpms(0, ($app));
# Make augeas reload so it'll find the new kernel
$g->aug_load();
@@ -611,17 +620,26 @@ sub add_application
my $self = shift;
my $label = shift;
- my $user_arch = $self->{desc}->{arch};
+ my $desc = $self->{desc};
+ my $user_arch = $desc->{arch};
+
+ my $config = $self->{config};
+ unless (defined($config)) {
+ my $search = Sys::VirtV2V::Config::get_app_search($desc, $label,
+ $user_arch);
+ die(user_message(__x("No config specified. No app match for {search}",
+ search => $search)));
+ }
- my $app = $self->match_app($label, $user_arch);
+ 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->{path}));
+ return if($self->_is_installed($app));
- my @install = ($app->{path});
+ my @install = ($app);
# Add any dependencies which aren't already installed to the install set
- foreach my $dep (@{$app->{deps}}) {
+ foreach my $dep (@$deps) {
push(@install, $dep) unless ($self->_is_installed($dep));
}
diff --git a/v2v/virt-v2v.pl b/v2v/virt-v2v.pl
index c7ebad3..66b1844 100755
--- a/v2v/virt-v2v.pl
+++ b/v2v/virt-v2v.pl
@@ -21,8 +21,6 @@ use strict;
use Pod::Usage;
use Getopt::Long;
-use File::Spec;
-use File::stat;
use Locale::TextDomain 'virt-v2v';
@@ -32,6 +30,7 @@ use Sys::Guestfs::Lib qw(open_guest get_partitions inspect_all_partitions
inspect_in_detail);
use Sys::VirtV2V;
+use Sys::VirtV2V::Config;
use Sys::VirtV2V::Converter;
use Sys::VirtV2V::Connection::LibVirt;
use Sys::VirtV2V::Connection::LibVirtXML;
@@ -215,21 +214,7 @@ GetOptions ("help|?" => sub {
# Read the config file if one was given
my $config;
-if(defined($config_file)) {
- # Check we can access the config file
- die(user_message(__x("Config file {path} doesn't exist",
- path => $config_file))) unless (-e $config_file);
-
- die(user_message(__x("Don't have permissions to read {path}",
- path => $config_file))) unless (-r $config_file);
-
- eval {
- $config = new XML::DOM::Parser->parsefile($config_file);
- };
-
- die(user_message(__x("Unable to parse config file {path}: {error}",
- path => $config_file, error => $@))) if ($@);
-}
+$config = Sys::VirtV2V::Config->new($config_file) if defined($config_file);
my $target;
if ($output_method eq "libvirt") {
@@ -312,7 +297,8 @@ exit(1) unless(defined($dom));
my $storage = $conn->get_storage_paths();
# Create the transfer iso if required
-my $transferiso = get_transfer_iso($config, $config_file);
+my $transferiso;
+$transferiso = $config->get_transfer_iso() if (defined($config));
if ($output_method eq 'rhev') {
$) = "36 36";
@@ -380,80 +366,6 @@ sub close_guest_handle
}
}
-sub get_transfer_iso
-{
- my ($config, $config_file) = @_;
-
- # Nothing to do if there's no config
- return undef unless (defined($config));
-
- # path-root doesn't have to be defined
- my ($root) = $config->findnodes('/virt-v2v/path-root/text()');
- $root = $root->getData() if (defined($root));
-
- # Construct a list of path arguments to mkisofs from paths referenced in the
- # config file
- # We actually use a hash here to avoid duplicates
- my %path_args;
- foreach my $path ($config->findnodes('/virt-v2v/app/path/text() | '.
- '/virt-v2v/app/dep/text()')) {
- $path = $path->getData();
-
- # Get the absolute path if iso-root was defined
- my $abs;
- if (defined($root)) {
- $abs = File::Spec->catfile($root, $path);
- } else {
- $abs = $path;
- }
-
- # Check the referenced path is accessible
- die(user_message(__x("Unable to access {path} referenced in ".
- "the config file",
- path => $path))) unless (-r $abs);
-
- $path_args{"$path=$abs"} = 1;
- }
-
- # Nothing further to do if there are no paths
- return if (keys(%path_args) == 0);
-
- # Get the path of the transfer iso
- my ($iso_path) = $config->findnodes('/virt-v2v/iso-path/text()');
-
- # We need this
- die(user_message(__"<iso-path> must be specified in the configuration ".
- "file")) unless (defined($iso_path));
- $iso_path = $iso_path->getData();
-
- # Check that the transfer iso exists, and is newer than the config file
- if (-e $iso_path) {
- my $iso_st = stat($iso_path)
- or die(user_message(__x("Unable to stat iso file {path}: {error}",
- path => $iso_path, error => $!)));
-
- my $config_st = stat($config_file)
- or die(user_message(__x("Unable to stat config file {path}: ".
- "{error}",
- path => $config_file, error => $!)));
-
- # Don't need to re-create if the iso file is newer than the config file
- return $iso_path if ($iso_st->mtime > $config_st->mtime);
- }
-
- # Re-create the transfer iso
- my $eh = Sys::VirtV2V::ExecHelper->run
- ('mkisofs', '-o', $iso_path,
- '-r', '-J',
- '-V', '__virt-v2v_transfer__',
- '-graft-points', keys(%path_args));
- die(user_message(__x("Failed to create transfer iso. ".
- "Command output was:\n{output}",
- output => $eh->output()))) unless ($eh->status() == 0);
-
- return $iso_path;
-}
-
sub get_guestfs_handle
{
my ($storage, $transferiso) = @_;
--
1.6.6.1
14 years, 7 months