[PATCH] Add SSH transfer method
Support obtaining guest storage over ssh. The following now works: virt-v2v -f v2v/virt-v2v.conf -ic 'xen+ssh://xen.example.com/system' \ -op transfer rhel54pv64 --- MANIFEST | 1 + lib/Sys/VirtV2V/Connection.pm | 1 + lib/Sys/VirtV2V/Connection/LibVirt.pm | 4 + lib/Sys/VirtV2V/Transfer/SSH.pm | 208 +++++++++++++++++++++++++++++++++ v2v/virt-v2v.pl | 15 ++- 5 files changed, 222 insertions(+), 7 deletions(-) create mode 100644 lib/Sys/VirtV2V/Transfer/SSH.pm diff --git a/MANIFEST b/MANIFEST index 65b5a8e..5fd38c6 100644 --- a/MANIFEST +++ b/MANIFEST @@ -17,6 +17,7 @@ lib/Sys/VirtV2V/Target/LibVirt.pm lib/Sys/VirtV2V/Target/RHEV.pm lib/Sys/VirtV2V/Transfer/ESX.pm lib/Sys/VirtV2V/Transfer/LocalCopy.pm +lib/Sys/VirtV2V/Transfer/SSH.pm MANIFEST This list of files MANIFEST.SKIP META.yml diff --git a/lib/Sys/VirtV2V/Connection.pm b/lib/Sys/VirtV2V/Connection.pm index c901b90..da6a44b 100644 --- a/lib/Sys/VirtV2V/Connection.pm +++ b/lib/Sys/VirtV2V/Connection.pm @@ -24,6 +24,7 @@ use Sys::Virt; use Sys::VirtV2V::Transfer::ESX; use Sys::VirtV2V::Transfer::LocalCopy; +use Sys::VirtV2V::Transfer::SSH; use Sys::VirtV2V::UserMessage qw(user_message); use Locale::TextDomain 'virt-v2v'; diff --git a/lib/Sys/VirtV2V/Connection/LibVirt.pm b/lib/Sys/VirtV2V/Connection/LibVirt.pm index 0a0330e..5e06356 100644 --- a/lib/Sys/VirtV2V/Connection/LibVirt.pm +++ b/lib/Sys/VirtV2V/Connection/LibVirt.pm @@ -150,6 +150,10 @@ sub new $transfer = "Sys::VirtV2V::Transfer::ESX"; } + elsif ($self->{uri}->scheme =~ /\+ssh$/) { + $transfer = "Sys::VirtV2V::Transfer::SSH"; + } + # Default to LocalCopy # XXX: Need transfer methods for remote libvirt connections, e.g. scp else { diff --git a/lib/Sys/VirtV2V/Transfer/SSH.pm b/lib/Sys/VirtV2V/Transfer/SSH.pm new file mode 100644 index 0000000..3b08716 --- /dev/null +++ b/lib/Sys/VirtV2V/Transfer/SSH.pm @@ -0,0 +1,208 @@ +# Sys::VirtV2V::Transfer::SSH +# Copyright (C) 2010 Red Hat Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +package Sys::VirtV2V::Transfer::SSH; + +use POSIX; +use File::Spec; +use File::stat; + +use Sys::VirtV2V::UserMessage qw(user_message); + +use Locale::TextDomain 'virt-v2v'; + +=pod + +=head1 NAME + +Sys::VirtV2V::Transfer::SSH - Copy a remote guest's storage via ssh + +=head1 SYNOPSIS + + use Sys::VirtV2V::Transfer::SSH; + + $vol = Sys::VirtV2V::Transfer::SSH->transfer($conn, $path, $target); + +=head1 DESCRIPTION + +Sys::VirtV2V::Transfer::SSH retrieves guest storage devices from a remote server +via SSH. + +=head1 METHODS + +=over + +=item transfer(conn, path, target) + +Transfer <path> from the remove server. Storage will be created using <target>. + +=cut + +sub transfer +{ + my $class = shift; + + my ($conn, $path, $target) = @_; + + my (undef, undef, $name) = File::Spec->splitpath($path); + + if ($target->volume_exists($name)) { + print STDERR user_message(__x("WARNING: storage volume {name} ". + "already exists on the target. NOT ". + "copying it again. Delete the volume ". + "and retry to copy again.", + name => $name)); + return $target->get_volume($name); + } + + my $uri = $conn->{uri}; + my $username = $conn->{username}; + my $password = $conn->{password}; + my $host = $conn->{hostname}; + + die("URI not defined for connection") unless (defined($uri)); + + my ($pid, $size, $fh, $error) = + _connect($host, $username, $path); + + my $vol = $target->create_volume($name, $size); + $vol->open(); + + for (;;) { + my $buffer; + # Transfer in 8k chunks + my $in = sysread($fh, $buffer, 8 * 1024); + die(user_message(__x("Error reading data from {path}: {error}", + path => $path, + error => $!))) if (!defined($in)); + + last if ($in == 0); + + $vol->write($buffer); + } + + $vol->close(); + + waitpid($pid, 0) == $pid or die("error reaping child: $!"); + # If the child returned an error, check for anything on its stderr + if ($? != 0) { + my $msg = ""; + while (<$error>) { + $msg .= $_; + } + die(user_message(__x("Unexpected error copying {path} from {host}. ". + "Command output: {output}", + path => $path, + host => $uri->host, + output => $msg))); + } + + return $vol; +} + +sub _connect +{ + my ($host, $username, $path) = @_; + + my ($stdin_read, $stdin_write); + my ($stdout_read, $stdout_write); + my ($stderr_read, $stderr_write); + + pipe($stdin_read, $stdin_write); + pipe($stdout_read, $stdout_write); + pipe($stderr_read, $stderr_write); + + my $pid = fork(); + if ($pid == 0) { + my @command; + push(@command, 'ssh'); + push(@command, '-l', $username) if (defined($username)); + push(@command, $host); + push(@command, "stat -c %s $path; cat $path"); + + # Close the ends of the pipes we don't need + close($stdin_write); + close($stdout_read); + close($stderr_read); + + # dup2() stdin, stdout and stderr to pipes + open(STDIN, "<&".fileno($stdin_read)) + or die("dup stdin failed: $!"); + open(STDOUT, "<&".fileno($stdout_write)) + or die("dup stdout failed: $!"); + open(STDERR, "<&".fileno($stderr_write)) + or die("dup stderr failed: $!"); + + # We parse stderr, so make sure it's in English + $ENV{LANG} = 'C'; + exec(@command); + } + + close($stdin_read); + close($stdout_write); + close($stderr_write); + + # Check that we don't get output on stderr before we read the file size + for(;;) { + my ($rin, $rout); + $rin = ''; + vec($rin, fileno($stdout_read), 1) = 1; + vec($rin, fileno($stderr_read), 1) = 1; + + my $nfound = select($rout=$rin, undef, undef, undef); + die("select failed: $!") if ($nfound < 0); + + if (vec($rout, fileno($stderr_read), 1) == 1) { + my $stderr = ''; + while(<$stderr_read>) { + $stderr .= $_; + } + + die(user_message(__x("Unexpected error getting {path}: ". + "{output}", + path => $path, output => $stderr))); + } + + if (vec($rout, fileno($stdout_read), 1) == 1) { + last; + } + } + + # First line returned is the output of stat + my $size = <$stdout_read>; + + return ($pid, $size, $stdout_read, $stderr_read); +} + +=back + +=head1 COPYRIGHT + +Copyright (C) 2010 Red Hat Inc. + +=head1 LICENSE + +Please see the file COPYING.LIB for the full license. + +=head1 SEE ALSO + +L<virt-v2v(1)>, +L<http://libguestfs.org/>. + +=cut + +1; diff --git a/v2v/virt-v2v.pl b/v2v/virt-v2v.pl index 7d30856..3dd1b74 100755 --- a/v2v/virt-v2v.pl +++ b/v2v/virt-v2v.pl @@ -96,6 +96,10 @@ my $input_uri = "qemu:///system"; Specifies the connection to use when using the libvirt input method. If omitted, this defaults to qemu:///system. +B<N.B.> virt-v2v can currently automatically obtain guest storage from local +libvirt connections, ESX connections, and connections over SSH. Other types of +connection are not supported. + =cut my $input_transport; @@ -429,14 +433,11 @@ sub inspect_guest =head1 PREPARING TO CONVERT A GUEST -=head2 Xen guests - -The following steps are required before converting a Xen guest. Note that only -local Xen guests are currently supported. These steps are not required for -conversions from ESX, and will not be required for remote Xen guests when we -support that. +=head2 Local Xen guests -=head3 Obtain domain XML for the guest domain +B<N.B.> The following is required when converting guests on a host which used to +run Xen, but has been updated to run KVM. It is not required when converting a +Xen guest imported directly from a running libvirt/Xen instance. virt-v2v uses a libvirt domain description to determine the current configuration of the guest, including the location of its storage. This should -- 1.6.6.1
On Fri, Apr 09, 2010 at 06:07:04PM +0100, Matthew Booth wrote:
Support obtaining guest storage over ssh. The following now works:
Looks very similar to what we do in libvirt. ACK. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones virt-df lists disk usage of guests without needing to install any software inside the virtual machine. Supports Linux and Windows. http://et.redhat.com/~rjones/virt-df/
participants (2)
-
Matthew Booth -
Richard W.M. Jones