Matthew Booth
2010-Feb-01 17:23 UTC
[Libguestfs] [ESX support] Working ESX conversion for RHEL 5
With this patchset I have successfully[1] imported a RHEL 5 guest directly from ESX with the following command line: virt-v2v -ic 'esx://yellow.marston/?no_verify=1' -op transfer RHEL5-64 Login details are stored in ~/.netrc Note that this is the only guest I've tested against. I haven't for example, checked that I haven't broken Xen imports. Matt [1] With the exception of network reconfiguration. After import I manually munged the network config with virsh edit.
Matthew Booth
2010-Feb-01 17:23 UTC
[Libguestfs] [PATCH 1/8] MetadataReader: Allow different libvirt connections for input and output
Command line option changes: -s -> -f -c -> -ic -c, --connect now generates an error message. New option -oc specifies output connection. --- lib/Sys/VirtV2V/MetadataReader.pm | 20 +++-- lib/Sys/VirtV2V/MetadataReader/LibVirt.pm | 9 ++- lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm | 5 +- v2v/virt-v2v.pl | 108 ++++++++++++++------------ 4 files changed, 79 insertions(+), 63 deletions(-) diff --git a/lib/Sys/VirtV2V/MetadataReader.pm b/lib/Sys/VirtV2V/MetadataReader.pm index 6a1bec3..7b5e7d5 100644 --- a/lib/Sys/VirtV2V/MetadataReader.pm +++ b/lib/Sys/VirtV2V/MetadataReader.pm @@ -36,7 +36,9 @@ Sys::VirtV2V::MetadataReader - Read a variety of guest metadata formats use Sys::VirtV2V::MetadataReader; - $reader = Sys::VirtV2V::MetadataReader->instantiate("libvirtxml", $vmm, @args); + $reader = Sys::VirtV2V::MetadataReader->instantiate("libvirtxml", $uri, + $config, @args); + exit 1 unless($mdr->is_configured()); $dom = $reader->get_dom(); =head1 DESCRIPTION @@ -53,7 +55,7 @@ implements methods to access backends. =over -=item instantiate(name, vmm, @args) +=item instantiate(name, $uri, $config, @args) =over @@ -61,13 +63,13 @@ implements methods to access backends. The name of the module to instantiate. -=item config +=item uri -A parsed virt-v2v configuration file. +A URI describing the target connection. -=item vmm +=item config -A Sys::Virt connection. +A parsed virt-v2v configuration file. =item args @@ -83,15 +85,15 @@ sub instantiate { my $class = shift; - my ($name, $config, $vmm, @args) = @_; + my ($name, $uri, $config, @args) = @_; defined($name) or carp("instantiate called without name argument"); + defined($uri) or carp("instantiate called without uri argument"); defined($config) or carp("instantiate called without config argument"); - defined($vmm) or carp("instantiate called without vmm argument"); foreach my $module ($class->modules()) { if($module->get_name() eq $name) { - return $module->_new($config->{$name}, $vmm, @args); + return $module->_new($uri, $config->{$name}, @args); } } diff --git a/lib/Sys/VirtV2V/MetadataReader/LibVirt.pm b/lib/Sys/VirtV2V/MetadataReader/LibVirt.pm index d18f432..e6fcfff 100644 --- a/lib/Sys/VirtV2V/MetadataReader/LibVirt.pm +++ b/lib/Sys/VirtV2V/MetadataReader/LibVirt.pm @@ -38,7 +38,8 @@ Sys::VirtV2V::MetadataReader::LibVirt - Read libvirt metadata from libvirtd use Sys::VirtV2V::MetadataReader; - $reader = Sys::VirtV2V::MetadataReader->instantiate("libvirt", $vmm, @args); + $reader = Sys::VirtV2V::MetadataReader->instantiate + ("libvirt", "xen+ssh://xenserver.example.com/", $config, @args); $dom = $reader->get_dom(); =head1 DESCRIPTION @@ -62,12 +63,16 @@ sub _new { my $class = shift; - my ($config, $vmm, @args) = @_; + my ($uri, $config, @args) = @_; my $self = {}; bless($self, $class); + my @vmm_params = (auth => 1); + push(@vmm_params, url => $uri) if defined($uri); + my $vmm = Sys::Virt->new(@vmm_params); + $self->{vmm} = $vmm; $self->_handle_args(@args); diff --git a/lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm b/lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm index 877b7e5..191210f 100644 --- a/lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm +++ b/lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm @@ -37,7 +37,8 @@ Sys::VirtV2V::MetadataReader::LibVirtXML - Read libvirt XML from a file use Sys::VirtV2V::MetadataReader; - $reader = Sys::VirtV2V::MetadataReader->instantiate("libvirtxml", $vmm, @args); + $reader = Sys::VirtV2V::MetadataReader->instantiate("libvirtxml", undef, + $config, @args); $dom = $reader->get_dom(); =head1 DESCRIPTION @@ -61,7 +62,7 @@ sub _new { my $class = shift; - my ($config, $vmm, @args) = @_; + my ($uri, $config, @args) = @_; my %obj = (); my $self = \%obj; diff --git a/v2v/virt-v2v.pl b/v2v/virt-v2v.pl index 0ad3c6e..ad1610f 100755 --- a/v2v/virt-v2v.pl +++ b/v2v/virt-v2v.pl @@ -46,9 +46,9 @@ virt-v2v - Convert a guest to use KVM virt-v2v guest-domain.xml - virt-v2v -s virt-v2v.conf guest-domain.xml + virt-v2v -f virt-v2v.conf guest-domain.xml - virt-v2v --connect qemu:///system guest-domain.xml + virt-v2v -ic qemu:///system guest-domain =head1 DESCRIPTION @@ -112,58 +112,59 @@ table below. =cut -my $help; +my $input_method = "libvirt"; -=item B<--help> - -Display brief help. - -=cut +=item B<-i input> -my $version; +Specifies how the conversion source metadata can be obtained. The default is +C<libvirt>. Supported options are: -=item B<--version> +=over -Display version number and exit. +=item I<libvirt> -=cut +Guest argument is the name of a libvirt domain. -my $uri; +=item I<libvirtxml> -=item B<--connect URI> | B<-c URI> +Guest argument is the path to an XML file describing a libvirt domain. -Connect to libvirt using the given I<URI>. If omitted, then we connect to the -default libvirt hypervisor. +=back =cut -my $input = "libvirt"; +my $input_uri; -=item B<--input input> | B<-i input> +=item B<-ic URI> -The specified guest description uses the given I<input format>. The default is -C<libvirt>. Supported options are: +Specifies the connection to use when using the libvirt input method. If omitted, +then we connect to the default libvirt hypervisor. -=over - -=item I<libvirt> - -Guest argument is the name of a libvirt domain. +=cut -=item I<libvirtxml> +my $output_uri = "qemu:///system"; -Guest argument is the path to an XML file describing a libvirt domain. +=item B<-oc URI> -=back +Specifies the libvirt connection to use to create the converted guest. If +ommitted, this defaults to qemu:///system. =cut my $config_file; -=item B<--config file> | B<-s file> +=item B<-f file>, B<--config file> Load the virt-v2v configuration from I<file>. There is no default. +=item B<--help> + +Display brief help. + +=item B<--version> + +Display version number and exit. + =back =cut @@ -171,26 +172,28 @@ Load the virt-v2v configuration from I<file>. There is no default. # Initialise the message output prefix Sys::VirtV2V::UserMessage->set_identifier('virt-v2v'); -GetOptions ("help|?" => \$help, - "version" => \$version, - "connect|c=s" => \$uri, - "input|i=s" => \$input, - "config|s=s" => \$config_file - ) or pod2usage(2); -pod2usage(0) if($help); - -if ($version) { - print "$Sys::VirtV2V::VERSION\n"; - exit(0); -} +GetOptions ("help|?" => sub { + pod2usage(0); + }, + "version" => sub { + print "$Sys::VirtV2V::VERSION\n"; + exit(0); + }, + "c|connect" => sub { + # -c|--connect is the default for other virt tools. Be nice to + # the user and point out that virt-v2v is different. + pod2usage({ -message => __("Use -ic or -oc to specify an ". + "input or an output connection"), + -exitval => 1 }); + }, + "i=s" => \$input_method, + "ic=s" => \$input_uri, + "oc=s" => \$output_uri, + "f|config=s" => \$config_file +) or pod2usage(2); pod2usage(user_message(__"no guest argument given")) if @ARGV == 0; -# Connect to libvirt -my @vmm_params = (auth => 1); -push(@vmm_params, uri => $uri) if(defined($uri)); -my $vmm = Sys::Virt->new(@vmm_params); - # Read the config file if one was given my $config = {}; if(defined($config_file)) { @@ -206,11 +209,11 @@ if(defined($config_file)) { } # Get an appropriate MetadataReader -my $mdr = Sys::VirtV2V::MetadataReader->instantiate($input, $config, - $vmm, @ARGV); +my $mdr = Sys::VirtV2V::MetadataReader->instantiate($input_method, $input_uri, + $config, @ARGV); if(!defined($mdr)) { - print STDERR user_message __x("{input} is not a valid metadata format", - input => $input); + print STDERR user_message __x("{input} is not a valid input method", + input => $input_method); exit(1); } @@ -239,6 +242,11 @@ my $os = inspect_guest($g); # Instantiate a GuestOS instance to manipulate the guest my $guestos = Sys::VirtV2V::GuestOS->instantiate($g, $os); +# Connect to target libvirt +my @vmm_params = (auth => 1); +push(@vmm_params, uri => $output_uri) if(defined($output_uri)); +my $vmm = Sys::Virt->new(@vmm_params); + # Modify the guest and its metadata for the target hypervisor Sys::VirtV2V::Converter->convert($vmm, $guestos, $dom, $os); -- 1.6.6
Matthew Booth
2010-Feb-01 17:23 UTC
[Libguestfs] [PATCH 2/8] Connection: Rename MetadataReader to Connection
--- MANIFEST | 6 ++-- lib/Sys/VirtV2V.pm | 2 +- .../VirtV2V/{MetadataReader.pm => Connection.pm} | 22 ++++++++-------- .../{MetadataReader => Connection}/LibVirt.pm | 26 ++++++++++---------- .../{MetadataReader => Connection}/LibVirtXML.pm | 26 ++++++++++---------- po/POTFILES.in | 6 ++-- po/es.po | 16 ++++++------ po/fr.po | 16 ++++++------ po/it.po | 16 ++++++------ po/pl.po | 16 ++++++------ po/ru.po | 16 ++++++------ po/virt-v2v.pot | 16 ++++++------ po/zh_CN.po | 16 ++++++------ snapshot/v2v-snapshot.pl | 6 ++-- v2v/virt-v2v.pl | 8 +++--- 15 files changed, 107 insertions(+), 107 deletions(-) rename lib/Sys/VirtV2V/{MetadataReader.pm => Connection.pm} (80%) rename lib/Sys/VirtV2V/{MetadataReader => Connection}/LibVirt.pm (84%) rename lib/Sys/VirtV2V/{MetadataReader => Connection}/LibVirtXML.pm (85%) diff --git a/MANIFEST b/MANIFEST index 8603430..7bfad76 100644 --- a/MANIFEST +++ b/MANIFEST @@ -8,9 +8,9 @@ lib/Sys/VirtV2V/GuestOS.pm lib/Sys/VirtV2V/GuestOS/RedHat.pm lib/Sys/VirtV2V/Converter.pm lib/Sys/VirtV2V/Converter/Linux.pm -lib/Sys/VirtV2V/MetadataReader.pm -lib/Sys/VirtV2V/MetadataReader/LibVirt.pm -lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm +lib/Sys/VirtV2V/Connection.pm +lib/Sys/VirtV2V/Connection/LibVirt.pm +lib/Sys/VirtV2V/Connection/LibVirtXML.pm lib/Sys/VirtV2V/UserMessage.pm MANIFEST This list of files MANIFEST.SKIP diff --git a/lib/Sys/VirtV2V.pm b/lib/Sys/VirtV2V.pm index a8731af..f74f347 100644 --- a/lib/Sys/VirtV2V.pm +++ b/lib/Sys/VirtV2V.pm @@ -64,7 +64,7 @@ L<v2v-snapshot(1)>, L<Sys::VirtV2V::GuestOS(3pm)>, L<Sys::VirtV2V::HVSource(3pm)>, L<Sys::VirtV2V::Converter(3pm)>, -L<Sys::VirtV2V::MetadataReader(3pm)>, +L<Sys::VirtV2V::Connection(3pm)>, L<http://libguestfs.org/> =cut diff --git a/lib/Sys/VirtV2V/MetadataReader.pm b/lib/Sys/VirtV2V/Connection.pm similarity index 80% rename from lib/Sys/VirtV2V/MetadataReader.pm rename to lib/Sys/VirtV2V/Connection.pm index 7b5e7d5..31fbdb3 100644 --- a/lib/Sys/VirtV2V/MetadataReader.pm +++ b/lib/Sys/VirtV2V/Connection.pm @@ -1,4 +1,4 @@ -# Sys::VirtV2V::MetadataReader +# Sys::VirtV2V::Connection # Copyright (C) 2009 Red Hat Inc. # # This library is free software; you can redistribute it and/or @@ -15,13 +15,13 @@ # 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::MetadataReader; +package Sys::VirtV2V::Connection; use strict; use warnings; use Module::Pluggable sub_name => 'modules', - search_path => ['Sys::VirtV2V::MetadataReader'], + search_path => ['Sys::VirtV2V::Connection'], require => 1; use Carp; @@ -30,25 +30,25 @@ use Carp; =head1 NAME -Sys::VirtV2V::MetadataReader - Read a variety of guest metadata formats +Sys::VirtV2V::Connection - Read a variety of guest metadata formats =head1 SYNOPSIS - use Sys::VirtV2V::MetadataReader; + use Sys::VirtV2V::Connection; - $reader = Sys::VirtV2V::MetadataReader->instantiate("libvirtxml", $uri, + $reader = Sys::VirtV2V::Connection->instantiate("libvirtxml", $uri, $config, @args); exit 1 unless($mdr->is_configured()); $dom = $reader->get_dom(); =head1 DESCRIPTION -Sys::VirtV2V::MetadataReader reads the metadata of a, possibly foreign, +Sys::VirtV2V::Connection reads the metadata of a, possibly foreign, guest. It provides the DOM representation of an equivalent libvirt XML representation. -Sys::VirtV2V::MetadataReader is an interface to various backends, each of -which implement a consistent API. Sys::VirtV2V::MetadataReader itself only +Sys::VirtV2V::Connection is an interface to various backends, each of +which implement a consistent API. Sys::VirtV2V::Connection itself only implements methods to access backends. =head1 METHODS @@ -134,8 +134,8 @@ Please see the file COPYING.LIB for the full license. =head1 SEE ALSO -L<Sys::VirtV2V::MetadataReader::LibVirt(3pm)>, -L<Sys::VirtV2V::MetadataReader::LibVirtXML(3pm)>, +L<Sys::VirtV2V::Connection::LibVirt(3pm)>, +L<Sys::VirtV2V::Connection::LibVirtXML(3pm)>, L<virt-v2v(1)>, L<v2v-snapshot(1)>, L<http://libguestfs.org/>. diff --git a/lib/Sys/VirtV2V/MetadataReader/LibVirt.pm b/lib/Sys/VirtV2V/Connection/LibVirt.pm similarity index 84% rename from lib/Sys/VirtV2V/MetadataReader/LibVirt.pm rename to lib/Sys/VirtV2V/Connection/LibVirt.pm index e6fcfff..d3aa80d 100644 --- a/lib/Sys/VirtV2V/MetadataReader/LibVirt.pm +++ b/lib/Sys/VirtV2V/Connection/LibVirt.pm @@ -1,4 +1,4 @@ -# Sys::VirtV2V::MetadataReader::LibVirt +# Sys::VirtV2V::Connection::LibVirt # Copyright (C) 2009 Red Hat Inc. # # This library is free software; you can redistribute it and/or @@ -15,7 +15,7 @@ # 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::MetadataReader::LibVirt; +package Sys::VirtV2V::Connection::LibVirt; use strict; use warnings; @@ -32,25 +32,25 @@ use Locale::TextDomain 'virt-v2v'; =head1 NAME -Sys::VirtV2V::MetadataReader::LibVirt - Read libvirt metadata from libvirtd +Sys::VirtV2V::Connection::LibVirt - Read libvirt metadata from libvirtd =head1 SYNOPSIS - use Sys::VirtV2V::MetadataReader; + use Sys::VirtV2V::Connection; - $reader = Sys::VirtV2V::MetadataReader->instantiate + $reader = Sys::VirtV2V::Connection->instantiate ("libvirt", "xen+ssh://xenserver.example.com/", $config, @args); $dom = $reader->get_dom(); =head1 DESCRIPTION -Sys::VirtV2V::MetadataReader::LibVirt is a backend for -Sys::VirtV2V::MetadataReader which reads a guest's libvirt XML directly from a +Sys::VirtV2V::Connection::LibVirt is a backend for +Sys::VirtV2V::Connection which reads a guest's libvirt XML directly from a libvirt connection. =head1 METHODS -See BACKEND INTERFACE in L<Sys::VirtV2V::MetadataReader> for a detailed +See BACKEND INTERFACE in L<Sys::VirtV2V::Connection> for a detailed description of its exported methods. =over @@ -94,9 +94,9 @@ sub _handle_args } } -=item Sys::VirtV2V::MetadataReader::LibVirtXML->get_name() +=item Sys::VirtV2V::Connection::LibVirtXML->get_name() -See BACKEND INTERFACE in L<Sys::VirtV2V::MetadataReader> for details. +See BACKEND INTERFACE in L<Sys::VirtV2V::Connection> for details. =cut @@ -109,7 +109,7 @@ sub get_name =item is_configured() -See BACKEND INTERFACE in L<Sys::VirtV2V::MetadataReader> for details. +See BACKEND INTERFACE in L<Sys::VirtV2V::Connection> for details. =cut @@ -183,7 +183,7 @@ sub _get_domain =item get_dom() -See BACKEND INTERFACE in L<Sys::VirtV2V::MetadataReader> for details. +See BACKEND INTERFACE in L<Sys::VirtV2V::Connection> for details. =cut @@ -216,7 +216,7 @@ Please see the file COPYING.LIB for the full license. =head1 SEE ALSO -L<Sys::VirtV2V::MetadataReader(3)>, +L<Sys::VirtV2V::Connection(3)>, L<virt-v2v(1)>, L<v2v-snapshot(1)>, L<http://libguestfs.org/>. diff --git a/lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm b/lib/Sys/VirtV2V/Connection/LibVirtXML.pm similarity index 85% rename from lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm rename to lib/Sys/VirtV2V/Connection/LibVirtXML.pm index 191210f..c05aa0f 100644 --- a/lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm +++ b/lib/Sys/VirtV2V/Connection/LibVirtXML.pm @@ -1,4 +1,4 @@ -# Sys::VirtV2V::MetadataReader::LibVirtXML +# Sys::VirtV2V::Connection::LibVirtXML # Copyright (C) 2009 Red Hat Inc. # # This library is free software; you can redistribute it and/or @@ -15,7 +15,7 @@ # 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::MetadataReader::LibVirtXML; +package Sys::VirtV2V::Connection::LibVirtXML; use strict; use warnings; @@ -31,25 +31,25 @@ use Locale::TextDomain 'virt-v2v'; =head1 NAME -Sys::VirtV2V::MetadataReader::LibVirtXML - Read libvirt XML from a file +Sys::VirtV2V::Connection::LibVirtXML - Read libvirt XML from a file =head1 SYNOPSIS - use Sys::VirtV2V::MetadataReader; + use Sys::VirtV2V::Connection; - $reader = Sys::VirtV2V::MetadataReader->instantiate("libvirtxml", undef, + $reader = Sys::VirtV2V::Connection->instantiate("libvirtxml", undef, $config, @args); $dom = $reader->get_dom(); =head1 DESCRIPTION -Sys::VirtV2V::MetadataReader::LibVirtXML is a backend for -Sys::VirtV2V::MetadataReader which reads libvirt XML guest descriptions from a +Sys::VirtV2V::Connection::LibVirtXML is a backend for +Sys::VirtV2V::Connection which reads libvirt XML guest descriptions from a file. =head1 METHODS -See BACKEND INTERFACE in L<Sys::VirtV2V::MetadataReader> for a detailed +See BACKEND INTERFACE in L<Sys::VirtV2V::Connection> for a detailed description of its exported methods. =over @@ -116,9 +116,9 @@ sub _handle_args } } -=item Sys::VirtV2V::MetadataReader::LibVirtXML->get_name() +=item Sys::VirtV2V::Connection::LibVirtXML->get_name() -See BACKEND INTERFACE in L<Sys::VirtV2V::MetadataReader> for details. +See BACKEND INTERFACE in L<Sys::VirtV2V::Connection> for details. =cut @@ -131,7 +131,7 @@ sub get_name =item is_configured() -See BACKEND INTERFACE in L<Sys::VirtV2V::MetadataReader> for details. +See BACKEND INTERFACE in L<Sys::VirtV2V::Connection> for details. =cut @@ -153,7 +153,7 @@ sub is_configured =item get_dom() -See BACKEND INTERFACE in L<Sys::VirtV2V::MetadataReader> for details. +See BACKEND INTERFACE in L<Sys::VirtV2V::Connection> for details. =cut @@ -220,7 +220,7 @@ Please see the file COPYING.LIB for the full license. =head1 SEE ALSO -L<Sys::VirtV2V::MetadataReader(3)>, +L<Sys::VirtV2V::Connection(3)>, L<virt-v2v(1)>, L<v2v-snapshot(1)>, L<http://libguestfs.org/>. diff --git a/po/POTFILES.in b/po/POTFILES.in index 40331c8..8fa921c 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -4,8 +4,8 @@ ../lib/Sys/VirtV2V/HVSource/Xen/Linux.pm ../lib/Sys/VirtV2V/Converter/Linux.pm ../lib/Sys/VirtV2V/Converter.pm -../lib/Sys/VirtV2V/MetadataReader/LibVirt.pm -../lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm -../lib/Sys/VirtV2V/MetadataReader.pm +../lib/Sys/VirtV2V/Connection/LibVirt.pm +../lib/Sys/VirtV2V/Connection/LibVirtXML.pm +../lib/Sys/VirtV2V/Connection.pm ../snapshot/v2v-snapshot.pl ../v2v/virt-v2v.pl diff --git a/po/es.po b/po/es.po index 4ca3dcc..2ee7eff 100644 --- a/po/es.po +++ b/po/es.po @@ -177,40 +177,40 @@ msgstr "El hipervisor conectado no soporta la caracter??stica {feature}" msgid "Unable to find a module to configure this guest" msgstr "No es posible encontrar un m??dulo para configurar este hu??sped" -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirt.pm:85 -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm:113 +#: ../lib/Sys/VirtV2V/Connection/LibVirt.pm:85 +#: ../lib/Sys/VirtV2V/Connection/LibVirtXML.pm:113 #, perl-brace-format msgid "WARNING: {modulename} only takes a single filename." msgstr "ADVERTENCIA: {modulename} s??lo necesita un nombre de archivo." -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirt.pm:125 +#: ../lib/Sys/VirtV2V/Connection/LibVirt.pm:125 #, perl-brace-format msgid "Guest {name} must be shutdown first" msgstr "Hu??sped {name} se debe apagar primero" -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirt.pm:145 +#: ../lib/Sys/VirtV2V/Connection/LibVirt.pm:145 #, perl-brace-format msgid "{name} isn't a valid guest name" msgstr "{name} no es un nombre de hu??sped v??lido" -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm:90 +#: ../lib/Sys/VirtV2V/Connection/LibVirtXML.pm:90 #, perl-brace-format msgid "WARNING: unknown configuration directive {directive} in {name} section." msgstr "" "ADVERTENCIA: directiva {directive} de configuraci??n desconocida en la " "secci??n {name}. " -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm:143 +#: ../lib/Sys/VirtV2V/Connection/LibVirtXML.pm:143 #, perl-brace-format msgid "You must specify a filename when using {modulename}" msgstr "Debe especificar un nombre de archivo cuando utilice {modulename}" -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm:167 +#: ../lib/Sys/VirtV2V/Connection/LibVirtXML.pm:167 #, perl-brace-format msgid "Failed to open {path}: {error}" msgstr "Fall?? al abrir {path}: {error}" -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm:180 +#: ../lib/Sys/VirtV2V/Connection/LibVirtXML.pm:180 #, perl-brace-format msgid "Unable to parse {path}: {error}" msgstr "Imposible analizar sint??cticamente a {path}: {error}" diff --git a/po/fr.po b/po/fr.po index d95a3c0..ff3c893 100644 --- a/po/fr.po +++ b/po/fr.po @@ -159,38 +159,38 @@ msgstr "" msgid "Unable to find a module to configure this guest" msgstr "" -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirt.pm:85 -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm:113 +#: ../lib/Sys/VirtV2V/Connection/LibVirt.pm:85 +#: ../lib/Sys/VirtV2V/Connection/LibVirtXML.pm:113 #, perl-brace-format msgid "WARNING: {modulename} only takes a single filename." msgstr "" -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirt.pm:125 +#: ../lib/Sys/VirtV2V/Connection/LibVirt.pm:125 #, perl-brace-format msgid "Guest {name} must be shutdown first" msgstr "" -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirt.pm:145 +#: ../lib/Sys/VirtV2V/Connection/LibVirt.pm:145 #, perl-brace-format msgid "{name} isn't a valid guest name" msgstr "" -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm:90 +#: ../lib/Sys/VirtV2V/Connection/LibVirtXML.pm:90 #, perl-brace-format msgid "WARNING: unknown configuration directive {directive} in {name} section." msgstr "" -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm:143 +#: ../lib/Sys/VirtV2V/Connection/LibVirtXML.pm:143 #, perl-brace-format msgid "You must specify a filename when using {modulename}" msgstr "" -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm:167 +#: ../lib/Sys/VirtV2V/Connection/LibVirtXML.pm:167 #, perl-brace-format msgid "Failed to open {path}: {error}" msgstr "" -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm:180 +#: ../lib/Sys/VirtV2V/Connection/LibVirtXML.pm:180 #, perl-brace-format msgid "Unable to parse {path}: {error}" msgstr "" diff --git a/po/it.po b/po/it.po index 2324fe3..56f6f92 100644 --- a/po/it.po +++ b/po/it.po @@ -174,40 +174,40 @@ msgstr "L'hypervisor connesso non supporta la funzione {feature}" msgid "Unable to find a module to configure this guest" msgstr "Impossibile trovare un modulo per la configurazione dell'ospite" -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirt.pm:85 -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm:113 +#: ../lib/Sys/VirtV2V/Connection/LibVirt.pm:85 +#: ../lib/Sys/VirtV2V/Connection/LibVirtXML.pm:113 #, perl-brace-format msgid "WARNING: {modulename} only takes a single filename." msgstr "ATTENZIONE: {modulename} accetta un solo nome file." -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirt.pm:125 +#: ../lib/Sys/VirtV2V/Connection/LibVirt.pm:125 #, perl-brace-format msgid "Guest {name} must be shutdown first" msgstr "" -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirt.pm:145 +#: ../lib/Sys/VirtV2V/Connection/LibVirt.pm:145 #, perl-brace-format msgid "{name} isn't a valid guest name" msgstr "{name} non ?? un nome di ospite valido" -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm:90 +#: ../lib/Sys/VirtV2V/Connection/LibVirtXML.pm:90 #, perl-brace-format msgid "WARNING: unknown configuration directive {directive} in {name} section." msgstr "" "ATTENZIONE: direttiva di configurazione {directive} sconosciuta nella " "sezione {name}." -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm:143 +#: ../lib/Sys/VirtV2V/Connection/LibVirtXML.pm:143 #, perl-brace-format msgid "You must specify a filename when using {modulename}" msgstr "Quando si usa {modulename} ?? necessario specificare un nome file" -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm:167 +#: ../lib/Sys/VirtV2V/Connection/LibVirtXML.pm:167 #, perl-brace-format msgid "Failed to open {path}: {error}" msgstr "Impossibile aprire {path}: {error}" -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm:180 +#: ../lib/Sys/VirtV2V/Connection/LibVirtXML.pm:180 #, perl-brace-format msgid "Unable to parse {path}: {error}" msgstr "Impossibile eseguire il parsing di {path}: {error}" diff --git a/po/pl.po b/po/pl.po index 8e06175..6329aba 100644 --- a/po/pl.po +++ b/po/pl.po @@ -167,39 +167,39 @@ msgstr "Po????czony nadzorca nie obs??uguje funkcji {feature}" msgid "Unable to find a module to configure this guest" msgstr "Nie mo??na odnale???? modu??u do skonfigurowania tego go??cia" -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirt.pm:85 -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm:113 +#: ../lib/Sys/VirtV2V/Connection/LibVirt.pm:85 +#: ../lib/Sys/VirtV2V/Connection/LibVirtXML.pm:113 #, perl-brace-format msgid "WARNING: {modulename} only takes a single filename." msgstr "OSTRZE??ENIE: {modulename} przyjmuje tylko jedn?? nazw?? pliku." -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirt.pm:125 +#: ../lib/Sys/VirtV2V/Connection/LibVirt.pm:125 #, perl-brace-format msgid "Guest {name} must be shutdown first" msgstr "Go???? {name} musi najpierw zosta?? wy????czony" -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirt.pm:145 +#: ../lib/Sys/VirtV2V/Connection/LibVirt.pm:145 #, perl-brace-format msgid "{name} isn't a valid guest name" msgstr "{name} nie jest prawid??ow?? nazw?? go??cia" -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm:90 +#: ../lib/Sys/VirtV2V/Connection/LibVirtXML.pm:90 #, perl-brace-format msgid "WARNING: unknown configuration directive {directive} in {name} section." msgstr "" "OSTRZE??ENIE: nieznana dyrektywa konfiguracji {directive} w sekcji {name}." -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm:143 +#: ../lib/Sys/VirtV2V/Connection/LibVirtXML.pm:143 #, perl-brace-format msgid "You must specify a filename when using {modulename}" msgstr "Nale??y poda?? nazw?? pliku podczas u??ywania {modulename}" -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm:167 +#: ../lib/Sys/VirtV2V/Connection/LibVirtXML.pm:167 #, perl-brace-format msgid "Failed to open {path}: {error}" msgstr "Otwarcie {path} nie powiod??o si??: {error}" -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm:180 +#: ../lib/Sys/VirtV2V/Connection/LibVirtXML.pm:180 #, perl-brace-format msgid "Unable to parse {path}: {error}" msgstr "Nie mo??na przetworzy?? {path}: {error}" diff --git a/po/ru.po b/po/ru.po index 436160a..cd1d5f9 100644 --- a/po/ru.po +++ b/po/ru.po @@ -167,38 +167,38 @@ msgstr "???????????????????????? ???????????????????? ???? ???????????????????? msgid "Unable to find a module to configure this guest" msgstr "???? ???????????? ???????????? ?????? ?????????????????? ?????????? ??????????" -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirt.pm:85 -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm:113 +#: ../lib/Sys/VirtV2V/Connection/LibVirt.pm:85 +#: ../lib/Sys/VirtV2V/Connection/LibVirtXML.pm:113 #, perl-brace-format msgid "WARNING: {modulename} only takes a single filename." msgstr "????????????????: {modulename} ?????????? ?????????????????? ???????????? ???????? ?????? ??????????." -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirt.pm:125 +#: ../lib/Sys/VirtV2V/Connection/LibVirt.pm:125 #, perl-brace-format msgid "Guest {name} must be shutdown first" msgstr "" -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirt.pm:145 +#: ../lib/Sys/VirtV2V/Connection/LibVirt.pm:145 #, perl-brace-format msgid "{name} isn't a valid guest name" msgstr "???????????????????????? ?????? ??????????: {name}" -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm:90 +#: ../lib/Sys/VirtV2V/Connection/LibVirtXML.pm:90 #, perl-brace-format msgid "WARNING: unknown configuration directive {directive} in {name} section." msgstr "????????????????: ?????????????????????? ?????????????????? ?????????????????? {directive} ?? ???????????? {name}." -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm:143 +#: ../lib/Sys/VirtV2V/Connection/LibVirtXML.pm:143 #, perl-brace-format msgid "You must specify a filename when using {modulename}" msgstr "???????????????????? ?????????????? ?????? ?????????? ?????? ?????????????????????????? {modulename}" -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm:167 +#: ../lib/Sys/VirtV2V/Connection/LibVirtXML.pm:167 #, perl-brace-format msgid "Failed to open {path}: {error}" msgstr "???? ?????????????? ?????????????? {path}: {error}" -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm:180 +#: ../lib/Sys/VirtV2V/Connection/LibVirtXML.pm:180 #, perl-brace-format msgid "Unable to parse {path}: {error}" msgstr "???? ?????????????? ?????????????????? {path}: {error}" diff --git a/po/virt-v2v.pot b/po/virt-v2v.pot index d95a3c0..ff3c893 100644 --- a/po/virt-v2v.pot +++ b/po/virt-v2v.pot @@ -159,38 +159,38 @@ msgstr "" msgid "Unable to find a module to configure this guest" msgstr "" -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirt.pm:85 -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm:113 +#: ../lib/Sys/VirtV2V/Connection/LibVirt.pm:85 +#: ../lib/Sys/VirtV2V/Connection/LibVirtXML.pm:113 #, perl-brace-format msgid "WARNING: {modulename} only takes a single filename." msgstr "" -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirt.pm:125 +#: ../lib/Sys/VirtV2V/Connection/LibVirt.pm:125 #, perl-brace-format msgid "Guest {name} must be shutdown first" msgstr "" -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirt.pm:145 +#: ../lib/Sys/VirtV2V/Connection/LibVirt.pm:145 #, perl-brace-format msgid "{name} isn't a valid guest name" msgstr "" -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm:90 +#: ../lib/Sys/VirtV2V/Connection/LibVirtXML.pm:90 #, perl-brace-format msgid "WARNING: unknown configuration directive {directive} in {name} section." msgstr "" -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm:143 +#: ../lib/Sys/VirtV2V/Connection/LibVirtXML.pm:143 #, perl-brace-format msgid "You must specify a filename when using {modulename}" msgstr "" -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm:167 +#: ../lib/Sys/VirtV2V/Connection/LibVirtXML.pm:167 #, perl-brace-format msgid "Failed to open {path}: {error}" msgstr "" -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm:180 +#: ../lib/Sys/VirtV2V/Connection/LibVirtXML.pm:180 #, perl-brace-format msgid "Unable to parse {path}: {error}" msgstr "" diff --git a/po/zh_CN.po b/po/zh_CN.po index 1200971..6af4d70 100644 --- a/po/zh_CN.po +++ b/po/zh_CN.po @@ -166,38 +166,38 @@ msgstr "???????????????????????????????????? {feature}" msgid "Unable to find a module to configure this guest" msgstr "??????????????????????????????????????????" -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirt.pm:85 -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm:113 +#: ../lib/Sys/VirtV2V/Connection/LibVirt.pm:85 +#: ../lib/Sys/VirtV2V/Connection/LibVirtXML.pm:113 #, perl-brace-format msgid "WARNING: {modulename} only takes a single filename." msgstr "?????????{modulename} ???????????????????????????" -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirt.pm:125 +#: ../lib/Sys/VirtV2V/Connection/LibVirt.pm:125 #, perl-brace-format msgid "Guest {name} must be shutdown first" msgstr "??????????????????????????? {name}" -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirt.pm:145 +#: ../lib/Sys/VirtV2V/Connection/LibVirt.pm:145 #, perl-brace-format msgid "{name} isn't a valid guest name" msgstr "{name} ????????????????????????" -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm:90 +#: ../lib/Sys/VirtV2V/Connection/LibVirtXML.pm:90 #, perl-brace-format msgid "WARNING: unknown configuration directive {directive} in {name} section." msgstr "?????????{name} ??????????????????????????? {directive}???" -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm:143 +#: ../lib/Sys/VirtV2V/Connection/LibVirtXML.pm:143 #, perl-brace-format msgid "You must specify a filename when using {modulename}" msgstr "?????? {modulename} ????????????????????????" -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm:167 +#: ../lib/Sys/VirtV2V/Connection/LibVirtXML.pm:167 #, perl-brace-format msgid "Failed to open {path}: {error}" msgstr "?????? {path} ?????????{error}" -#: ../lib/Sys/VirtV2V/MetadataReader/LibVirtXML.pm:180 +#: ../lib/Sys/VirtV2V/Connection/LibVirtXML.pm:180 #, perl-brace-format msgid "Unable to parse {path}: {error}" msgstr "???????????? {path}???{error}" diff --git a/snapshot/v2v-snapshot.pl b/snapshot/v2v-snapshot.pl index 86e4393..ccbc907 100755 --- a/snapshot/v2v-snapshot.pl +++ b/snapshot/v2v-snapshot.pl @@ -31,7 +31,7 @@ use Sys::Virt; use Sys::VirtV2V; use Sys::VirtV2V::ExecHelper; -use Sys::VirtV2V::MetadataReader; +use Sys::VirtV2V::Connection; use Sys::VirtV2V::UserMessage qw(user_message); =encoding utf8 @@ -239,8 +239,8 @@ if (!defined($datadir)) { } } -# Get an appropriate MetadataReader -my $mdr = Sys::VirtV2V::MetadataReader->instantiate($input, {}, $vmm, @ARGV); +# Get an appropriate Connection +my $mdr = Sys::VirtV2V::Connection->instantiate($input, {}, $vmm, @ARGV); if(!defined($mdr)) { print STDERR user_message(__x("{input} is not a valid input format", input => $input)); diff --git a/v2v/virt-v2v.pl b/v2v/virt-v2v.pl index ad1610f..82f16ce 100755 --- a/v2v/virt-v2v.pl +++ b/v2v/virt-v2v.pl @@ -33,7 +33,7 @@ use Sys::Guestfs::Lib qw(open_guest get_partitions inspect_all_partitions use Sys::VirtV2V; use Sys::VirtV2V::GuestOS; use Sys::VirtV2V::Converter; -use Sys::VirtV2V::MetadataReader; +use Sys::VirtV2V::Connection; use Sys::VirtV2V::UserMessage qw(user_message); =encoding utf8 @@ -208,8 +208,8 @@ if(defined($config_file)) { } } -# Get an appropriate MetadataReader -my $mdr = Sys::VirtV2V::MetadataReader->instantiate($input_method, $input_uri, +# Get an appropriate Connection +my $mdr = Sys::VirtV2V::Connection->instantiate($input_method, $input_uri, $config, @ARGV); if(!defined($mdr)) { print STDERR user_message __x("{input} is not a valid input method", @@ -217,7 +217,7 @@ if(!defined($mdr)) { exit(1); } -# Check MetadataReader is properly initialised +# Check Connection is properly initialised exit 1 unless($mdr->is_configured()); # Configure GuestOS ([files] and [deps] sections) -- 1.6.6
Matthew Booth
2010-Feb-01 17:23 UTC
[Libguestfs] [PATCH 3/8] ESX: Import guests from VMware's ESX server
This change adds the ability to import a guest and its storage from VMware's ESX server using the LibVirt connection. An example command line: virt-v2v -ic 'esx://yellow.marston/?no_verify=1' -op transfer RHEL5-64 This will import the guest RHEL5-64 from esx server yellow.marston, copying its storage to a local pool called transfer. Sys::VirtV2V::Connection is refactored to be a superclass. Subclasses are now created explicitly by virt-v2v.pl rather than using a generic instantiate mechanism. Sys::VirtV2V::Connection::LibVirt knows explicitly about ESX, and will use the new Sys::VirtV2V::Transfer::ESX to fetch its storage. virt-v2v.pl is updated to reflect the other changes. --- MANIFEST | 1 + lib/Sys/VirtV2V/Connection.pm | 160 +++++++++----- lib/Sys/VirtV2V/Connection/LibVirt.pm | 185 ++++++++++------- lib/Sys/VirtV2V/Connection/LibVirtXML.pm | 99 ++------- lib/Sys/VirtV2V/Transfer/ESX.pm | 344 ++++++++++++++++++++++++++++++ v2v/virt-v2v.pl | 140 ++++++++---- 6 files changed, 676 insertions(+), 253 deletions(-) create mode 100644 lib/Sys/VirtV2V/Transfer/ESX.pm diff --git a/MANIFEST b/MANIFEST index 7bfad76..3d6bf00 100644 --- a/MANIFEST +++ b/MANIFEST @@ -12,6 +12,7 @@ lib/Sys/VirtV2V/Connection.pm lib/Sys/VirtV2V/Connection/LibVirt.pm lib/Sys/VirtV2V/Connection/LibVirtXML.pm lib/Sys/VirtV2V/UserMessage.pm +lib/Sys/VirtV2V/Transfer/ESX.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 31fbdb3..6a967d0 100644 --- a/lib/Sys/VirtV2V/Connection.pm +++ b/lib/Sys/VirtV2V/Connection.pm @@ -20,113 +20,163 @@ package Sys::VirtV2V::Connection; use strict; use warnings; -use Module::Pluggable sub_name => 'modules', - search_path => ['Sys::VirtV2V::Connection'], - require => 1; +use Sys::Virt; -use Carp; +use Locale::TextDomain 'virt-v2v'; =pod =head1 NAME -Sys::VirtV2V::Connection - Read a variety of guest metadata formats +Sys::VirtV2V::Connection - Obtain domain metadata =head1 SYNOPSIS - use Sys::VirtV2V::Connection; + use Sys::VirtV2V::Connection::LibVirt; - $reader = Sys::VirtV2V::Connection->instantiate("libvirtxml", $uri, - $config, @args); - exit 1 unless($mdr->is_configured()); - $dom = $reader->get_dom(); + $conn = Sys::VirtV2V::Connection::LibVirt->new($uri, $name, $pool); + $dom = $conn->get_dom(); + @storage = $conn->get_local_storage(); =head1 DESCRIPTION -Sys::VirtV2V::Connection reads the metadata of a, possibly foreign, -guest. It provides the DOM representation of an equivalent libvirt XML -representation. +Sys::VirtV2V::Connection describes a connection to a, possibly remote, source of +guest metadata and storage. It is a virtual superclass and can't be instantiated +directly. Use one of the subclasses: -Sys::VirtV2V::Connection is an interface to various backends, each of -which implement a consistent API. Sys::VirtV2V::Connection itself only -implements methods to access backends. + Sys::VirtV2V::Connection::LibVirt + Sys::VirtV2V::Connection::LibVirtXML =head1 METHODS =over -=item instantiate(name, $uri, $config, @args) +=item get_local_storage -=over - -=item name +Return a list of the domain's storage devices. The returned list contains local +paths. -The name of the module to instantiate. +=cut -=item uri +sub get_local_storage +{ + my $self = shift; -A URI describing the target connection. + return @{$self->{storage}}; +} -=item config +=item get_dom() -A parsed virt-v2v configuration file. +Returns an XML::DOM::Document describing a libvirt configuration equivalent to +the input. -=item args +Returns undef and displays an error if there was an error -Backend-specific arguments describing where its data is located. +=cut -=back +sub get_dom +{ + my $self = shift; -Instantiate a backend instance with the given name. + return $self->{dom}; +} -=cut -sub instantiate +# Iterate over returned storage. Transfer it and update DOM as necessary. To be +# called by subclasses. +sub _storage_iterate { - my $class = shift; + my $self = shift; + + my ($transfer, $pool) = @_; + + my $dom = $self->get_dom(); - my ($name, $uri, $config, @args) = @_; + # Create a hash of guest devices to their paths + my @storage; + foreach my $disk ($dom->findnodes('/domain/devices/disk')) { + my ($source_e) = $disk->findnodes('source'); - defined($name) or carp("instantiate called without name argument"); - defined($uri) or carp("instantiate called without uri argument"); - defined($config) or carp("instantiate called without config argument"); + my ($source) = $source_e->findnodes('@file | @dev'); + defined($source) or die("source element has neither dev nor file: \n". + $dom.toString()); - foreach my $module ($class->modules()) { - if($module->get_name() eq $name) { - return $module->_new($uri, $config->{$name}, @args); + my ($target) = $disk->findnodes('target/@dev'); + defined($target) or die("disk does not have a target device: \n". + $dom.toString()); + + # If the disk is a floppy or a cdrom, blank its source + my $device = $disk->getAttribute('device'); + if ($device eq 'floppy' || $device eq 'cdrom') { + $source_e->setAttribute($source->getName(), ''); } - } - return undef; -} + else { + my $path = $source->getValue(); -=back + if (defined($transfer)) { + # Die if transfer required and no output pool + die (user_message(__"No output pool was specified")) + unless (defined($pool)); -=head1 BACKEND INTERFACE + # Fetch the remote storage + my $vol = $transfer->transfer($self, $path, $pool); -=over + # Parse the XML description of the returned volume + my $voldom + new XML::DOM::Parser->parse($vol->get_xml_description()); -=item CLASS->get_name() + # Find any existing driver element. + my ($driver) = $disk->findnodes('driver'); -Return the module's name. + # Create a new driver element if none exists + unless (defined($driver)) { + $driver + $disk->getOwnerDocument()->createElement("driver"); + $disk->appendChild($driver); + } + $driver->setAttribute('name', 'qemu'); -=item is_configured() + # Get the volume format for passing to the qemu driver + my ($format) + $voldom->findnodes('/volume/target/format/@type'); -Return 1 if the module has been suffiently configured to proceed. -Return 0 and display an error message otherwise. + $format = $format->getValue() if (defined($format)); -=item get_dom() + # Auto-detect if no format is specified explicitly + $format ||= 'auto'; -Returns an XML::DOM::Document describing a libvirt configuration equivalent to -the input. + $driver->setAttribute('type', $format); -Returns undef and displays an error if there was an error + # Remove the @file or @dev attribute before adding a new one + $source_e->removeAttributeNode($source); + + $path = $vol->get_path(); + + # Set @file or @dev as appropriate + if ($vol->get_info()->{type} =+ Sys::Virt::StorageVol::TYPE_FILE) + { + $disk->setAttribute('type', 'file'); + $source_e->setAttribute('file', $path); + } else { + $disk->setAttribute('type', 'block'); + $source_e->setAttribute('dev', $path); + } + } + + push(@storage, $path); + } + } + + $self->{storage} = \@storage; +} =back =head1 COPYRIGHT -Copyright (C) 2009 Red Hat Inc. +Copyright (C) 2009,2010 Red Hat Inc. =head1 LICENSE diff --git a/lib/Sys/VirtV2V/Connection/LibVirt.pm b/lib/Sys/VirtV2V/Connection/LibVirt.pm index d3aa80d..59e4913 100644 --- a/lib/Sys/VirtV2V/Connection/LibVirt.pm +++ b/lib/Sys/VirtV2V/Connection/LibVirt.pm @@ -20,10 +20,17 @@ package Sys::VirtV2V::Connection::LibVirt; use strict; use warnings; +use Sys::VirtV2V::Connection; +our @ISA = ("Sys::VirtV2V::Connection"); + +use Net::Netrc; +use URI; use XML::DOM; use Sys::Virt; +use Sys::VirtV2V; +use Sys::VirtV2V::Transfer::ESX; use Sys::VirtV2V::UserMessage qw(user_message); use Locale::TextDomain 'virt-v2v'; @@ -36,107 +43,133 @@ Sys::VirtV2V::Connection::LibVirt - Read libvirt metadata from libvirtd =head1 SYNOPSIS - use Sys::VirtV2V::Connection; + use Sys::VirtV2V::Connection::LibVirt; - $reader = Sys::VirtV2V::Connection->instantiate - ("libvirt", "xen+ssh://xenserver.example.com/", $config, @args); - $dom = $reader->get_dom(); + $conn = Sys::VirtV2V::Connection::LibVirt->new + ("xen+ssh://xenserver.example.com/", $name, $pool); + $dom = $conn->get_dom(); =head1 DESCRIPTION -Sys::VirtV2V::Connection::LibVirt is a backend for +Sys::VirtV2V::Connection::LibVirt is an implementation of Sys::VirtV2V::Connection which reads a guest's libvirt XML directly from a libvirt connection. =head1 METHODS -See BACKEND INTERFACE in L<Sys::VirtV2V::Connection> for a detailed -description of its exported methods. - =over -=cut +=item new(uri, name, pool) -use constant NAME => "libvirt"; +Create a new Sys::VirtV2V::Connection::LibVirt. Domain I<name> will be +downloaded from I<uri>. Remote storage will be copied to a new volume, which +will be create in <pool>. + +=cut -sub _new +sub new { my $class = shift; - my ($uri, $config, @args) = @_; + my ($uri, $name, $pool) = @_; my $self = {}; bless($self, $class); - my @vmm_params = (auth => 1); - push(@vmm_params, url => $uri) if defined($uri); - my $vmm = Sys::Virt->new(@vmm_params); + $self->{uri} = URI->new($uri); + $self->{name} = $name; - $self->{vmm} = $vmm; - $self->_handle_args(@args); + # Parse uri authority for hostname and username + $self->{uri}->authority() =~ /^(?:([^:]*)(?::([^@]*))?@)?(.*)$/ + or die(user_message(__x("Unable to parse URI authority: {auth}", + auth => $self->{uri}->authority()))); - return $self; -} + my $username = $self->{username} = $1; + my $hostname = $self->{hostname} = $3; -sub _handle_args -{ - my $self = shift; + print STDERR user_message(__("WARNING: Specifying a password in the ". + "connection URI is not supported. It has ". + "been ignored.")) if (defined($2)); - # The first argument is the name of a libvirt domain - $self->{name} = shift; + # Look for credentials in .netrc if the URI contains a hostname + if (defined($hostname)) { + if (defined($username)) { + my $mach = Net::Netrc->lookup($hostname, $username); + $self->{password} = $mach->password if (defined($mach)); + } - # Warn if we were given more than 1 argument - if(scalar(@_) > 0) { - print STDERR user_message - (__x("WARNING: {modulename} only takes a single domain name.", - modulename => NAME)); - } -} + else { + my $mach = Net::Netrc->lookup($hostname); -=item Sys::VirtV2V::Connection::LibVirtXML->get_name() + if (defined($mach)) { + $self->{username} = $mach->login; + $self->{password} = $mach->password; + } + } + } -See BACKEND INTERFACE in L<Sys::VirtV2V::Connection> for details. + my $sourcevmm; + eval { + $sourcevmm = Sys::Virt->new( + uri => $uri, + readonly => 1, + auth => 1, + credlist => [ + Sys::Virt::CRED_AUTHNAME, + Sys::Virt::CRED_PASSPHRASE + ], + callback => sub { + my $creds = shift; + + foreach my $cred (@$creds) { + if ($cred->{type} == Sys::Virt::CRED_AUTHNAME) { + $cred->{result} = $self->{username}; + } + + elsif ($cred->{type} == Sys::Virt::CRED_PASSPHRASE) { + $cred->{result} = $self->{password}; + } + + else { die($cred->{type}, "\n"); } + } + } + ); + }; + die(user_message(__x("Failed to connect to {uri}: {error}", + uri => $uri, + error => $@->stringify()))) if ($@); -=cut + $self->{sourcevmm} = $sourcevmm; -sub get_name -{ - my $class = shift; + $self->_check_shutdown(); - return NAME; -} + $self->_get_dom(); -=item is_configured() + my $transfer; + if ($self->{uri}->scheme eq "esx") { + $transfer = "Sys::VirtV2V::Transfer::ESX"; + } -See BACKEND INTERFACE in L<Sys::VirtV2V::Connection> for details. + $self->_storage_iterate($transfer, $pool); -=cut + return $self; +} -sub is_configured +sub _check_shutdown { my $self = shift; my $vmm = $self->{vmm}; - my $name = $self->{name}; - - # Check the given domain exists - my $domain = _get_domain($vmm, $name); - - # Don't continue if it isn't - return 0 unless(defined($domain)); + my $domain = $self->_get_domain(); # Check the domain is shutdown - unless ($domain->get_info()->{state} == Sys::Virt::Domain::STATE_SHUTOFF) { - print STDERR user_message - (__x("Guest {name} is currently {state}. ". - "It must be shut down first.", - state => _state_string($domain->get_info()->{state}), - name => $name)); - return 0; - } - - return 1; + die(user_message(__x("Guest {name} is currently {state}. It must be ". + "shut down first.", + state => _state_string($domain->get_info()->{state}), + name => $self->{name}))) + unless ($domain->get_info()->{state} =+ Sys::Virt::Domain::STATE_SHUTOFF); } sub _state_string @@ -164,30 +197,30 @@ sub _state_string sub _get_domain { - my ($vmm, $name) = @_; + my $self = shift; + + return $self->{domain} if (defined($self->{domain})); + + my $vmm = $self->{sourcevmm}; + my $name = $self->{name}; # Lookup the domain my $domain; eval { $domain = $vmm->get_domain_by_name($name); }; + die($@) if ($@); - # Warn and exit if we didn't find it - unless($domain) { - print STDERR user_message - (__x("{name} isn't a valid guest name", name => $name)); - } + # Check we found it + die(user_message(__x("{name} isn't a valid guest name", name => $name))) + unless($domain); + + $self->{domain} = $domain; return $domain; } -=item get_dom() - -See BACKEND INTERFACE in L<Sys::VirtV2V::Connection> for details. - -=cut - -sub get_dom +sub _get_dom { my $self = shift; @@ -195,13 +228,15 @@ sub get_dom my $name = $self->{name}; # Lookup the domain - my $domain = _get_domain($vmm, $name); + my $domain = $self->_get_domain(); # Warn and exit if we didn't find it return undef unless(defined($domain)); my $xml = $domain->get_xml_description(); - return new XML::DOM::Parser->parse($xml); + + my $dom = new XML::DOM::Parser->parse($xml); + $self->{dom} = $dom; } =back diff --git a/lib/Sys/VirtV2V/Connection/LibVirtXML.pm b/lib/Sys/VirtV2V/Connection/LibVirtXML.pm index c05aa0f..6867a9b 100644 --- a/lib/Sys/VirtV2V/Connection/LibVirtXML.pm +++ b/lib/Sys/VirtV2V/Connection/LibVirtXML.pm @@ -20,6 +20,8 @@ package Sys::VirtV2V::Connection::LibVirtXML; use strict; use warnings; +our @ISA = ("Sys::VirtV2V::Connection"); + use XML::DOM; use XML::DOM::XPath; @@ -35,34 +37,33 @@ Sys::VirtV2V::Connection::LibVirtXML - Read libvirt XML from a file =head1 SYNOPSIS - use Sys::VirtV2V::Connection; + use Sys::VirtV2V::Connection::LibVirtXML; - $reader = Sys::VirtV2V::Connection->instantiate("libvirtxml", undef, - $config, @args); - $dom = $reader->get_dom(); + $conn = Sys::VirtV2V::Connection::LibVirtXML->new($config, $path); + $dom = $conn->get_dom(); =head1 DESCRIPTION -Sys::VirtV2V::Connection::LibVirtXML is a backend for +Sys::VirtV2V::Connection::LibVirtXML is an implementation of Sys::VirtV2V::Connection which reads libvirt XML guest descriptions from a file. =head1 METHODS -See BACKEND INTERFACE in L<Sys::VirtV2V::Connection> for a detailed -description of its exported methods. - =over -=cut +=item new(config, path) -use constant NAME => "libvirtxml"; +Create a new LibVirtXML connection. Configuration for transforming the metadata +is taken from I<config>, and the metadata itself is read from I<path>. + +=cut -sub _new +sub new { my $class = shift; - my ($uri, $config, @args) = @_; + my ($config, $path) = @_; my %obj = (); my $self = \%obj; @@ -87,77 +88,22 @@ sub _new } else { - print STDERR user_message - (__x("WARNING: unknown configuration directive ". - "{directive} in {name} section.", - directive => $directive, name => NAME)); - $self->{invalidconfig} = 1; + die(__x("WARNING: unknown configuration directive ". + "{directive} in {name} section.", + directive => $directive, name => 'libvirtxml')); } } } - $self->_handle_args(@args); - - return $self; -} - -sub _handle_args -{ - my $self = shift; - - # The first argument is the libvirt xml file's path - $self->{path} = shift; - - # Warn if we were given more than 1 argument - if(scalar(@_) > 0) { - print STDERR user_message - (__x("WARNING: {modulename} only takes a single filename.", - modulename => NAME)); - } -} - -=item Sys::VirtV2V::Connection::LibVirtXML->get_name() - -See BACKEND INTERFACE in L<Sys::VirtV2V::Connection> for details. - -=cut - -sub get_name -{ - my $class = shift; - - return NAME; -} - -=item is_configured() - -See BACKEND INTERFACE in L<Sys::VirtV2V::Connection> for details. - -=cut - -sub is_configured -{ - my $self = shift; - - if(!defined($self->{path})) { - print STDERR user_message - (__x("You must specify a filename when using {modulename}", - modulename => NAME)); - return 0; - } + $self->_get_dom($path); - return 0 if(exists($self->{invalidconfig})); + # No transfer methods defined yet + $self->_storage_iterate(undef, undef); - return 1; + return $self; } -=item get_dom() - -See BACKEND INTERFACE in L<Sys::VirtV2V::Connection> for details. - -=cut - -sub get_dom +sub _get_dom { my $self = shift; @@ -212,7 +158,7 @@ sub get_dom =head1 COPYRIGHT -Copyright (C) 2009 Red Hat Inc. +Copyright (C) 2009,2010 Red Hat Inc. =head1 LICENSE @@ -220,7 +166,6 @@ Please see the file COPYING.LIB for the full license. =head1 SEE ALSO -L<Sys::VirtV2V::Connection(3)>, L<virt-v2v(1)>, L<v2v-snapshot(1)>, L<http://libguestfs.org/>. diff --git a/lib/Sys/VirtV2V/Transfer/ESX.pm b/lib/Sys/VirtV2V/Transfer/ESX.pm new file mode 100644 index 0000000..353c10d --- /dev/null +++ b/lib/Sys/VirtV2V/Transfer/ESX.pm @@ -0,0 +1,344 @@ +# Sys::VirtV2V::Transfer::ESX +# 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::ESX::UA; + +use strict; +use warnings; + +use Sys::Virt::Error; + +use Sys::VirtV2V; + +use Sys::VirtV2V::UserMessage qw(user_message); + +use Locale::TextDomain 'virt-v2v'; + +# This is a gross hack to bring sanity to Net::HTTPS's SSL handling. Net::HTTPS +# can use either Net::SSL or IO::Socket::SSL, depending on which is available at +# runtime. It does not expose which of these it is using, or provide any common +# interface for configuring them. Neither of these libraries will verify a peer +# certificate by default. The configuration required to ensure certificates are +# verified is custom to the driver in use. If the wrong driver is configured, it +# will silently do nothing. +# +# To try to fix this situation, we hardcode here that we want Net::SSL. In the +# _new constructor, we check that Net::SSL was actually used, and die() if it +# wasn't. We subsequently only include configuration for Net::SSL. +BEGIN { + use Net::HTTPS; + + $Net::HTTPS::SSL_SOCKET_CLASS = "Net::SSL"; +} + +use LWP::UserAgent; +our @ISA = ("LWP::UserAgent"); + +our %handles; + +sub new { + my $class = shift; + + my ($server, $username, $password, $pool, $verify) = @_; + + my $self = $class->SUPER::new( + agent => 'virt-v2v/'.$Sys::VirtV2V::VERSION, + protocols_allowed => [ 'https' ] + ); + $self->show_progress(1); + + $self->add_handler(response_header => sub { + my ($response, $self, $h) = @_; + + if ($response->is_success) { + $self->verify_certificate($response) if ($verify); + $self->create_volume($response); + } + }); + + $self->{_v2v_server} = $server; + $self->{_v2v_pool} = $pool; + $self->{_v2v_username} = $username; + $self->{_v2v_password} = $password; + + if ($verify) { + # Leave HTTPS_CA_DIR alone if it is already set + # Setting HTTPS_CA_DIR to the empty string results in it using the + # compiled-in default paths + $ENV{HTTPS_CA_DIR} = "" unless (exists($ENV{HTTPS_CA_DIR})); + } else { + # Unset HTTPS_CA_DIR if it is already set + delete($ENV{HTTPS_CA_DIR}); + } + + die("Invalid configuration of Net::HTTPS") + unless(Net::HTTPS->isa('Net::SSL')); + + return $self; +} + +sub get_volume +{ + my $self = shift; + + my ($path) = @_; + + # Need to turn this: + # [yellow:storage1] win2k3r2-32/win2k3r2-32.vmdk + # into this: + # https://yellow.rhev.marston/folder/win2k3r2-32/win2k3r2-32-flat.vmdk? \ + # dcPath=ha-datacenter&dsName=yellow:storage1 + + $path =~ /^\[(.*)\]\s+(.*)\.vmdk$/ + or die("Failed to parse ESX path: $path"); + my $datastore = $1; + my $vmdk = $2; + + my $url = URI->new("https://".$self->{_v2v_server}); + $url->path("/folder/$vmdk-flat.vmdk"); + $url->query_form(dcPath => "ha-datacenter", dsName => $datastore); + + # Replace / with _ so the vmdk name can be used as a volume name + $self->{_v2v_volname} = $vmdk; + $self->{_v2v_volname} =~ s,/,_,g; + + # Check to see if this volume already exists + eval { + my $pool = $self->{_v2v_pool}; + $self->{_v2v_vol} = $pool->get_volume_by_name($self->{_v2v_volname}); + }; + + # The above command should generate VIR_ERR_NO_STORAGE_VOL because the + # volume doesn't exist + unless($@ && $@->code == Sys::Virt::Error::ERR_NO_STORAGE_VOL) { + unless ($@) { + print STDERR user_message(__x("WARNING: storage volume {name} ". + "already exists in the target ". + "pool. NOT fetching it again. ". + "Delete the volume and retry to ". + "download again.", + name => $self->{_v2v_volname})); + return $self->{_v2v_vol}; + } + + # We got an error, but not the one we expected + die(user_message(__x("Unexpected error accessing storage pool: ", + "{error}", error => $@->stringify()))); + } + + my $r = $self->SUPER::get($url, + ':content_cb' => sub { $self->handle_data(@_); }, + ':read_size_hint' => 64 * 1024); + + if ($r->is_success) { + # It reports success even if one of the callbacks died + my $died = $r->header('X-Died'); + die($died) if (defined($died)); + + # Close the volume file descriptor + close($self->{_v2v_volfh}); + return $self->{_v2v_vol}; + } + + if ($r->code == 401) { + die(user_message(__x("Authentication error connecting to ". + "{server}. Used credentials for {username} ". + "from .netrc.", + server => $self->{_v2v_server}, + username => $self->{_v2v_username}))) + } + + die(user_message(__x("Failed to connect to ESX server: {error}", + error => $r->status_line))); +} + +sub get_basic_credentials +{ + my $self = shift; + + my ($realm, $uri, $isproxy) = @_; # Not interested in any of these things + # because we only ever contact a single + # server in a single context + + return ($self->{_v2v_username}, $self->{_v2v_password}); +} + +sub handle_data +{ + my $self = shift; + + my ($data, $response) = @_; + + my $volfh = $self->{_v2v_volfh}; + + syswrite($volfh, $data) + or die(user_message(__x("Error writing to {path}: {error}", + path => $self->{_v2v_volpath}, + error => $!))); +} + +sub create_volume +{ + my $self = shift; + + my ($response) = @_; + + my $pool = $self->{_v2v_pool}; + + # Create a volume in the target storage pool of the correct size + my $name = $self->{_v2v_volname}; + die("create_volume called, but _v2v_volname is not set") + unless (defined($name)); + + my $size = $response->content_length(); + + my $vol_xml = " + <volume> + <name>$name</name> + <capacity>$size</capacity> + </volume> + "; + + my $volume; + eval { + $volume = $pool->create_volume($vol_xml); + }; + die(user_message(__x("Failed to create storage volume: {error}", + error => $@->stringify()))) if ($@); + $self->{_v2v_vol} = $volume; + + # Open the volume for writing + open(my $volfh, '>', $volume->get_path()) + or die(user_message(__x("Error opening storage volume {path} ". + "for writing: {error}", error => $!))); + + $self->{_v2v_volfh} = $volfh; +} + +sub verify_certificate +{ + my $self = shift; + + my ($r) = @_; + + # No point in trying to verify headers if the request failed anyway + return unless ($r->is_success); + + my $subject = $r->header('Client-SSL-Cert-Subject'); + die(user_message(__"Server response didn't include an SSL subject")) + unless ($subject); + + $subject =~ /\/CN=([^\/]*)/ + or die(user_message(__x("SSL Certification Subject doesn't contain a ". + "common name: {subject}", + subject => $subject))); + my $cn = $1; + + $self->{_v2v_server} =~ /(^|\.)\Q$cn\E$/ + or die(user_message(__x("Server {server} presented an SSL certificate ". + "for {commonname}", + server => $self->{_v2v_server}, + commonname => $cn))); +} + +package Sys::VirtV2V::Transfer::ESX; + +use Sys::Virt; + +use Sys::VirtV2V::UserMessage qw(user_message); + +use Locale::TextDomain 'virt-v2v'; + +=pod + +=head1 NAME + +Sys::VirtV2V::Transfer::ESX - Transfer guest storage from an ESX server + +=head1 SYNOPSIS + + use Sys::VirtV2V::Transfer::ESX; + + $vol = Sys::VirtV2V::Transfer::ESX->transfer($conn, $path, $pool); + +=head1 DESCRIPTION + +Sys::VirtV2V::Transfer::ESX retrieves guest storage devices from an ESX server. + +=head1 METHODS + +=over + +=item transfer(conn, path, pool) + +Transfer <path> from a remote ESX server. Server and authentication details will +be taken from <conn>. Storage will be copied to a new volume created in <pool>. + +=cut + +sub transfer +{ + my $class = shift; + + my ($conn, $path, $pool) = @_; + + my $uri = $conn->{uri}; + my $username = $conn->{username}; + my $password = $conn->{password}; + + die("URI not defined for connection") unless (defined($uri)); + + die(user_message(__x("Authentication is required to connect to ". + "{server} and no credentials were found in ". + ".netrc.", + server => $conn->{hostname}))) + unless (defined($username)); + + # Look for no_verify in the URI + my %query = $uri->query_form; + + my $noverify = 0; + $noverify = 1 if (exists($query{no_verify}) && $query{no_verify} eq "1"); + + # Initialise a user agent + my $ua = Sys::VirtV2V::Transfer::ESX::UA->new($conn->{hostname}, + $username, + $password, + $pool); + + return $ua->get_volume($path); +} + +=back + +=head1 COPYRIGHT + +Copyright (C) 2009, 2010 Red Hat Inc. + +=head1 LICENSE + +Please see the file COPYING.LIB for the full license. + +=head1 SEE ALSO + +L<virt-v2v(1)>, +L<v2v-snapshot(1)>, +L<http://libguestfs.org/>. + +=cut + +1; diff --git a/v2v/virt-v2v.pl b/v2v/virt-v2v.pl index 82f16ce..b097ccd 100755 --- a/v2v/virt-v2v.pl +++ b/v2v/virt-v2v.pl @@ -33,7 +33,8 @@ use Sys::Guestfs::Lib qw(open_guest get_partitions inspect_all_partitions use Sys::VirtV2V; use Sys::VirtV2V::GuestOS; use Sys::VirtV2V::Converter; -use Sys::VirtV2V::Connection; +use Sys::VirtV2V::Connection::LibVirt; +use Sys::VirtV2V::Connection::LibVirtXML; use Sys::VirtV2V::UserMessage qw(user_message); =encoding utf8 @@ -133,12 +134,20 @@ Guest argument is the path to an XML file describing a libvirt domain. =cut -my $input_uri; +my $input_uri = "qemu:///system"; =item B<-ic URI> Specifies the connection to use when using the libvirt input method. If omitted, -then we connect to the default libvirt hypervisor. +this defaults to qemu:///system. + +=cut + +my $input_transport; + +=item B<-it method> + +Species the transport method used to obtain raw storage from the source guest. =cut @@ -151,9 +160,18 @@ ommitted, this defaults to qemu:///system. =cut +my $output_pool; + +=item B<-op pool> + +Specifies the pool which will be used to create new storage for the converted +guest. + +=cut + my $config_file; -=item B<-f file>, B<--config file> +=item B<-f file> Load the virt-v2v configuration from I<file>. There is no default. @@ -189,6 +207,7 @@ GetOptions ("help|?" => sub { "i=s" => \$input_method, "ic=s" => \$input_uri, "oc=s" => \$output_uri, + "op=s" => \$output_pool, "f|config=s" => \$config_file ) or pod2usage(2); @@ -208,33 +227,89 @@ if(defined($config_file)) { } } +# Connect to target libvirt +my $vmm = Sys::Virt->new( + auth => 1, + uri => $output_uri +); + # Get an appropriate Connection -my $mdr = Sys::VirtV2V::Connection->instantiate($input_method, $input_uri, - $config, @ARGV); -if(!defined($mdr)) { - print STDERR user_message __x("{input} is not a valid input method", - input => $input_method); +my $conn; +eval { + if ($input_method eq "libvirtxml") { + my $path = shift(@ARGV) or + pod2usage({ -message => user_message(__"You must specify a filename"), + -exitval => 1 }); + + # Warn if we were given more than 1 argument + if(scalar(@_) > 0) { + print STDERR user_message + (__x("WARNING: {modulename} only takes a single filename.", + modulename => 'libvirtxml')); + } + + $conn = Sys::VirtV2V::Connection::LibVirtXML->new($config, $path); + } + + elsif ($input_method eq "libvirt") { + my $name = shift(@ARGV) or + pod2usage({ -message => user_message(__"You must specify a guest"), + -exitval => 1 }); + + # Get a handle to the output pool if one is defined + my $pool; + if (defined($output_pool)) { + eval { + $pool = $vmm->get_storage_pool_by_name($output_pool); + }; + + if ($@) { + print STDERR user_message + (__x("Output pool {poolname} is not a valid local ". + "storage pool", + poolname => $output_pool)); + exit(1); + } + } + + $conn = Sys::VirtV2V::Connection::LibVirt->new($input_uri, $name, + $pool); + + # Warn if we were given more than 1 argument + if(scalar(@_) > 0) { + print STDERR user_message + (__x("WARNING: {modulename} only takes a single domain name.", + modulename => 'libvirt')); + } + } + + else { + print STDERR user_message __x("{input} is not a valid input method", + input => $input_method); + exit(1); + } +}; +if ($@) { + print STDERR $@; exit(1); } -# Check Connection is properly initialised -exit 1 unless($mdr->is_configured()); - # Configure GuestOS ([files] and [deps] sections) Sys::VirtV2V::GuestOS->configure($config); + ############################################################################### ## Start of processing # Get a libvirt configuration for the guest -my $dom = $mdr->get_dom(); +my $dom = $conn->get_dom(); exit(1) unless(defined($dom)); -# Get a list of the guest's storage devices -my @devices = get_guest_devices($dom); +# Get a list of the guest's transfered storage devices +my @storage = $conn->get_local_storage(); -# Open a libguestfs handle on the guest's devices -my $g = get_guestfs_handle(@devices); +# Open a libguestfs handle on the guest's storage devices +my $g = get_guestfs_handle(@storage); # Inspect the guest my $os = inspect_guest($g); @@ -242,11 +317,6 @@ my $os = inspect_guest($g); # Instantiate a GuestOS instance to manipulate the guest my $guestos = Sys::VirtV2V::GuestOS->instantiate($g, $os); -# Connect to target libvirt -my @vmm_params = (auth => 1); -push(@vmm_params, uri => $output_uri) if(defined($output_uri)); -my $vmm = Sys::Virt->new(@vmm_params); - # Modify the guest and its metadata for the target hypervisor Sys::VirtV2V::Converter->convert($vmm, $guestos, $dom, $os); @@ -255,15 +325,14 @@ $g->sync(); $vmm->define_domain($dom->toString()); +exit(0); ############################################################################### ## Helper functions sub get_guestfs_handle { - my @params = \@_; # Initialise parameters with list of devices - - my $g = open_guest(@params, rw => 1); + my $g = open_guest(\@_, rw => 1); # Mount the transfer iso if GuestOS needs it my $transferiso = Sys::VirtV2V::GuestOS->get_transfer_iso(); @@ -328,27 +397,6 @@ sub inspect_guest return $os; } -sub get_guest_devices -{ - my $dom = shift; - - my @devices; - foreach my $source ($dom->findnodes('/domain/devices/disk/source')) { - my $attrs = $source->getAttributes(); - - # Get either dev or file, whichever is defined - my $attr = $attrs->getNamedItem("dev"); - $attr = $attrs->getNamedItem("file") if(!defined($attr)); - - defined($attr) or die("source element has neither dev nor file: ". - $source.toString()); - - push(@devices, $attr->getValue()); - } - - return @devices; -} - =head1 PREPARING TO RUN VIRT-V2V =head2 Backup the guest -- 1.6.6
Matthew Booth
2010-Feb-01 17:23 UTC
[Libguestfs] [PATCH 4/8] Converter: Configure default devices
Add a set of default devices if they are not present. These are currently defined to be a ps2 mouse, tablet input and cirrus graphics. --- lib/Sys/VirtV2V/Converter.pm | 34 +++++++++++++++++++++++++++++++++- 1 files changed, 33 insertions(+), 1 deletions(-) diff --git a/lib/Sys/VirtV2V/Converter.pm b/lib/Sys/VirtV2V/Converter.pm index 64a5a46..1de17c8 100644 --- a/lib/Sys/VirtV2V/Converter.pm +++ b/lib/Sys/VirtV2V/Converter.pm @@ -67,8 +67,12 @@ use constant KVM_XML_VIRTIO => " <interface type='network'> <model type='virtio'/> </interface> + <input type='tablet' bus='usb'/> <input type='mouse' bus='ps2'/> <graphics type='vnc' port='-1' listen='127.0.0.1'/> + <video> + <model type='cirrus' vram='9216' heads='1'/> + </video> </devices> </domain> "; @@ -86,8 +90,12 @@ use constant KVM_XML_NOVIRTIO => " <interface type='network'> <model type='e1000'/> </interface> + <input type='tablet' bus='usb'/> <input type='mouse' bus='ps2'/> <graphics type='vnc' port='-1' listen='127.0.0.1'/> + <video> + <model type='cirrus' vram='9216' heads='1'/> + </video> </devices> </domain> "; @@ -179,9 +187,12 @@ sub _configure_metadata # Remove any configuration related to a PV kernel bootloader _unconfigure_bootloaders($dom); - # Configure network and block drivers in the guest + # Configure network and block drivers _configure_drivers($dom, $virtio); + # Ensure guest has a standard set of default devices + _configure_default_devices($dom, $default_dom); + # Add a default os section if none exists _configure_os($dom, $default_dom, $arch); } @@ -229,6 +240,27 @@ sub _configure_os $type->setAttribute('arch', $arch) unless(defined($arch_attr)); } +sub _configure_default_devices +{ + my ($dom, $default_dom) = @_; + + my ($devices) = $dom->findnodes('/domain/devices'); + + # Remove any existing input, graphics or video devices + foreach my $input ($devices->findnodes('input | video | graphics')) { + $devices->removeChild($input); + } + + my ($input_devices) = $default_dom->findnodes('/domain/devices'); + + # Add new default devices from default XML + foreach my $input ($input_devices->findnodes('input | video | graphics')) { + my $new = $input->cloneNode(1); + $new->setOwnerDocument($devices->getOwnerDocument()); + $devices->appendChild($new); + } +} + sub _configure_capabilities { my ($dom, $vmm, $guestcaps) = @_; -- 1.6.6
Matthew Booth
2010-Feb-01 17:23 UTC
[Libguestfs] [PATCH 5/8] Converter: Remove VMware tools if it is installed
--- lib/Sys/VirtV2V/Converter/Linux.pm | 21 ++++++++++++++++----- 1 files changed, 16 insertions(+), 5 deletions(-) diff --git a/lib/Sys/VirtV2V/Converter/Linux.pm b/lib/Sys/VirtV2V/Converter/Linux.pm index 057ef45..4458fc3 100644 --- a/lib/Sys/VirtV2V/Converter/Linux.pm +++ b/lib/Sys/VirtV2V/Converter/Linux.pm @@ -386,6 +386,7 @@ sub _unconfigure_hv my ($guestos, $desc) = @_; _unconfigure_xen($guestos, $desc); + _unconfigure_vmware($guestos, $desc); } # Unconfigure Xen specific guest modifications @@ -393,11 +394,6 @@ sub _unconfigure_xen { my ($guestos, $desc) = @_; - carp("unconfigure called without guestos argument") - unless defined($guestos); - carp("unconfigure called without desc argument") - unless defined($desc); - my $found_kmod = 0; # Look for kmod-xenpv-*, which can be found on RHEL 3 machines @@ -462,6 +458,21 @@ sub _unconfigure_xen } } +# Unconfigure VMware specific guest modifications +sub _unconfigure_vmware +{ + my ($guestos, $desc) = @_; + + # Uninstall VMwareTools + foreach my $app (@{$desc->{apps}}) { + my $name = $app->{name}; + + if ($name eq "VMwareTools") { + $guestos->remove_application($name); + } + } +} + # Get a list of all foreign hypervisor specific kernel modules which are being # used by the guest sub _find_hv_kernel_modules -- 1.6.6
Rename _configure_metadata to _convert_metadata --- lib/Sys/VirtV2V/Converter.pm | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Sys/VirtV2V/Converter.pm b/lib/Sys/VirtV2V/Converter.pm index 1de17c8..4115c1e 100644 --- a/lib/Sys/VirtV2V/Converter.pm +++ b/lib/Sys/VirtV2V/Converter.pm @@ -150,7 +150,7 @@ sub convert unless (defined($guestcaps)); # Convert the metadata - _configure_metadata($vmm, $dom, $desc, $guestcaps); + _convert_metadata($vmm, $dom, $desc, $guestcaps); my ($name) = $dom->findnodes('/domain/name/text()'); $name = $name->getNodeValue(); @@ -164,7 +164,7 @@ sub convert } } -sub _configure_metadata +sub _convert_metadata { my ($vmm, $dom, $desc, $guestcaps) = @_; -- 1.6.6
Matthew Booth
2010-Feb-01 17:23 UTC
[Libguestfs] [PATCH 7/8] Converter: add default <features> if none are present
Add ACPI if the guest and hypervisor support it. Add APIC and PAE if the hypervisor supports it. --- lib/Sys/VirtV2V/Converter.pm | 66 ++++++++++++++++++++++++++++++----------- 1 files changed, 48 insertions(+), 18 deletions(-) diff --git a/lib/Sys/VirtV2V/Converter.pm b/lib/Sys/VirtV2V/Converter.pm index 4115c1e..a53dd92 100644 --- a/lib/Sys/VirtV2V/Converter.pm +++ b/lib/Sys/VirtV2V/Converter.pm @@ -314,28 +314,58 @@ sub _configure_capabilities } } - # Check that /domain/features are listed in capabilities - # Get a list of supported features - my %features; - foreach my $feature ($guestcap->findnodes('features/*')) { - $features{$feature->getNodeName()} = 1; - } + # Get the domain features node + my ($domfeatures) = $dom->findnodes('/domain/features'); + + # Check existing features are supported by the hypervisor + if (defined($domfeatures)) { + # Check that /domain/features are listed in capabilities + # Get a list of supported features + my %features; + foreach my $feature ($guestcap->findnodes('features/*')) { + $features{$feature->getNodeName()} = 1; + } - foreach my $feature ($dom->findnodes('/domain/features/*')) { - my $name = $feature->getNodeName(); + foreach my $feature ($domfeatures->findnodes('*')) { + my $name = $feature->getNodeName(); - if (!exists($features{$name})) { - print STDERR user_message - (__x("The connected hypervisor does not support ". - "feature {feature}", feature => $name)); - $feature->getParentNode()->removeChild($feature); + if (!exists($features{$name})) { + print STDERR user_message + (__x("The connected hypervisor does not support ". + "feature {feature}", feature => $name)); + $feature->getParentNode()->removeChild($feature); + } + + if ($name eq 'acpi' && !$guestcaps->{acpi}) { + print STDERR user_message + (__"The target guest does not support acpi under KVM. ACPI ". + "will be disabled."); + $feature->getParentNode()->removeChild($feature); + } } + } - if ($name eq 'acpi' && !$guestcaps->{acpi}) { - print STDERR user_message - (__"The target guest does not support acpi under KVM. ACPI ". - "will be disabled."); - $feature->getParentNode()->removeChild($feature); + # Add a features element if there isn't one already + else { + $domfeatures = $dom->createElement('features'); + my ($root) = $dom->findnodes('/domain'); + $root->appendChild($domfeatures); + } + + # Add acpi support if the guest supports it + if ($guestcaps->{acpi}) { + $domfeatures->appendChild($dom->createElement('acpi')); + } + + # Add apic and pae if they're supported by the hypervisor and not already + # there + foreach my $feature ('apic', 'pae') { + my ($d) = $domfeatures->findnodes($feature); + next if (defined($d)); + + my ($c) = $guestcap->findnodes("features/$feature"); + if (defined($c)) { + $domfeatures->appendChild($dom->createElement($feature)); } } } -- 1.6.6
Matthew Booth
2010-Feb-01 17:23 UTC
[Libguestfs] [PATCH 8/8] Converter: Remove obsolete xen-specific configuration
Don't update /domain/@type just for xen as it's always set explicitly to kvm. Don't update /domain/devices/input/@bus as it's replaced by a default device. --- lib/Sys/VirtV2V/Converter.pm | 69 ++++++++---------------------------------- 1 files changed, 13 insertions(+), 56 deletions(-) diff --git a/lib/Sys/VirtV2V/Converter.pm b/lib/Sys/VirtV2V/Converter.pm index a53dd92..736de8c 100644 --- a/lib/Sys/VirtV2V/Converter.pm +++ b/lib/Sys/VirtV2V/Converter.pm @@ -197,17 +197,6 @@ sub _convert_metadata _configure_os($dom, $default_dom, $arch); } -sub _unconfigure_hvs -{ - my ($dom, $default_dom) = @_; - die("unconfigure_hvs called without dom argument") - unless defined($dom); - die("unconfigure_hvs called without default_dom argument") - unless defined($default_dom); - - _unconfigure_xen_metadata($dom, $default_dom); -} - sub _configure_os { my ($dom, $default_dom, $arch) = @_; @@ -431,32 +420,20 @@ sub _configure_drivers } } -sub _replace_with_default_metadata +sub _unconfigure_hvs { - my ($node, $xpath, $default_dom) = @_; - - # Look for a replacement in the defaults - my ($default) = $default_dom->findnodes($xpath); - if(defined($default)) { - if($node->isa('XML::DOM::Attr')) { - $node->setNodeValue($default->getNodeValue()); - } else { - my $replacement = $default->cloneNode(1); - $replacement->setOwnerDocument($node->getOwnerDocument()); - - $node->getParentNode()->replaceChild($replacement, $node); - } - } - - else { - # Warn if no replacement was found - print STDERR user_message - (__x("WARNING: No replacement found for {xpath} in ". - "domain XML. The node was removed.", - xpath => $xpath)); + my ($dom, $default_dom) = @_; + die("unconfigure_hvs called without dom argument") + unless defined($dom); + die("unconfigure_hvs called without default_dom argument") + unless defined($default_dom); - $node->getParentNode()->removeChild($node); + # Remove emulator if it is defined + foreach my $emulator ($dom->findnodes('/domain/devices/emulator')) { + $emulator->getParent()->removeChild($emulator); } + + _unconfigure_xen_metadata($dom, $default_dom); } sub _unconfigure_xen_metadata @@ -466,23 +443,6 @@ sub _unconfigure_xen_metadata # The list of target xen-specific nodes is mostly taken from inspection of # domain.rng - # Nodes which should be replaced with a default if they are present - foreach my $hv_node ( - [ '/domain/@type', 'xen' ], - [ '/domain/devices/input/@bus', 'xen' ] - ) { - my $xpath = $hv_node->[0]; - my $pattern = $hv_node->[1]; - - foreach my $node ($dom->findnodes($xpath)) { - if(defined($pattern)) { - next unless($node->getNodeValue() =~ m{$pattern}); - } - - _replace_with_default_metadata($node, $xpath, $default_dom); - } - } - # Remove machine if it has a xen-specific value # We could replace it with the generic 'pc', but 'pc' is a moving target # across QEMU releases. By removing it entirely, libvirt will automatically @@ -495,11 +455,6 @@ sub _unconfigure_xen_metadata } } - # Remove emulator if it is defined - foreach my $emulator ($dom->findnodes('/domain/devices/emulator')) { - $emulator->getParent()->removeChild($emulator); - } - # Remove the script element if its path attribute is 'vif-bridge' foreach my $script ($dom->findnodes('/domain/devices/interface/script[@path = "vif-bridge"]')) { @@ -507,6 +462,8 @@ sub _unconfigure_xen_metadata } # Other Xen related metadata is handled separately + # /domain/@type + # /domain/devices/input/@bus = xen # /domain/devices/disk/target/@bus = 'xen' # /domain/os/loader = 'xen' # /domain/bootloader -- 1.6.6
Adam Huffman
2010-Feb-03 23:45 UTC
[Libguestfs] [ESX support] Working ESX conversion for RHEL 5
On Mon, Feb 1, 2010 at 5:23 PM, Matthew Booth <mbooth at redhat.com> wrote:> With this patchset I have successfully[1] imported a RHEL 5 guest directly from > ESX with the following command line: > > virt-v2v -ic 'esx://yellow.marston/?no_verify=1' -op transfer RHEL5-64 > > Login details are stored in ~/.netrc > > Note that this is the only guest I've tested against. I haven't for example, > checked that I haven't broken Xen imports.Will this work, or can this be easily modified to work for VMware Server guests? Adam> > Matt > > [1] With the exception of network reconfiguration. After import I manually > munged the network config with virsh edit. > > _______________________________________________ > Libguestfs mailing list > Libguestfs at redhat.com > https://www.redhat.com/mailman/listinfo/libguestfs >