[Libguestfs] [PATCH 1/2] Refactor guest and volume creation into Sys::VirtV2V::Target::LibVirt

Matthew Booth mbooth at redhat.com
Tue Mar 30 16:14:45 UTC 2010


Move all target-specific functionality into its own module in preparation for
output to RHEV.
---
 MANIFEST                          |    1 +
 lib/Sys/VirtV2V/Connection.pm     |   46 ++---
 lib/Sys/VirtV2V/Converter.pm      |  138 +------------
 lib/Sys/VirtV2V/Target/LibVirt.pm |  419 +++++++++++++++++++++++++++++++++++++
 lib/Sys/VirtV2V/Transfer/ESX.pm   |   91 +++------
 po/POTFILES.in                    |    1 +
 v2v/virt-v2v.pl                   |   69 ++++---
 7 files changed, 511 insertions(+), 254 deletions(-)
 create mode 100644 lib/Sys/VirtV2V/Target/LibVirt.pm

diff --git a/MANIFEST b/MANIFEST
index cc6dd92..d4dd140 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/Target/LibVirt.pm
 lib/Sys/VirtV2V/Transfer/ESX.pm
 MANIFEST			This list of files
 MANIFEST.SKIP
diff --git a/lib/Sys/VirtV2V/Connection.pm b/lib/Sys/VirtV2V/Connection.pm
index 46bd020..5ecc7e3 100644
--- a/lib/Sys/VirtV2V/Connection.pm
+++ b/lib/Sys/VirtV2V/Connection.pm
@@ -36,7 +36,7 @@ Sys::VirtV2V::Connection - Obtain domain metadata
 
  use Sys::VirtV2V::Connection::LibVirt;
 
- $conn = Sys::VirtV2V::Connection::LibVirt->new($uri, $name, $pool);
+ $conn = Sys::VirtV2V::Connection::LibVirt->new($uri, $name, $target);
  $dom = $conn->get_dom();
  $storage = $conn->get_storage_paths();
  $devices = $conn->get_storage_devices();
@@ -106,7 +106,7 @@ sub _storage_iterate
 {
     my $self = shift;
 
-    my ($transfer, $pool) = @_;
+    my ($transfer, $target) = @_;
 
     my $dom = $self->get_dom();
 
@@ -121,8 +121,8 @@ sub _storage_iterate
         defined($source) or die("source element has neither dev nor file: \n".
                                 $dom->toString());
 
-        my ($target) = $disk->findnodes('target/@dev');
-        defined($target) or die("disk does not have a target device: \n".
+        my ($dev) = $disk->findnodes('target/@dev');
+        defined($dev) or die("disk does not have a target device: \n".
                                 $dom->toString());
 
         # If the disk is a floppy or a cdrom, blank its source
@@ -135,16 +135,15 @@ sub _storage_iterate
             my $path = $source->getValue();
 
             if (defined($transfer)) {
-                # Die if transfer required and no output pool
-                die (user_message(__"No output pool was specified"))
-                    unless (defined($pool));
+                # Die if transfer required and no output target
+                die (user_message(__"No output target was specified"))
+                    unless (defined($target));
 
                 # Fetch the remote storage
-                my $vol = $transfer->transfer($self, $path, $pool);
+                my $vol = $transfer->transfer($self, $path, $target);
 
-                # Parse the XML description of the returned volume
-                my $voldom =
-                    new XML::DOM::Parser->parse($vol->get_xml_description());
+                # Export the new path
+                $path = $vol->get_path();
 
                 # Find any existing driver element.
                 my ($driver) = $disk->findnodes('driver');
@@ -156,37 +155,24 @@ sub _storage_iterate
                     $disk->appendChild($driver);
                 }
                 $driver->setAttribute('name', 'qemu');
-
-                # Get the volume format for passing to the qemu driver
-                my ($format) =
-                    $voldom->findnodes('/volume/target/format/@type');
-
-                $format = $format->getValue() if (defined($format));
-
-                # Auto-detect if no format is specified explicitly
-                $format ||= 'auto';
-
-                $driver->setAttribute('type', $format);
+                $driver->setAttribute('type', $vol->get_format());
 
                 # 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)
+                if ($vol->is_block())
                 {
-                    $disk->setAttribute('type', 'file');
-                    $source_e->setAttribute('file', $path);
-                } else {
                     $disk->setAttribute('type', 'block');
                     $source_e->setAttribute('dev', $path);
+                } else {
+                    $disk->setAttribute('type', 'file');
+                    $source_e->setAttribute('file', $path);
                 }
             }
 
             push(@paths, $path);
-            push(@devices, $target->getNodeValue());
+            push(@devices, $dev->getNodeValue());
         }
     }
 
