[Libguestfs] [PATCH] Add LocalCopy transfer method to transfer local files to a target

Matthew Booth mbooth at redhat.com
Wed Mar 31 10:26:33 UTC 2010


Also changes command line parsing to require a pool to be specified when using
libvirt output, meaning storage will always be copied.
---
 MANIFEST                                 |    1 +
 lib/Sys/VirtV2V/Connection.pm            |   74 +++++++++---------
 lib/Sys/VirtV2V/Connection/LibVirt.pm    |   18 +++--
 lib/Sys/VirtV2V/Connection/LibVirtXML.pm |   11 ++-
 lib/Sys/VirtV2V/Target/LibVirt.pm        |   18 ++---
 lib/Sys/VirtV2V/Transfer/ESX.pm          |    7 +-
 lib/Sys/VirtV2V/Transfer/LocalCopy.pm    |  121 ++++++++++++++++++++++++++++++
 v2v/virt-v2v.pl                          |    7 ++-
 8 files changed, 193 insertions(+), 64 deletions(-)
 create mode 100644 lib/Sys/VirtV2V/Transfer/LocalCopy.pm

diff --git a/MANIFEST b/MANIFEST
index 1bc6018..d5debe1 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -15,6 +15,7 @@ lib/Sys/VirtV2V/UserMessage.pm
 lib/Sys/VirtV2V/Target/LibVirt.pm
 lib/Sys/VirtV2V/Target/RHEV.pm
 lib/Sys/VirtV2V/Transfer/ESX.pm
+lib/Sys/VirtV2V/Transfer/LocalCopy.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 5ecc7e3..5b4ed8d 100644
--- a/lib/Sys/VirtV2V/Connection.pm
+++ b/lib/Sys/VirtV2V/Connection.pm
@@ -22,10 +22,12 @@ use warnings;
 
 use Sys::Virt;
 
-use Locale::TextDomain 'virt-v2v';
-
+use Sys::VirtV2V::Transfer::ESX;
+use Sys::VirtV2V::Transfer::LocalCopy;
 use Sys::VirtV2V::UserMessage qw(user_message);
 
+use Locale::TextDomain 'virt-v2v';
+
 =pod
 
 =head1 NAME