diff --git a/lib/Sys/VirtV2V/Converter.pm b/lib/Sys/VirtV2V/Converter.pm
index b817f49..fbcaa51 100644
--- a/lib/Sys/VirtV2V/Converter.pm
+++ b/lib/Sys/VirtV2V/Converter.pm
@@ -42,7 +42,7 @@ Sys::VirtV2V::Converter - Convert a guest to run on KVM
  use Sys::VirtV2V::Converter;
 
  my $guestos = Sys::VirtV2V::GuestOS->new($g, $os, $dom, $config);
- Sys::VirtV2V::Converter->convert($vmm, $guestos, $config, $dom, $os, $devices);
+ Sys::VirtV2V::Converter->convert($guestos, $config, $dom, $os, $devices);
 
 =head1 DESCRIPTION
 
@@ -102,16 +102,12 @@ use constant KVM_XML_NOVIRTIO => "
 </domain>
 ";
 
-=item Sys::VirtV2V::Converter->convert(vmm, guestos, dom, desc)
+=item Sys::VirtV2V::Converter->convert(guestos, dom, desc)
 
 Instantiate an appropriate backend and call convert on it.
 
 =over
 
-=item vmm
-
-A Sys::Virt connection.
-
 =item guestos
 
 An initialised Sys::VirtV2V::GuestOS object for the guest.
@@ -137,8 +133,7 @@ sub convert
 {
     my $class = shift;
 
-    my ($vmm, $guestos, $config, $dom, $desc, $devices) = @_;
-    carp("convert called without vmm argument") unless defined($vmm);
+    my ($guestos, $config, $dom, $desc, $devices) = @_;
     carp("convert called without guestos argument") unless defined($guestos);
     # config will be undefined if no config was specified
     carp("convert called without dom argument") unless defined($dom);
@@ -162,23 +157,14 @@ sub convert
     _map_networks($dom, $config) if (defined($config));
 
     # Convert the metadata
-    _convert_metadata($vmm, $dom, $desc, $devices, $guestcaps);
-
-    my ($name) = $dom->findnodes('/domain/name/text()');
-    $name = $name->getNodeValue();
+    _convert_metadata($dom, $desc, $devices, $guestcaps);
 
-    if($guestcaps->{virtio}) {
-        print user_message
-            (__x("{name} configured with virtio drivers", name => $name));
-    } else {
-        print user_message
-            (__x("{name} configured without virtio drivers", name => $name));
-    }
+    return $guestcaps;
 }
 
 sub _convert_metadata
 {
-    my ($vmm, $dom, $desc, $devices, $guestcaps) = @_;
+    my ($dom, $desc, $devices, $guestcaps) = @_;
 
     my $arch   = $guestcaps->{arch};
     my $virtio = $guestcaps->{virtio};
@@ -193,9 +179,6 @@ sub _convert_metadata
     # Replace source hypervisor metadata with KVM defaults
     _unconfigure_hvs($dom, $default_dom);
 
-    # Configure guest according to local hypervisor's capabilities
-    _configure_capabilities($dom, $vmm, $guestcaps);
-
     # Remove any configuration related to a PV kernel bootloader
     _unconfigure_bootloaders($dom);
 
@@ -264,115 +247,6 @@ sub _configure_default_devices
     }
 }
 