@@ -134,41 +136,39 @@ sub _storage_iterate
         else {
             my $path = $source->getValue();
 
-            if (defined($transfer)) {
-                # 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, $target);
-
-                # Export the new path
-                $path = $vol->get_path();
-
-                # Find any existing driver element.
-                my ($driver) = $disk->findnodes('driver');
-
-                # Create a new driver element if none exists
-                unless (defined($driver)) {
-                    $driver =
-                        $disk->getOwnerDocument()->createElement("driver");
-                    $disk->appendChild($driver);
-                }
-                $driver->setAttribute('name', 'qemu');
-                $driver->setAttribute('type', $vol->get_format());
-
-                # Remove the @file or @dev attribute before adding a new one
-                $source_e->removeAttributeNode($source);
-
-                # Set @file or @dev as appropriate
-                if ($vol->is_block())
-                {
-                    $disk->setAttribute('type', 'block');
-                    $source_e->setAttribute('dev', $path);
-                } else {
-                    $disk->setAttribute('type', 'file');
-                    $source_e->setAttribute('file', $path);
-                }
+            # 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, $target);
+
+            # Export the new path
+            $path = $vol->get_path();
+
+            # Find any existing driver element.
+            my ($driver) = $disk->findnodes('driver');
+
+            # Create a new driver element if none exists
+            unless (defined($driver)) {
+                $driver =
+                    $disk->getOwnerDocument()->createElement("driver");
+                $disk->appendChild($driver);
+            }
+            $driver->setAttribute('name', 'qemu');
+            $driver->setAttribute('type', $vol->get_format());
+
+            # Remove the @file or @dev attribute before adding a new one
+            $source_e->removeAttributeNode($source);
+
+            # Set @file or @dev as appropriate
+            if ($vol->is_block())
+            {
+                $disk->setAttribute('type', 'block');
+                $source_e->setAttribute('dev', $path);
+            } else {
+                $disk->setAttribute('type', 'file');
+                $source_e->setAttribute('file', $path);
             }
 
             push(@paths, $path);
diff --git a/lib/Sys/VirtV2V/Connection/LibVirt.pm b/lib/Sys/VirtV2V/Connection/LibVirt.pm
index 2c289b9..43fe624 100644
--- a/lib/Sys/VirtV2V/Connection/LibVirt.pm
+++ b/lib/Sys/VirtV2V/Connection/LibVirt.pm
@@ -30,7 +30,6 @@ 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';
@@ -46,7 +45,7 @@ Sys::VirtV2V::Connection::LibVirt - Read libvirt metadata from libvirtd
  use Sys::VirtV2V::Connection::LibVirt;
 
  $conn = Sys::VirtV2V::Connection::LibVirt->new
-    ("xen+ssh://xenserver.example.com/", $name, $pool);
+    ("xen+ssh://xenserver.example.com/", $name, $target);
  $dom = $conn->get_dom();
 
 =head1 DESCRIPTION
@@ -59,11 +58,10 @@ libvirt connection.
 
 =over
 
-=item new(uri, name, pool)
+=item new(uri, name, target)
 
 Create a new Sys::VirtV2V::Connection::LibVirt. Domain I<name> will be
-obtained from I<uri>. Remote storage will be copied to a new volume, which
-will be created in I<pool>.
+obtained from I<uri>. Remote storage will be create on I<target>.
 
 =cut
 
@@ -71,7 +69,7 @@ sub new
 {
     my $class = shift;
 
-    my ($uri, $name, $pool) = @_;
+    my ($uri, $name, $target) = @_;
 
     my $self = {};
 
@@ -151,7 +149,13 @@ sub new
         $transfer = "Sys::VirtV2V::Transfer::ESX";
     }
 
-    $self->_storage_iterate($transfer, $pool);
+    # Default to LocalCopy
+    # XXX: Need transfer methods for remote libvirt connections, e.g. scp
+    else {
+        $transfer = "Sys::VirtV2V::Transfer::LocalCopy";
+    }
+
+    $self->_storage_iterate($transfer, $target);
 
     return $self;
 }
diff --git a/lib/Sys/VirtV2V/Connection/LibVirtXML.pm b/lib/Sys/VirtV2V/Connection/LibVirtXML.pm
index 03b85b1..0ce07fd 100644
--- a/lib/Sys/VirtV2V/Connection/LibVirtXML.pm
+++ b/lib/Sys/VirtV2V/Connection/LibVirtXML.pm
@@ -39,7 +39,7 @@ Sys::VirtV2V::Connection::LibVirtXML - Read libvirt XML from a file
 
  use Sys::VirtV2V::Connection::LibVirtXML;
 
- $conn = Sys::VirtV2V::Connection::LibVirtXML->new($path);
+ $conn = Sys::VirtV2V::Connection::LibVirtXML->new($path, $target);
  $dom = $conn->get_dom();
 
 =head1 DESCRIPTION
@@ -52,9 +52,10 @@ file.
 
 =over
 
-=item new(path)
+=item new(path, target)
 
 Create a new LibVirtXML connection. The metadata itself is read from I<path>.
+Storage will be copied to I<target>.
 
 =cut
 
@@ -62,7 +63,7 @@ sub new
 {
     my $class = shift;
 
-    my ($path) = @_;
+    my ($path, $target) = @_;
 
     my $self = {};
     $self->{path} = $path;
@@ -71,8 +72,8 @@ sub new
 
     $self->_get_dom($path);
 
-    # No transfer methods defined yet
-    $self->_storage_iterate(undef, undef);
+    # Only support LocalCopy for libvirtxml
+    $self->_storage_iterate("Sys::VirtV2V::Transfer::LocalCopy", $target);
 
     return $self;
 }
diff --git a/lib/Sys/VirtV2V/Target/LibVirt.pm b/lib/Sys/VirtV2V/Target/LibVirt.pm
index 96ed513..ab95fb7 100644
--- a/lib/Sys/VirtV2V/Target/LibVirt.pm
+++ b/lib/Sys/VirtV2V/Target/LibVirt.pm
@@ -186,16 +186,14 @@ sub new
 
     $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)));
-        }
+    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;
diff --git a/lib/Sys/VirtV2V/Transfer/ESX.pm b/lib/Sys/VirtV2V/Transfer/ESX.pm
index 42b4326..9db28e0 100644
--- a/lib/Sys/VirtV2V/Transfer/ESX.pm
+++ b/lib/Sys/VirtV2V/Transfer/ESX.pm
@@ -120,10 +120,9 @@ sub get_volume
     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.",