-sub _configure_capabilities
-{
-    my ($dom, $vmm, $guestcaps) = @_;
-
-    # Parse the capabilities of the connected libvirt
-    my $caps = new XML::DOM::Parser->parse($vmm->get_capabilities());
-
-    my $arch = $guestcaps->{arch};
-
-    (my $guestcap) = $caps->findnodes
-        ("/capabilities/guest[arch[\@name='$arch']/domain/\@type='kvm']");
-
-    die(__x("The connected hypervisor does not support a {arch} kvm guest",
-        arch => $arch)) unless(defined($guestcap));
-
-    # Ensure that /domain/@type = 'kvm'
-    my ($type) = $dom->findnodes('/domain/@type');
-    $type->setNodeValue('kvm');
-
-    # Set /domain/os/type to the value taken from capabilities
-    my ($os_type) = $dom->findnodes('/domain/os/type/text()');
-    if(defined($os_type)) {
-        my ($caps_os_type) = $guestcap->findnodes('os_type/text()');
-        $os_type->setNodeValue($caps_os_type->getNodeValue());
-    }
-
-    # Check that /domain/os/type/@machine, if set, is listed in capabilities
-    my ($machine) = $dom->findnodes('/domain/os/type/@machine');
-    if(defined($machine)) {
-        my @machine_caps = $guestcap->findnodes
-            ("arch[\@name='$arch']/machine/text()");
-
-        my $found = 0;
-        foreach my $machine_cap (@machine_caps) {
-            if($machine eq $machine_cap) {
-                $found = 1;
-                last;
-            }
-        }
-
-        # If the machine isn't listed as a capability, warn and remove it
-        if(!$found) {
-            print STDERR user_message
-                (__x("The connected hypervisor does not support a ".
-                     "machine type of {machine}. It will be set to the ".
-                     "current default.",
-                     machine => $machine->getValue()));
-
-            my ($type) = $dom->findnodes('/domain/os/type');
-            $type->getAttributes()->removeNamedItem('machine');
-        }
-    }
-
-    # 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 ($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 ($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));
-        }
-    }
-}
-
 sub _unconfigure_bootloaders
 {
     my ($dom) = @_;
diff --git a/lib/Sys/VirtV2V/Target/LibVirt.pm b/lib/Sys/VirtV2V/Target/LibVirt.pm
new file mode 100644
index 0000000..96ed513
--- /dev/null
+++ b/lib/Sys/VirtV2V/Target/LibVirt.pm
@@ -0,0 +1,419 @@
+# Sys::VirtV2V::Target::LibVirt
+# 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
+
+use strict;
+use warnings;
+
+package Sys::VirtV2V::Target::LibVirt::Vol;
+
+sub _new
+{
+    my $class = shift;
+    my ($vol) = @_;
+
+    my $self = {};
+    bless($self, $class);
+
+    $self->{vol} = $vol;
+
+    return $self;
+}
+
+sub _create
+{
+    my $class = shift;
+    my ($pool, $name, $size) = @_;
+
+    my $vol_xml = "
+        <volume>
+            <name>$name</name>
+            <capacity>$size</capacity>
+        </volume>
+    ";
+
+    my $vol;
+    eval {
+        $vol = $pool->create_volume($vol_xml);
+    };
+    die(user_message(__x("Failed to create storage volume: {error}",
+                         error => $@->stringify()))) if ($@);
+
+    return $class->_new($vol);
+}
+
+sub _get
+{
+    my $class = shift;
+    my ($pool, $name) = @_;
+
+    my $vol;
+    eval {
+        $vol = $pool->get_volume_by_name($name);
+    };
+    die(user_message(__x("Failed to get storage volume: {error}",
+                          error => $@->stringify()))) if ($@);
+
+    return $class->_new($vol);
+}
+
+sub get_path
+{
+    my $self = shift;
+
+    return $self->{vol}->get_path();
+}
+
+sub get_format
+{
+    my $self = shift;
+
+    my $vol = $self->{vol};
+    my $voldom = new XML::DOM::Parser->parse($vol->get_xml_description());
+
+    my ($format) = $voldom->findnodes('/volume/target/format/@type');
+    $format = $format->getValue() if (defined($format));
+    $format ||= 'auto';
+
+    return $format;
+}
+
+sub is_block
+{
+    my $self = shift;
+
+    my $type = $self->{vol}->get_info()->{type};
+    return $type == Sys::Virt::StorageVol::TYPE_BLOCK;
+}
+
+sub open
+{
+    my $self = shift;
+
+    my $path = $self->get_path();
+    open(my $fd, '>', $path)
+        or die(user_message(__x("Error opening storage volume {path} ".
+                                "for writing: {error}", error => $!)));
+
+    $self->{fd} = $fd;
+}
+
+sub write
+{
+    my $self = shift;
+    my ($data) = @_;
+
+    defined($self->{fd}) or die("write called without open");
+
+    syswrite($self->{fd}, $data)
+        or die(user_message(__x("Error writing to {path}: {error}",
+                                path => $self->get_path(),
+                                error => $!)));
+}
+
+sub close
+{
+    my $self = shift;
+
+    close($self->{fd})
+        or die(user_message(__x("Error closing volume handle: {error}",
+                                error => $!)));
+
+    delete($self->{fd});
+}
+
+package Sys::VirtV2V::Target::LibVirt;
+
+use Locale::TextDomain 'virt-v2v';
+
+=head1 NAME
+
+Sys::VirtV2V::Target::LibVirt - Output to libvirt
+
+=head1 SYNOPSIS
+
+ use Sys::VirtV2V::Target::LibVirt;
+
+ my $target = new Sys::VirtV2V::Target::LibVirt($uri, $poolname);
+
+=head1 DESCRIPTION
+
+Sys::VirtV2V::Target::LibVirt creates a new libvirt domain using the given
+target URI. New storage will be created in the target pool.
+
+=head1 METHODS
+
+=over
+
+=item Sys::VirtV2V::Target::LibVirt->new(uri, poolname)
+
+Create a new Sys::VirtV2V::Target::LibVirt object.
+
+=over
+
+=item uri
+
+A libvirt connection URI
+
+=item poolname
+
+The name of a storage pool managed by the target libvirt daemon.
+
+=back
+
+=cut
+
+sub new
+{
+    my $class = shift;
+    my ($uri, $poolname) = @_;
+
+    my $self = {};
+    bless($self, $class);
+
+    $self->{vmm} = Sys::Virt->new(auth => 1, uri => $uri);
+
+    if (defined($poolname)) {
+        eval {
+            $self->{pool} = $self->{vmm}->get_storage_pool_by_name($poolname);
+        };
+
+        if ($@) {
+            die(user_message(__x("Output pool {poolname} is not a valid ".
+                                 "storage pool",
+                                 poolname => $poolname)));
+        }
+    }
+
+    return $self;
+}
+
+=item create_volume(name, size)
+
+Create a new volume in the pool whose name was passed to new().
+
+=over
+
+=item name
+
+The name of the volume which is being created.
+
+=item size
+
+The size of the volume which is being created in bytes.
+
+=back
+
+create_volume() returns a Sys::VirtV2V::Target::LibVirt::Vol object.
+
+=cut
+
+sub create_volume
+{
+    my $self = shift;
+    my ($name, $size) = @_;
+
+    return Sys::VirtV2V::Target::LibVirt::Vol->_create($self->{pool},
+                                                       $name, $size);
+}
+
+=item volume_exists (name)
+
+Check if volume I<name> exists in the target pool.
+
+Returns 1 if it exists, 0 otherwise.
+
+=cut
+
+sub volume_exists
+{
+    my $self = shift;
+    my ($name) = @_;
+
+    my $vol;
+    eval {
+        $vol = $self->{pool}->get_volume_by_name($name);
+    };
+
+    # The above command will generate VIR_ERR_NO_STORAGE_VOL if the
+    # volume doesn't exist
+    if ($@ && $@->code == Sys::Virt::Error::ERR_NO_STORAGE_VOL) {
+        return 0;
+    }
+
+    if ($@) {
+        # We got an error, but not the one we expected
+        die(user_message(__x("Unexpected error accessing storage pool: ",
+                             "{error}", error => $@->stringify())));
+    }
+
+    return 1;
+}
+
+=item get_volume (name)
+
+Get a reference to an existing volume. See L<create_volume> for return value.
+
+=cut
+
+sub get_volume
+{
+    my $self = shift;
+    my ($name) = @_;
+
+    return Sys::VirtV2V::Target::LibVirt::Vol->_get($self->{pool}, $name);
+}
+
+=item create_guest(dom)
+
+Create the guest in the target
+
+=cut
+
+sub create_guest
+{
+    my $self = shift;
+    my ($dom, $guestcaps) = @_;
+
+    my $vmm = $self->{vmm};
+
+    _configure_capabilities($vmm, $dom, $guestcaps);
+
+    $vmm->define_domain($dom->toString());
+}
+
+# Configure guest according to target hypervisor's capabilities
+sub _configure_capabilities
+{
+    my ($vmm, $dom, $guestcaps) = @_;
+
+    # Parse the capabilities of the connected libvirt
+    my $caps = new XML::DOM::Parser->parse($vmm->get_capabilities());
+
+    my $arch = $guestcaps->{arch};
+
+    (my $guestcap) = $caps->findnodes
+        ("/capabilities/guest[arch[\@name='$arch']/domain/\@type='kvm']");
+
+    die(__x("The connected hypervisor does not support a {arch} kvm guest",
+        arch => $arch)) unless(defined($guestcap));
+
+    # Ensure that /domain/@type = 'kvm'
+    my ($type) = $dom->findnodes('/domain/@type');
+    $type->setNodeValue('kvm');
+
+    # Set /domain/os/type to the value taken from capabilities
+    my ($os_type) = $dom->findnodes('/domain/os/type/text()');
+    if(defined($os_type)) {
+        my ($caps_os_type) = $guestcap->findnodes('os_type/text()');
+        $os_type->setNodeValue($caps_os_type->getNodeValue());
+    }
+
+    # Check that /domain/os/type/@machine, if set, is listed in capabilities
+    my ($machine) = $dom->findnodes('/domain/os/type/@machine');
+    if(defined($machine)) {
+        my @machine_caps = $guestcap->findnodes
+            ("arch[\@name='$arch']/machine/text()");
+
+        my $found = 0;
+        foreach my $machine_cap (@machine_caps) {
+            if($machine eq $machine_cap) {
+                $found = 1;
+                last;
+            }
+        }
+
+        # If the machine isn't listed as a capability, warn and remove it
+        if(!$found) {
+            print STDERR user_message
+                (__x("The connected hypervisor does not support a ".
+                     "machine type of {machine}. It will be set to the ".
+                     "current default.",
+                     machine => $machine->getValue()));
+
+            my ($type) = $dom->findnodes('/domain/os/type');
+            $type->getAttributes()->removeNamedItem('machine');
+        }
+    }
+
+    # 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 ($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 ($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));
+        }
+    }
+}
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2010 Red Hat Inc.
+
+=head1 LICENSE
+
+Please see the file COPYING for the full license.
+
+=cut
+
+1;
diff --git a/lib/Sys/VirtV2V/Transfer/ESX.pm b/lib/Sys/VirtV2V/Transfer/ESX.pm
index 66ba515..2d15732 100644
--- a/lib/Sys/VirtV2V/Transfer/ESX.pm
+++ b/lib/Sys/VirtV2V/Transfer/ESX.pm
@@ -71,7 +71,7 @@ sub new {
     });
 
     $self->{_v2v_server}   = $server;
-    $self->{_v2v_pool}     = $pool;
+    $self->{_v2v_target}   = $target;
     $self->{_v2v_username} = $username;
     $self->{_v2v_password} = $password;
 
@@ -113,31 +113,19 @@ sub get_volume
     $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 $volname = $vmdk;
+    $volname =~ s,/,_,g;
+    $self->{_v2v_volname} = $volname;
+
+    my $target = $self->{_v2v_target};
+    if ($target->volume_exists($volname)) {
+        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 => $volname));
+        return $target->get_volume($volname);
     }
 
     $self->{_v2v_received} = 0;
@@ -150,11 +138,9 @@ sub get_volume
         my $died = $r->header('X-Died');
         die($died) if (defined($died));
 
-        # Close the volume file descriptor
-        close($self->{_v2v_volfh})
-            or die(user_message(__x("Error closing volume handle: {error}",
-                                    error => $!)));
-        return $self->{_v2v_vol};
+        my $vol = $self->{_v2v_vol};
+        $vol->close();
+        return $vol;
     }
 
     die(user_message(__x("Didn't receive full volume. Received {received} of ".
@@ -192,14 +178,8 @@ sub handle_data
 
     my ($data, $response) = @_;
 
-    my $volfh = $self->{_v2v_volfh};
-
     $self->{_v2v_received} += length($data);
-
-    syswrite($volfh, $data)
-        or die(user_message(__x("Error writing to {path}: {error}",
-                                path => $self->{_v2v_volpath},
-                                error => $!)));
+    $self->{_v2v_vol}->write($data);
 }
 
 sub create_volume
@@ -208,9 +188,8 @@ sub create_volume
 
     my ($response) = @_;
 
-    my $pool = $self->{_v2v_pool};
+    my $target = $self->{_v2v_target};
 
-    # 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));
@@ -218,27 +197,9 @@ sub create_volume
     my $size = $response->content_length();
     $self->{_v2v_volsize} = $size;
 
-    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;
+    my $vol = $target->create_volume($name, $size);
+    $vol->open();
+    $self->{_v2v_vol} = $vol;
 }
 
 sub verify_certificate
@@ -295,10 +256,10 @@ Sys::VirtV2V::Transfer::ESX retrieves guest storage devices from an ESX server.
 
 =over
 
-=item transfer(conn, path, pool)
+=item transfer(conn, path, target)
 
 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>.
+be taken from <conn>. Storage will be created using <target>.
 
 =cut
 
@@ -306,7 +267,7 @@ sub transfer
 {
     my $class = shift;
 
-    my ($conn, $path, $pool) = @_;
+    my ($conn, $path, $target) = @_;
 
     my $uri      = $conn->{uri};
     my $username = $conn->{username};
@@ -330,7 +291,7 @@ sub transfer
     my $ua = Sys::VirtV2V::Transfer::ESX::UA->new($conn->{hostname},
                                                   $username,
                                                   $password,
-                                                  $pool,
+                                                  $target,
                                                   $noverify);
 
     return $ua->get_volume($path);
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 3874426..ffdf250 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -7,6 +7,7 @@
 ../lib/Sys/VirtV2V/GuestOS.pm
 ../lib/Sys/VirtV2V/GuestOS/RedHat.pm
 ../lib/Sys/VirtV2V.pm
+../lib/Sys/VirtV2V/Target/LibVirt.pm
 ../lib/Sys/VirtV2V/Transfer/ESX.pm
 ../lib/Sys/VirtV2V/UserMessage.pm
 ../snapshot/v2v-snapshot.pl
diff --git a/v2v/virt-v2v.pl b/v2v/virt-v2v.pl
index 33e65f3..a9e834f 100755
--- a/v2v/virt-v2v.pl
+++ b/v2v/virt-v2v.pl
@@ -36,6 +36,7 @@ use Sys::VirtV2V;
 use Sys::VirtV2V::Converter;
 use Sys::VirtV2V::Connection::LibVirt;
 use Sys::VirtV2V::Connection::LibVirtXML;
+use Sys::VirtV2V::Target::LibVirt;
 use Sys::VirtV2V::ExecHelper;
 use Sys::VirtV2V::GuestOS;
 use Sys::VirtV2V::UserMessage qw(user_message);
@@ -99,11 +100,29 @@ my $input_transport;
 
 =item B<-it method>
 
-Species the transport method used to obtain raw storage from the source guest.
+Specifies the transport method used to obtain raw storage from the source guest.
 This is currently only a placeholder, and does nothing.
 
 =cut
 
+my $output_method = "libvirt";
+
+=item B<-o method>
+
+Specifies the output method. Supported output methods are:
+
+=over
+
+=item libvirt
+
+Create a libvirt guest. See the I<-oc> and I<-op> options.
+
+=back
+
+If no output type is specified, it defaults to libvirt.
+
+=cut
+
 my $output_uri = "qemu:///system";
 
 =item B<-oc URI>
@@ -186,11 +205,13 @@ if(defined($config_file)) {
                          path => $config_file, error => $@))) if ($@);
 }
 