+                                      "already exists on the target. NOT ".
+                                      "fetching it again. Delete the volume ".
+                                      "and retry to download again.",
                                       name => $volname));
         return $target->get_volume($volname);
     }
diff --git a/lib/Sys/VirtV2V/Transfer/LocalCopy.pm b/lib/Sys/VirtV2V/Transfer/LocalCopy.pm
new file mode 100644
index 0000000..814bf4a
--- /dev/null
+++ b/lib/Sys/VirtV2V/Transfer/LocalCopy.pm
@@ -0,0 +1,121 @@
+# Sys::VirtV2V::Transfer::LocalCopy
+# 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::LocalCopy;
+
+use File::Spec;
+use File::stat;
+
+use Sys::VirtV2V::UserMessage qw(user_message);
+
+use Locale::TextDomain 'virt-v2v';
+
+=pod
+
+=head1 NAME
+
+Sys::VirtV2V::Transfer::LocalCopy - Copy a guest's local storage
+
+=head1 SYNOPSIS
+
+ use Sys::VirtV2V::Transfer::LocalCopy;
+
+ $vol = Sys::VirtV2V::Transfer::LocalCopy->transfer($conn, $path, $target);
+
+=head1 DESCRIPTION
+
+Sys::VirtV2V::Transfer::LocalCopy retrieves guest storage devices from local
+storage.
+
+=head1 METHODS
+
+=over
+
+=item transfer(conn, path, target)
+
+Transfer <path> from local storage. Storage will be created using <target>.
+
+=cut
+
+sub transfer
+{
+    my $class = shift;
+
+    my ($conn, $path, $target) = @_;
+
+    my (undef, undef, $name) = File::Spec->splitpath($path);
+
+    if ($target->volume_exists($name)) {
+        print STDERR user_message(__x("WARNING: storage volume {name} ".
+                                      "already exists on the target. NOT ".
+                                      "copying it again. Delete the volume ".
+                                      "and retry to copy again.",
+                                      name => $name));
+        return $target->get_volume($name);
+    }
+
+    my $fh;
+    open($fh, '<', $path)
+        or die(user_message(__x("Unable to open {path} for reading: {error}",
+                                path => $path,
+                                error => $!)));
+
+    my $st = stat($fh)
+        or die(user_message(__x("Unable to stat {path}: {error}",
+                                path => $path,
+                                error => $!)));
+
+    my $vol = $target->create_volume($name, $st->size);
+    $vol->open();
+
+    for (;;) {
+        my $buffer;
+        # Transfer in block chunks
+        my $in = sysread($fh, $buffer, $st->blksize);
+        die(user_message(__x("Error reading data from {path}: {error}",
+                             path => $path,
+                             error => $!))) if (!defined($in));
+
+        last if ($in == 0);
+
+        $vol->write($buffer);
+    }
+
+    $vol->close();
+
+    return $vol;
+}
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2010 Red Hat Inc.
+
+=head1 LICENSE
+
+Please see the file COPYING.LIB for the full license.
+
+=head1 SEE ALSO
+
+L<Sys::VirtV2V::Converter(3pm)>,
+L<virt-v2v(1)>,
+L<http://libguestfs.org/>.
+
+=cut
+
+1;
diff --git a/v2v/virt-v2v.pl b/v2v/virt-v2v.pl
index 351caf1..c1a4728 100755
--- a/v2v/virt-v2v.pl
+++ b/v2v/virt-v2v.pl
@@ -225,6 +225,11 @@ if(defined($config_file)) {
 
 my $target;
 if ($output_method eq "libvirt") {
+    pod2usage({ -message => __("You must specify an output storage pool ".
+                               "when using the libvirt output method"),
+                -exitval => 1 })
+        unless (defined($output_pool));
+
     $target = new Sys::VirtV2V::Target::LibVirt($output_uri, $output_pool);
 }
 
@@ -257,7 +262,7 @@ eval {
                      modulename => 'libvirtxml'));
         }
 
-        $conn = Sys::VirtV2V::Connection::LibVirtXML->new($path);
+        $conn = Sys::VirtV2V::Connection::LibVirtXML->new($path, $target);
     }
 
     elsif ($input_method eq "libvirt") {
-- 
1.6.6.1




More information about the Libguestfs mailing list