-# Connect to target libvirt
-my $vmm = Sys::Virt->new(
-    auth => 1,
-    uri => $output_uri
-);
+my $target;
+if ($output_method eq "libvirt") {
+    $target = new Sys::VirtV2V::Target::LibVirt($output_uri, $output_pool);
+} else {
+    die(user_message(__x("{output} is not a valid output method",
+                         output => $output_method)));
+}
 
 # Get an appropriate Connection
 my $conn;
@@ -215,24 +236,8 @@ eval {
             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);
+                                                       $target);
 
         # Warn if we were given more than 1 argument
         if(scalar(@_) > 0) {
@@ -280,10 +285,20 @@ my $os = inspect_guest($g);
 my $guestos = Sys::VirtV2V::GuestOS->new($g, $os, $dom, $config);
 
 # Modify the guest and its metadata for the target hypervisor
-Sys::VirtV2V::Converter->convert($vmm, $guestos, $config, $dom, $os,
-                                 $conn->get_storage_devices());
-
-$vmm->define_domain($dom->toString());
+my $guestcaps = Sys::VirtV2V::Converter->convert($guestos, $config, $dom, $os,
+                                                 $conn->get_storage_devices());
+
+$target->create_guest($dom, $guestcaps);
+
+my ($name) = $dom->findnodes('/domain/name/text()');
+$name = $name->getNodeValue();
+if($guestcaps->{virtio}) {
+    print user_message
+        (__x("{name} configured with virtio drivers", name => $name));
+} else {
+    print user_message
+        (__x("{name} configured without virtio drivers", name => $name));
+}
 
 exit(0);
 
-- 
1.6.6.1




More information about the Libguestfs mailing list