[PATCH 1/2] Refactor guest and volume creation into Sys::VirtV2V::Target::LibVirt
by Matthew Booth
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
14 years, 5 months
[PATCH] Remove v2v-snapshot
by Matthew Booth
We now always copy a guest during conversion, meaning this tool is no longer
required.
---
Build.PL | 2 +-
MANIFEST | 2 -
lib/Sys/VirtV2V.pm | 9 +-
lib/Sys/VirtV2V/Connection.pm | 1 -
po/POTFILES.in | 1 -
snapshot/run-snapshot-locally | 43 --
snapshot/v2v-snapshot.pl | 931 -----------------------------------------
v2v/virt-v2v.conf | 8 +-
8 files changed, 9 insertions(+), 988 deletions(-)
delete mode 100755 snapshot/run-snapshot-locally
delete mode 100755 snapshot/v2v-snapshot.pl
diff --git a/Build.PL b/Build.PL
index 51afae8..f5e6e7c 100644
--- a/Build.PL
+++ b/Build.PL
@@ -217,7 +217,7 @@ my $build = $class->new (
dist_version_from => 'lib/Sys/VirtV2V.pm',
confdoc_files => [ 'v2v/virt-v2v.conf.pod' ],
install_path => { 'locale' => '/usr/local/share/locale' },
- script_files => [ 'snapshot/v2v-snapshot.pl', 'v2v/virt-v2v.pl' ],
+ script_files => [ 'v2v/virt-v2v.pl' ],
meta_add => {
resources => {
license => "http://www.gnu.org/licenses/gpl.html",
diff --git a/MANIFEST b/MANIFEST
index d5debe1..d200497 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -31,8 +31,6 @@ po/te.po
po/virt-v2v.pot
po/zh_CN.po
README-NLS
-snapshot/run-snapshot-locally
-snapshot/v2v-snapshot.pl
t/001-pod.t
t/002-pod-coverage.t
t/003-syntax.t
diff --git a/lib/Sys/VirtV2V.pm b/lib/Sys/VirtV2V.pm
index a831972..3e39d64 100644
--- a/lib/Sys/VirtV2V.pm
+++ b/lib/Sys/VirtV2V.pm
@@ -30,8 +30,8 @@ Sys::VirtV2V - Convert a virtual guest to use KVM
=head1 DESCRIPTION
-Modules under Sys::VirtV2V are used by the L<virt-v2v(1)> and L<v2v-snapshot(1)>
-tools. See the documentation for those tools for a more detailed description.
+Modules under Sys::VirtV2V are used by the L<virt-v2v(1)> tool. See the
+virt-v2v documentation for a more detailed description.
The Sys::VirtV2V module provides package information for virt-v2v.
@@ -60,11 +60,6 @@ Please see the file COPYING.LIB for the full license.
=head1 SEE ALSO
L<virt-v2v(1)>,
-L<v2v-snapshot(1)>,
-L<Sys::VirtV2V::GuestOS(3pm)>,
-L<Sys::VirtV2V::HVSource(3pm)>,
-L<Sys::VirtV2V::Converter(3pm)>,
-L<Sys::VirtV2V::Connection(3pm)>,
L<http://libguestfs.org/>
=cut
diff --git a/lib/Sys/VirtV2V/Connection.pm b/lib/Sys/VirtV2V/Connection.pm
index 5b4ed8d..a211662 100644
--- a/lib/Sys/VirtV2V/Connection.pm
+++ b/lib/Sys/VirtV2V/Connection.pm
@@ -195,7 +195,6 @@ Please see the file COPYING.LIB for the full license.
L<Sys::VirtV2V::Connection::LibVirt(3pm)>,
L<Sys::VirtV2V::Connection::LibVirtXML(3pm)>,
L<virt-v2v(1)>,
-L<v2v-snapshot(1)>,
L<http://libguestfs.org/>.
=cut
diff --git a/po/POTFILES.in b/po/POTFILES.in
index ffdf250..d5c774a 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -10,5 +10,4 @@
../lib/Sys/VirtV2V/Target/LibVirt.pm
../lib/Sys/VirtV2V/Transfer/ESX.pm
../lib/Sys/VirtV2V/UserMessage.pm
-../snapshot/v2v-snapshot.pl
../v2v/virt-v2v.pl
diff --git a/snapshot/run-snapshot-locally b/snapshot/run-snapshot-locally
deleted file mode 100755
index 9fd1c06..0000000
--- a/snapshot/run-snapshot-locally
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/bin/sh
-# v2v-snapshot
-# Copyright (C) 2009 Red Hat Inc.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program 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 General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-# This script sets up the environment so you can run v2v-snapshot in place
-# without needing to do 'make install' first.
-#
-# Use it like this:
-# ./run-snapshot-locally [usual v2v-snapshot args ...]
-#
-# It requires the environment variable VIRTV2V_ROOT to be set. If using
-# libguestfs from source, LIBGUESTFS_ROOT must also be set.
-
-if [ -z "$VIRTV2V_ROOT" ]; then
- echo "VIRTV2V_ROOT must be set"
- exit 1
-fi
-
-PERL5LIB="$VIRTV2V_ROOT/blib/lib"
-
-if [ ! -z "$LIBGUESTFS_ROOT" ]; then
- LD_LIBRARY_PATH="$LIBGUESTFS_ROOT/src/.libs"
- LIBGUESTFS_PATH="$LIBGUESTFS_ROOT/appliance"
- PERL5LIB="$PERL5LIB:$LIBGUESTFS_ROOT/perl/blib/lib:$LIBGUESTFS_ROOT/perl/blib/arch"
-fi
-
-export PERL5LIB LD_LIBRARY_PATH LIBGUESTFS_PATH
-
-exec perl "$VIRTV2V_ROOT/snapshot/v2v-snapshot.pl" "$@"
diff --git a/snapshot/v2v-snapshot.pl b/snapshot/v2v-snapshot.pl
deleted file mode 100755
index ccbc907..0000000
--- a/snapshot/v2v-snapshot.pl
+++ /dev/null
@@ -1,931 +0,0 @@
-#!/usr/bin/perl
-# v2v-snapshot
-# Copyright (C) 2009 Red Hat Inc.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program 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 General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-use warnings;
-use strict;
-
-use English;
-
-use File::Temp qw(tempfile);
-use Getopt::Long;
-use Pod::Usage;
-
-use Locale::TextDomain 'virt-v2v';
-
-use Sys::Virt;
-
-use Sys::VirtV2V;
-use Sys::VirtV2V::ExecHelper;
-use Sys::VirtV2V::Connection;
-use Sys::VirtV2V::UserMessage qw(user_message);
-
-=encoding utf8
-
-=head1 NAME
-
-v2v-snapshot - Convert a guest to use a qcow2 snapshot for storage
-
-=head1 SYNOPSIS
-
- v2v-snapshot guest-domain
-
- v2v-snapshot --rollback guest-domain
-
- v2v-snapshot --commit guest-domain
-
-=head1 DESCRIPTION
-
-v2v-snapshot is a tool for creating a local snapshot of a guest's storage. It is
-suitable for use when making a change to a guest which might have to be rolled
-back. v2v-snapshot creates a qcow2 snapshot for all a guest's block devices and
-updates the guest's libvirt domain to use them.
-
-When a change has been tested, v2v-snapshot can either commit the change, which
-will update the original storage with the changes made to the snapshot, or
-rollback the change, which will remove the snapshot. In either case, the guest
-will be updated again to use its original storage.
-
-B<v2v-snapshot can only snapshot a guest which is shutdown. It is not intended
-as a long-term storage option, and is not a general snapshotting tool for
-guests.>
-
-=head1 OPTIONS
-
-=over 4
-
-=cut
-
-my $help;
-
-=item B<--help>
-
-Display brief help.
-
-=cut
-
-my $version;
-
-=item B<--version>
-
-Display version number and exit.
-
-=cut
-
-my $uri;
-
-=item B<--connect URI> | B<-c URI>
-
-Connect to libvirt using the given I<URI>. If omitted, then we connect to the
-default libvirt hypervisor.
-
-=cut
-
-my $input = "libvirt";
-
-=item B<--input input> | B<-i input>
-
-The specified guest description uses the given I<input format>. The default is
-C<libvirt>. Supported options are:
-
-=over
-
-=item I<libvirt>
-
-Guest argument is the name of a libvirt domain.
-
-=item I<libvirtxml>
-
-Guest argument is the path to an XML file describing a libvirt domain.
-
-=back
-
-=cut
-
-my $outputxml;
-
-=item B<--outputxml file> | B<-o file>
-
-Write the updated domain xml to I<file> instead of directly creating the domain.
-If I<file> is '-' output will be to standard out.
-
-This option can be useful when snapshotting or rolling back to domain XML which
-can't be created on the local hypervisor, for example because Xen is not
-available locally.
-
-=cut
-
-my $datadir;
-
-=item B<--datadir dir> | B<-d dir>
-
-The directory v2v-snapshot will store its data. 2 subdirectories will be created
-for holding snapshot images and XML backups. It defaults to
-I</var/lib/virt-v2v>, except for connections to qemu:///session, when it
-defaults to ~/.virt-v2v.
-
-=cut
-
-my $force = 0;
-
-=item B<--force> | B<-f>
-
-Force an action to complete which might not be safe. Force is required to:
-
-=over
-
-Create a snapshot of a guest which already has an active snapshot (overwrites
-the XML backup).
-
-Rollback a guest which has no XML backup (all guest metadata is lost).
-
-=back
-
-=cut
-
-my $commit = 0;
-
-=item B<--commit>
-
-Commit the existing snapshot to its backing store and update the guest to using
-the original storage.
-
-=cut
-
-my $rollback = 0;
-
-=item B<--rollback>
-
-Remove the snapshot and restore the guest to its previous, unmodified storage.
-
-=back
-
-=cut
-
-# Initialise the message output prefix
-Sys::VirtV2V::UserMessage->set_identifier('v2v-snapshot');
-
-GetOptions ("help|?" => \$help,
- "version" => \$version,
- "connect|c=s" => \$uri,
- "input|i=s" => \$input,
- "outputxml|o=s" => \$outputxml,
- "datadir|d=s" => \$datadir,
- "force|f" => \$force,
- "commit" => \$commit,
- "rollback" => \$rollback
- ) or pod2usage(2);
-pod2usage(0) if($help);
-pod2usage({
- -message => __"--commit and --rollback options are mutually exclusive",
- -exitval => 1
-}) if($commit && $rollback);
-
-if ($version) {
- print "$Sys::VirtV2V::VERSION\n";
- exit(0);
-}
-
-pod2usage(user_message(__"no guest argument given")) if @ARGV == 0;
-
-# Get a libvirt connection
-my @vmm_params = (auth => 1);
-push(@vmm_params, uri => $uri) if(defined($uri));
-my $vmm = Sys::Virt->new(@vmm_params);
-
-# Set the default datadir depending on the connection
-if (!defined($datadir)) {
- if ($vmm->get_uri() eq "qemu:///session") {
- # Get the current user's home directory
- my (undef, undef, undef, undef, undef, # name, passwd, uid, gid, quota
- undef, undef, $home, undef, undef # comment, gcos, dir, shell, expire
- ) = getpwuid($UID);
-
- unless (defined($home)) {
- print STDERR user_message(__x("Unable to get home directory ".
- "for current user"));
- exit(1);
- }
-
- $datadir = File::Spec->catdir($home, ".virt-v2v");
- }
-
- else {
- $datadir = "/var/lib/virt-v2v";
- }
-
- unless (-d $datadir) {
- unless(mkdir($datadir)) {
- print STDERR user_message(__x("Unable to create data ".
- "directory {dir}: {error}",
- dir => $datadir,
- error => $@));
- exit(1);
- }
- }
-}
-
-# 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));
- exit(1);
-}
-
-exit(1) unless($mdr->is_configured());
-
-###############################################################################
-## Start of processing
-
-# Get a libvirt configuration for the guest
-my $dom = $mdr->get_dom();
-exit(1) unless(defined($dom));
-
-my $pool = _get_pool($vmm);
-
-if($commit) {
- _commit_guest($dom, $vmm, $pool) == 0 or exit(1);
-}
-
-elsif($rollback) {
- my $retval;
- ($retval, $dom) = _rollback_guest($dom, $vmm, $pool);
-
- exit(1) unless($retval == 0);
-}
-
-# No commit or rollback. Snapshot the guest
-else {
- _snapshot_guest($dom, $vmm, $pool) == 0 or exit(1);
-}
-
-# Don't try to output anything if the domain is no longer defined
-if(!defined($dom)) {
- print user_message(__"No domain has been created.");
- exit($force == 1 ? 0 : 1);
-}
-
-# If --outputxml was given, just write the xml instead of creating the domain
-if($outputxml) {
- my $out;
- my $error = 0;
-
- # Write output to a file
- if('-' ne $outputxml) {
- unless(open($out, '>', $outputxml)) {
- print STDERR user_message(__x("Unable to open {file}: {error}",
- file => $outputxml,
- error => $!));
- ($out, $outputxml) = tempfile(_get_guest_name($dom).'-XXXXXX',
- SUFFIX => '.xml');
- $error = 1;
- }
-
- print $out $dom->toString();
-
- close($out) or die(__x("Error closing {file}: {error}",
- file => $outputxml, error => $!));
-
- if($error) {
- print STDERR user_message(__x("Wrote output to {file}",
- file => $outputxml));
- exit(1);
- }
- }
-
- # Write output to STDOUT
- else {
- print $dom->toString();
- }
-}
-
-else {
- eval {
- $vmm->define_domain($dom->toString());
- };
-
- if($@) {
- print STDERR user_message(__x("Unable to create guest: {error}",
- error => $@->stringify()));
- print STDERR user_message(__"Consider using the --outputxml option");
-
- # Write the output to a temporary file
- my ($out, $outputxml) = tempfile(_get_guest_name($dom).'-XXXXXX',
- SUFFIX => '.xml');
-
- print $out $dom->toString();
-
- print STDERR user_message(__x("Wrote output to {file}",
- file => $outputxml));
-
- unless(close($out)) {
- print STDERR user_message(__x("Error closing {file}: {error}",
- file => $outputxml, error => $!));
- }
-
- exit(1);
- }
-}
-
-###############################################################################
-## Helper functions
-
-sub _get_pool
-{
- my ($vmm) = @_;
-
- # Look for the v2v-snapshot storage pool
- my $pool;
- eval {
- $pool = $vmm->get_storage_pool_by_name('v2v-snapshot');
- };
-
- # If it wasn't there, try creating it
- if($@) {
- my $snapshotdir = $datadir.'/snapshots';
-
- unless (-d $snapshotdir) {
- unless(mkdir($snapshotdir)) {
- print STDERR user_message(__x("Unable to create snapshot ".
- "directory {dir}: {error}",
- dir => $snapshotdir,
- error => $@));
- exit(1);
- }
- }
-
- eval {
- $pool = $vmm->create_storage_pool("
- <pool type='dir'>
- <name>v2v-snapshot</name>
- <target>
- <path>$snapshotdir</path>
- </target>
- </pool>
- ");
- };
-
- # If that didn't work, give up
- if($@) {
- print STDERR user_message(__x("Unable to create v2v-snapshot ".
- "storage pool: {error}",
- error => $@->stringify()))
- unless(defined($pool));
- exit(1);
- }
- }
-
- # Check that the pool is usable
- my $pool_info = $pool->get_info();
-
- # If it's inactive, start it
- if($pool_info->{state} == Sys::Virt::StoragePool::STATE_INACTIVE) {
- eval {
- $pool->create();
- };
-
- if($@) {
- print STDERR user_message(__x("Unable to start v2v-snapshot ".
- "storage pool: {error}",
- error => $@->stringify()))
- unless(defined($pool));
- exit(1);
- }
- }
-
- # If it's building, there's nothing to do but wait
- elsif($pool_info->{state} == Sys::Virt::StoragePool::STATE_BUILDING) {
- print STDERR user_message(__"v2v-snapshot storage pool is ".
- "temporarily unavailable");
- exit(1);
- }
-
- return $pool;
-}
-
-# Get a storage volume object for a given path
-sub _get_volume
-{
- my ($path, $pool) = @_;
-
- my $vol;
- my $refreshed = 0;
- do {
- # XXX: Shouldn't be using an undocumented API
- # See RHBZ 519647. Replace with lookupByPath when it's available.
- eval {
- $vol = Sys::Virt::StorageVol->_new(path => $path,
- connection => $vmm);
- };
-
- if($@) {
- if($refreshed) {
- my $pool_xml = $pool->get_xml_description();
- my $pool_dom = new XML::DOM::Parser->parse($pool_xml);
-
- die(user_message(__x("Unable to find {path} in the ".
- "v2v-snapshot storage pool.",
- path => $path)));
- } else {
- $pool->refresh(0);
- $refreshed = 1;
- }
- }
- } until(defined($vol));
-
- return $vol;
-}
-
-sub _commit_guest
-{
- my ($dom, $vmm, $pool) = @_;
-
- # First, get a list of existing disks
- foreach my $disk ($dom->findnodes('/domain/devices/disk')) {
- my ($source) = $disk->findnodes('source');
- my ($target) = $disk->findnodes('target/@dev');
-
- # Look for the source location
- my $path;
- my $src_attrs = $source->getAttributes();
- foreach my $attr qw(dev file) {
- my $item = $src_attrs->getNamedItem($attr);
- if(defined($item)) {
- $path = $item->getNodeValue();
-
- # Remove the attribute. We'll add a new one in below.
- $src_attrs->removeNamedItem($attr);
-
- last;
- }
- }
-
- # Find the storage volume, which will include information on the backing
- # store
- my $vol;
- eval {
- $vol = _get_volume($path, $pool);
- };
- if($@) {
- print STDERR $@;
- return -1;
- }
-
- # Get the volume's backing store
- my $vol_xml = $vol->get_xml_description();
- my $vol_dom = new XML::DOM::Parser->parse($vol_xml);
- my ($backing_store) = $vol_dom->findnodes('/volume/backingStore');
-
- # Skip it if it doesn't have a backing store
- unless($backing_store) {
- print STDERR user_message(__x("Skipping device {target} as it ".
- "doesn't have a backing store",
- target => $target->getNodeValue()));
- next;
- }
-
- my ($backing_path) = $backing_store->findnodes('path/text()');
- $backing_path = $backing_path->getNodeValue();
- my ($backing_format) = $backing_store->findnodes('format/@type');
- $backing_format = $backing_format->getNodeValue();
-
- # Try to work out if the backing store is a file or a block device by
- # interrogating its storage volume object
- my $backing_type;
- eval {
- # XXX: See comment above about this usage of _new
- my $backing_vol = Sys::Virt::StorageVol->_new(path => $path,
- connection => $vmm);
- $backing_type = $backing_vol->get_info()->{type};
- };
-
- # Backing store isn't in a storage pool
- if($@) {
- # Guess based on path name
- # N.B. We could stat it, but that wouldn't work for a remote
- # connection
- if($backing_path =~ m{^/dev/}) {
- $backing_type = Sys::Virt::StorageVol::TYPE_BLOCK;
- } else {
- $backing_type = Sys::Virt::StorageVol::TYPE_FILE;
- }
- }
-
- # Update the domain XML with the location of the backing store
- if($backing_type == Sys::Virt::StorageVol::TYPE_BLOCK) {
- $source->setAttribute('dev', $backing_path);
- $source->removeAttribute('file');
- } else {
- $source->setAttribute('file', $backing_path);
- $source->removeAttribute('dev');
- }
-
- # Update the domain XML with with a driver appropriate to the backing
- # store
- my ($driver) = $disk->findnodes('driver');
-
- # Initialise the driver if it's not set
- $driver ||= $disk->appendChild($dom->createElement('driver'));
-
- $driver->setAttribute('name', 'qemu');
- $driver->setAttribute('type', $backing_format);
-
- # Commit snapshot to its backing store
- # XXX: There should be a libvirt API to do this
- my $eh = Sys::VirtV2V::ExecHelper->run
- ('/usr/bin/qemu-img', 'commit', '-f', 'qcow2', $path);
-
- # Check commit succeeded
- if($eh->status != 0) {
- print STDERR user_message(__x("Failed to commit snapshot '{path}' ".
- "to backing store '{backingstore}'.".
- "\nCommand output was:\n{output}",
- path => $path,
- backingstore => $backing_path,
- output => $eh->output()));
-
- return -1;
- }
-
- # Delete the snapshot volume
- $vol->delete(0);
- }
-
- # Remove the XML backup if it exists
- my $xmlpath = _get_xml_path($dom);
- unlink($xmlpath) if(-e $xmlpath);
-
- return 0;
-}
-
-sub _snapshot_guest
-{
- my ($dom, $vmm, $pool) = @_;
-
- my $name = _get_guest_name($dom);
-
- # Store a backup of the domain XML before modification
- my $xmlpath = _get_xml_path($dom);
-
- # Error if the xml backup already exists and force not specified
- if(-e $xmlpath && !$force) {
- print STDERR
- user_message(__x("A snapshot already exists for {guest}. You must ".
- "commit it or roll it back back before creating ".
- "a new snapshot.", guest => $name));
- return -1;
- }
-
- # Keep a list files and volumes created by the snapshot process. We need to
- # clean all of these up in the event of an error.
- my @created_files = ();
- my @created_volumes = ();
-
- my $ret = eval {
- # Write the backup
- my $xmlbackup;
- open($xmlbackup, '>', $xmlpath)
- or die(__x("Unable to write to {path}: {error}",
- path => $xmlpath, error => $!));
- push(@created_files, $xmlpath);
-
- print $xmlbackup $dom->toString();
-
- close($xmlbackup)
- or die(__x("Error closing {path}: {error}",
- path => $xmlpath, error => $!));
-
- # Get a timestamp for use in naming snapshot volumes
- my $time = time();
-
- return _foreach_disk($dom, sub {
- my ($disk, $source, $target, $path) = @_;
-
- # Create a new qcow2 volume in the v2v-snapshot storage pool
- my $target_name = $target->getNodeValue();
- my $vol_name = "$name-$target_name-$time.qcow2";
- my $vol_xml = "
- <volume>
- <name>$vol_name</name>
- <capacity>0</capacity>
- <target>
- <format type='qcow2'/>
- </target>
- <backingStore>
- <path>$path</path>
- </backingStore>
- </volume>
- ";
-
- my $vol;
- eval {
- $vol = $pool->create_volume($vol_xml);
- };
-
- if($@) {
- print STDERR
- user_message(__x("Unable to create storage volume: {error}",
- error => $@->stringify()));
- return -1;
- }
-
- push(@created_volumes, $vol);
-
- # Update the source to be a "file" with the new path
- $source->setAttribute("file", $vol->get_path());
-
- # Remove the dev attribute in case it was set
- $source->removeAttribute("dev");
-
- # Also update the disk element to be a "file"
- $source->getParentNode()->setAttribute('type', 'file');
-
- # Replace the driver element with one which describes the qcow2 file
- my ($driver) = $disk->findnodes('driver');
-
- # Initialise the driver if it's not set
- $driver ||= $disk->appendChild($dom->createElement('driver'));
-
- $driver->setAttribute('name', 'qemu');
- $driver->setAttribute('type', 'qcow2');
-
- return 0;
- });
- };
-
- if ($@ || $ret < 0) {
- foreach my $file (@created_files) {
- unlink($file); # Don't check for further errors
- }
-
- foreach my $vol (@created_volumes) {
- $vol->delete();
- }
-
- die($@) if ($@);
- }
-
- return $ret;
-}
-
-sub _rollback_guest
-{
- my ($dom, $vmm, $pool) = @_;
-
- my $name = _get_guest_name($dom);
- my $xmlpath = _get_xml_path($dom);
-
- # Only rollback a guest without stored XML if force was specified
- if(! -e $xmlpath && !$force) {
- print STDERR user_message(__x("Refusing to rollback guest {name} ".
- "without backed-up xml",
- name => _get_guest_name($dom)));
- return -1;
- }
-
- # Delete all snapshots
- _foreach_disk($dom, sub {
- my ($disk, $source, $target, $path) = @_;
-
- # Find the storage volume, which will include information on the backing
- # store
- my $vol;
- eval {
- $vol = _get_volume($path, $pool);
- };
- if($@) {
- print STDERR $@;
- return -1;
- }
-
- # Check that the volume has a backing store
- my $vol_xml = $vol->get_xml_description();
- my $vol_dom = new XML::DOM::Parser->parse($vol_xml);
- my ($backing_store) = $vol_dom->findnodes('/volume/backingStore');
-
- unless(defined($backing_store)) {
- print STDERR user_message(__x("{path} is not a snapshot volume",
- path => $path));
- return -1;
- }
-
- if(-e $path && unlink($path) != 1) {
- print STDERR user_message(__x("Failed to delete {file}: {error}",
- file => $path, error => $!));
- return -1;
- }
-
- return 0;
- }) == 0 or return -1;
-
- # Load the backed-up XML
- if(-e $xmlpath) {
- $dom = new XML::DOM::Parser->parsefile($xmlpath);
- if(unlink($xmlpath) != 1) {
- print STDERR user_message(__x("Unable to delete backup xml file ".
- "{file}: {error}",
- file => $xmlpath, error => $!));
- return -1;
- }
- }
-
- # Or there is no backed-up XML (force must have been given)
- else {
- $dom = undef;
- }
-
- # undefined the guest if it is defined
- my $domain;
- eval {
- $domain = $vmm->get_domain_by_name($name);
- };
-
- # Nothing to do if the guest isn't defined
- if(defined($domain)) {
- my $state = $domain->get_info()->{state};
-
- # Check the domain is shutoff
- unless($state == Sys::Virt::Domain::STATE_SHUTOFF) {
- $domain->destroy();
-
- # $domain is now undefined. Get it back again
- $domain = $vmm->get_domain_by_name($name);
- }
-
- $domain->undefine();
- }
-
- return (0, $dom);
-}
-
-sub _foreach_disk
-{
- my ($dom, $func) = @_;
-
- # Get a list of existing disks
- foreach my $disk ($dom->findnodes('/domain/devices/disk[@device = \'disk\']'))
- {
- my ($source) = $disk->findnodes('source');
- my ($target) = $disk->findnodes('target/@dev');
-
- # Ignore disks with no source
- next if (!defined($source));
-
- # Look for the source location
- my $path;
- my $src_attrs = $source->getAttributes();
- foreach my $attr qw(dev file) {
- my $item = $src_attrs->getNamedItem($attr);
- if(defined($item)) {
- $path = $item->getNodeValue();
- last;
- }
- }
-
- # Warn and ignore this source if we didn't find either
- if(!defined($path)) {
- print STDERR user_message(__x("invalid source element: {element}",
- element => $source->toString()));
- next;
- }
-
- $func->($disk, $source, $target, $path) == 0 or return -1;
- }
-}
-
-sub _get_guest_name
-{
- my ($dom) = @_;
-
- # Get the name of the guest from the domain description
- my ($name_elem) = $dom->findnodes('/domain/name/text()');
- return $name_elem->toString();
-}
-
-sub _get_xml_path
-{
- my ($dom) = @_;
-
- my $xmldir = $datadir.'/xml';
-
- # Ensure it exists
- unless (-d $xmldir) {
- unless (mkdir($xmldir)) {
- print STDERR user_message(__x("Unable to create xml storage ".
- "directory {dir}: {error}",
- dir => $xmldir, error => $@));
- exit(1);
- }
- }
-
- return $xmldir.'/'._get_guest_name($dom).'.xml';
-}
-
-=head1 EXAMPLES
-
-=head2 Snapshot a local guest
-
-=over
-
-This example covers snapshotting a guest which is available through libvirt on
-the local machine. The guest domain's name is I<E<lt>guestE<gt>>.
-
-First ensure the guest is shutdown:
-
- # virsh destroy <guest>
-
-Snapshot the guest:
-
- # v2v-snapshot <guest>
-
-Start the guest, make some changes, modify hardware, test changes. To commit
-the changes:
-
- # v2v-snapshot --commit <guest>
-
-Alternatively, to rollback the changes:
-
- # v2v-snapshot --rollback <guest>
-
-=back
-
-=head2 Snapshot an imported guest
-
-=over
-
-This example covers snapshotting a guest which has been copied from another
-machine. For this to work you must have the domain XML exported from the origin
-machine:
-
- (Origin) # virsh dumpxml <guest> > guest.xml
-
-You must also present all of the guest's disk images to the local machine in the
-locations specified in guest.xml. If copying the images, the easiest
-way to achieve this is to copy it to the same path on the local machine as it
-had on the origin machine. If the storage can be remotely mounted, it should be
-presented to the local machine with the same paths as on the origin machine. If
-the location of the storage must change, you must manually edit guest.xml to
-reflect the new paths.
-
-If it is possible to create the domain on the local machine:
-
- # v2v-snapshot -i libvirtxml guest.xml
-
-This command will create the imported domain locally with snapshot storage.
-
-If it is not possible to create the domain locally, for example because it is a
-Xen domain and the local libvirt cannot manage Xen domains:
-
- # v2v-snapshot -o snapshot-guest.xml -i libvirtxml guest.xml
-
-This command does not attempt the create the domain locally, which would fail.
-Instead it writes updated domain XML to I<snapshot-guest.xml>. The disks in
-snapshot-guest.xml point to the newly created snapshot volumes.
-
-The latter method of import is intended for use when importing a Xen domain from
-a origin machine for conversion with L<virt-v2v(1)>. virt-v2v should be given
-I<snapshot-guest.xml> as the domain XML.
-
-=back
-
-=head1 SEE ALSO
-
-L<virt-v2v(1)>,
-L<http://libguestfs.org/>.
-
-=head1 AUTHOR
-
-Matthew Booth <mbooth(a)redhat.com>
-
-=head1 COPYRIGHT
-
-Copyright (C) 2009 Red Hat Inc.
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2 of the License, or
-(at your option) any later version.
-
-This program 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 General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
diff --git a/v2v/virt-v2v.conf b/v2v/virt-v2v.conf
index e88d8e7..31f272e 100644
--- a/v2v/virt-v2v.conf
+++ b/v2v/virt-v2v.conf
@@ -51,23 +51,28 @@
<!-- Networks -->
<!-- The default Xen bridge name -->
+ <!--
<network type='bridge' name='xenbr1'>
<network type='network' name='default'/>
</network>
+ -->
<!-- The default ESX bridge name -->
+ <!--
<network type='bridge' name='VM Network'>
<network type='network' name='default'/>
</network>
+ -->
<!-- The default libvirt network name -->
+ <!--
<network type='network' name='network'>
<network type='network' name='default'/>
</network>
+ -->
<!-- If importing to RHEV, you may want to use the default network name
'rhevm' instead -->
- <!--
<network type='bridge' name='xenbr1'>
<network type='network' name='rhevm'/>
</network>
@@ -79,5 +84,4 @@
<network type='network' name='network'>
<network type='network' name='rhevm'/>
</network>
- -->
</virt-v2v>
--
1.6.6.1
14 years, 5 months
[PATCH] Documentation: Update virt-v2v pod for RHEV export and IDE default
by Matthew Booth
---
v2v/virt-v2v.pl | 140 ++++++++++++++++++++++++++++++++++++++++---------------
1 files changed, 102 insertions(+), 38 deletions(-)
diff --git a/v2v/virt-v2v.pl b/v2v/virt-v2v.pl
index c1a4728..3559738 100755
--- a/v2v/virt-v2v.pl
+++ b/v2v/virt-v2v.pl
@@ -21,7 +21,6 @@ use strict;
use Pod::Usage;
use Getopt::Long;
-#use Data::Dumper;
use File::Spec;
use File::stat;
@@ -50,16 +49,19 @@ virt-v2v - Convert a guest to use KVM
=head1 SYNOPSIS
- virt-v2v -f virt-v2v.conf -i libvirtxml guest-domain.xml
+ virt-v2v -f virt-v2v.conf -i libvirtxml -op transfer guest-domain.xml
- virt-v2v -f virt-v2v.conf -ic esx://esx.server/ -op transfer guest-domain
+ virt-v2v -f virt-v2v.conf -ic esx://esx.server/ -op transfer esx_guest
+
+ virt-v2v -f virt-v2v.conf -ic esx://esx.server/ \
+ -o rhev -osd rhev.nfs.storage:/export_domain guest esx_guest
=head1 DESCRIPTION
virt-v2v converts guests from a foreign hypervisor to run on KVM, managed by
-libvirt. It can currently convert Red Hat Enterprise Linux and Fedora guests
-running on Xen and VMware ESX. It will enable VirtIO drivers in the converted
-guest if possible.
+libvirt or Red Hat Enterprise Virtualisation (RHEV) version 2.2 or later. It can
+currently convert Red Hat Enterprise Linux and Fedora guests running on Xen and
+VMware ESX. It will enable VirtIO drivers in the converted guest if possible.
=head1 OPTIONS
@@ -116,7 +118,13 @@ Specifies the output method. Supported output methods are:
=item libvirt
-Create a libvirt guest. See the I<-oc> and I<-op> options.
+Create a libvirt guest. See the I<-oc> and I<-op> options. I<-op> must be
+specified for the libvirt output method.
+
+=item rhev
+
+Create a guest on a RHEV 'Export' storage domain, which can later be imported
+into RHEV using the UI. I<-osd> must be specified for the rhev output method.
=back
@@ -478,14 +486,8 @@ sub inspect_guest
my %fses =
inspect_all_partitions ($g, \@partitions);
- #print "fses -----------\n";
- #print Dumper(\%fses);
-
my $oses = inspect_operating_systems ($g, \%fses);
- #print "oses -----------\n";
- #print Dumper($oses);
-
# Only work on single-root operating systems.
my $root_dev;
my @roots = keys %$oses;
@@ -522,17 +524,6 @@ 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.
-=head3 Backup the guest
-
-If converting a local guest using the libvirtxml input method, the guest will be
-converted in place: it will make changes to a guest directly without creating a
-backup. It is recommended that virt-v2v be run against a copy.
-
-The L<v2v-snapshot(1)> tool can be used to convert a guest to use a snapshot
-for storage prior to running virt-v2v against it. This snapshot can then be
-committed to the original storage after the conversion is confirmed as
-successful.
-
=head3 Obtain domain XML for the guest domain
virt-v2v uses a libvirt domain description to determine the current
@@ -545,19 +536,18 @@ This will require a reboot if the host running Xen is the same host that will
run KVM. This is because libvirt needs to connect to a running xen hypervisor to
obtain its metadata.
-=head2 ESX guests
+=head2 Converting to run on libvirt/KVM
=head3 Create a local storage pool for transferred storage
-virt-v2v copies the guest storage to the local machine during import from an ESX
-server. It creates new storage in a locally defined libvirt pool. This pool can
-be defined using any libvirt tool, and can be of any type.
+virt-v2v copies the guest storage to the local machine during import. When
+converting to run on libvirt, it creates new storage in a locally defined
+libvirt pool. This pool can be defined using any libvirt tool, and can be of any
+type.
The simplest way to create a new pool is with L<virt-manager(1)>. Pools can be
defined from the Storage tab under Host Details.
-=head2 All guests
-
=head3 Create local network interfaces
The local machine must have an appropriate network for the converted guest
@@ -566,6 +556,20 @@ be created using standard tools on the host.
Since version 0.8.3, L<virt-manager(1)> can also create and manage bridges.
+=head2 Converting to run on RHEV
+
+=head3 Create an NFS export domain
+
+virt-v2v can convert guests to run on RHEV 2.2 or later. It does this by writing
+the converted guest directly to an 'Export' NFS storage domain. The guest can
+later be imported into a RHEV Data Center through the UI.
+
+In RHEV 2.2, a new Export storage domain is created by clicking on 'New Domain'
+in the Storage tab. Ensure that the Domain function is 'Export' and the Storage
+type is 'NFS'. See the RHEV documentation for details. The NFS storage domain must be mountable by the machine running virt-v2v.
+
+B<N.B.> When exporting to RHEV, virt-v2v must run as root.
+
=head1 CONVERTING A LOCAL XEN GUEST
The following requires that the domain XML is available locally, and that the
@@ -573,9 +577,11 @@ storage referred to in the domain XML is available locally at the same paths.
To perform the conversion, run:
- virt-v2v -f virt-v2v.conf -i libvirtxml <domain>.xml
+ virt-v2v -f virt-v2v.conf -i libvirtxml -op <pool> <domain>.xml
-where C<< <domain>.xml >> is the path to the exported guest domain's xml. virt-v2v.conf should specify:
+where C<< <domain>.xml >> is the path to the exported guest domain's xml, and
+C<< <pool> >> is the local storage pool where copies of the guest's disks will
+be created. virt-v2v.conf should specify:
=over
@@ -595,9 +601,9 @@ It is possible to avoid specifying replacement kernels in the virt-v2v config
file by ensuring that the guest has an appropriate kernel installed prior to
conversion. If your guest uses a Xen paravirtualised kernel (it would be called
something like kernel-xen or kernel-xenU), you can install a regular kernel,
-which won't reference a hypervisor in its name, alongside it. You shouldn't
-make this newly installed kernel your default kernel because the chances are Xen
-will not boot it. virt-v2v will make it the default during conversion.
+which won't reference a hypervisor in its name, alongside it. You shouldn't make
+this newly installed kernel your default kernel because Xen may not boot it.
+virt-v2v will make it the default during conversion.
=head2 CONVERTING A GUEST FROM VMWARE ESX
@@ -650,19 +656,78 @@ entry is:
=head3 Connecting to an ESX server with an invalid certificate
-In non-production environments, the ESX server may have a non-valid certificate,
+In non-production environments, the ESX server may have an invalid certificate,
for example a self-signed certificate. In this case, certificate checking can be
explicitly disabled by adding '?no_verify=1' to the connection URI as shown
below:
... -ic esx://<esx.server>/?no_verify=1 ...
+=head1 EXPORTING A GUEST TO RHEV
+
+virt-v2v can export to RHEV any guest that it can convert. This includes:
+
+=over
+
+=item *
+
+Local Xen guests
+
+=item *
+
+ESX guests
+
+=item *
+
+Local libvirt/KVM guests
+
+=back
+
+To export to RHEV, specify:
+
+ ... -o rhev -osd <export_sd> ...
+
+on the command line in place of I<-op> as in the following examples:
+
+=over
+
+=item Exporting a local Xen guest to RHEV
+
+virt-v2v -f virt-v2v.conf -i libvirtxml -o rhev -osd <export_sd> <domain>.xml
+
+=item Export a VMWare ESX guest to RHEV
+
+virt-v2v -f virt-v2v.conf -ic esx://<esx.server>/ -o rhev -osd <export_sd> <domain>
+
+=item Export a local libvirt/KVM guest to RHEV
+
+virt-v2v -f virt-v2v.conf -o rhev -osd <export_sd> <domain>
+
+=back
+
+Ensure that I<virt-v2v.conf> contains a correct network mapping for your target
+RHEV configuration.
+
=head1 RUNNING THE CONVERTED GUEST
+=head2 Libvirt output method
+
On successful completion, virt-v2v will create a new libvirt domain for the
converted guest with the same name as the original guest. It can be started as
usual using libvirt tools, for example L<virt-manager(1)>.
+=head2 RHEV output method
+
+On successful completion virt-v2v will have written the new guest to the export
+storage domain, but it will not yet be ready to run. It must be imported into
+RHEV using the UI before it can be used.
+
+In RHEV 2.2 this is done from the Storage tab. Select the export domain the
+guest was written to. A pane will appear underneath the storage domain list
+displaying several tabs, one of which is 'VM Import'. The converted guest will
+be listed here. Select the appropriate guest an click 'Import'. See the RHEV
+documentation for additional details.
+
=head1 POST-CONVERSION TASKS
=head2 Guest network configuration
@@ -729,13 +794,12 @@ Additionally, initrd will preload the virtio_pci driver.
=head2 Non-VirtIO
X display cirrus
- Block sym53c8xx (scsi)
+ Block IDE
Network e1000
=head1 SEE ALSO
L<virt-manager(1)>,
-L<v2v-snapshot(1)>,
L<http://libguestfs.org/>.
=head1 AUTHOR
--
1.6.6.1
14 years, 5 months
[PATCH] Default to IDE when VirtIO isn't available
by Matthew Booth
Previously we used SCSI when VirtIO wasn't available. KVM's SCSI support is not
as mature as its IDE support, and SCSI isn't supported at all in RHEV.
---
lib/Sys/VirtV2V/Converter.pm | 50 +++++++++++-------
lib/Sys/VirtV2V/Converter/Linux.pm | 19 ++++---
lib/Sys/VirtV2V/GuestOS/RedHat.pm | 100 ++++++++++++++++++++----------------
3 files changed, 100 insertions(+), 69 deletions(-)
diff --git a/lib/Sys/VirtV2V/Converter.pm b/lib/Sys/VirtV2V/Converter.pm
index 71e94f7..5dc8550 100644
--- a/lib/Sys/VirtV2V/Converter.pm
+++ b/lib/Sys/VirtV2V/Converter.pm
@@ -87,7 +87,7 @@ use constant KVM_XML_NOVIRTIO => "
</os>
<devices>
<disk device='disk'>
- <target bus='scsi'/>
+ <target bus='ide'/>
</disk>
<interface type='network'>
<model type='e1000'/>
@@ -268,51 +268,65 @@ sub _unconfigure_bootloaders
}
}
+sub _suffixcmp
+{
+ my ($a, $b) = @_;
+
+ return 1 if (length($a) > length($b));
+ return -1 if (length($a) < length($b));
+
+ return 1 if ($a gt $b);
+ return -1 if ($a lt $b);
+ return 0;
+}
+
sub _configure_storage
{
my ($dom, $devices, $virtio) = @_;
- my $prefix = $virtio ? 'vd' : 'sd';
+ my $prefix = $virtio ? 'vd' : 'hd';
+
+ my @removed = ();
my $suffix = 'a';
foreach my $device (@$devices) {
my ($target) = $dom->findnodes("/domain/devices/disk[\@device='disk']/".
"target[\@dev='$device']");
- die(user_message(__x("Previously detected drive {drive} is no longer ".
- "present in domain XML: {xml}",
- drive => $device,
- xml => $dom->toString())))
+ die("Previously detected drive $device is no longer present in domain ".
+ "XML: ".$dom->toString())
unless (defined($target));
- $target->setAttribute('bus', $virtio ? 'virtio' : 'scsi');
- $target->setAttribute('dev', $prefix.$suffix);
- $suffix++; # Perl magic means 'z'++ == 'aa'
+ # Don't add more than 4 IDE disks
+ if (!$virtio && _suffixcmp($suffix, 'd') > 0) {
+ push(@removed, "$device(disk)");
+ } else {
+ $target->setAttribute('bus', $virtio ? 'virtio' : 'ide');
+ $target->setAttribute('dev', $prefix.$suffix);
+ $suffix++; # Perl magic means 'z'++ == 'aa'
+ }
}
- # Convert the first 4 CDROM drives to IDE, and remove the rest
- $suffix = 'a';
- my $i = 0;
- my @removed = ();
+ # Convert CD-ROM devices to IDE.
+ $suffix = 'a' if ($virtio);
foreach my $target
($dom->findnodes("/domain/devices/disk[\@device='cdrom']/target"))
{
- if ($i < 4) {
+ if (_suffixcmp($suffix, 'd') <= 0) {
$target->setAttribute('bus', 'ide');
$target->setAttribute('dev', "hd$suffix");
$suffix++;
} else {
- push(@removed, $target->getAttribute('dev'));
+ push(@removed, $target->getAttribute('dev')."(cdrom)");
my $disk = $target->getParentNode();
$disk->getParentNode()->removeChild($disk);
}
- $i++;
}
if (@removed > 0) {
- print user_message(__x("WARNING: Only 4 CDROM drives are supported. ".
- "The following CDROM drives have been removed: ".
+ print user_message(__x("WARNING: Only 4 IDE devices are supported. ".
+ "The following drives have been removed: ".
"{list}",
list => join(' ', @removed)));
}
diff --git a/lib/Sys/VirtV2V/Converter/Linux.pm b/lib/Sys/VirtV2V/Converter/Linux.pm
index 6f49351..3e76762 100644
--- a/lib/Sys/VirtV2V/Converter/Linux.pm
+++ b/lib/Sys/VirtV2V/Converter/Linux.pm
@@ -142,7 +142,7 @@ sub _configure_kernel_modules
# Make a note of whether we've added scsi_hostadapter
# We need this on RHEL 4/virtio because mkinitrd can't detect root on
- # virtio. For simplicity we always ensure this is set.
+ # virtio. For simplicity we always ensure this is set for virtio disks.
my $scsi_hostadapter = 0;
foreach my $module (keys(%$modules)) {
@@ -164,17 +164,22 @@ sub _configure_kernel_modules
$hvs_modules{$module} = 1;
}
- $guestos->update_kernel_module($module,
- $virtio ? "virtio_blk" : "sym53c8xx");
+ if ($virtio) {
+ $guestos->update_kernel_module($module, 'virtio_blk');
+ $scsi_hostadapter = 1;
+ }
- $scsi_hostadapter = 1;
+ # IDE doesn't need scsi_hostadapter
+ else {
+ $guestos->disable_kernel_module($module);
+ }
}
}
# Add an explicit scsi_hostadapter if it wasn't there before
- $guestos->enable_kernel_module('scsi_hostadapter',
- $virtio ? "virtio_blk" : "sym53c8xx")
- unless($scsi_hostadapter);
+ if ($virtio && !$scsi_hostadapter) {
+ $guestos->enable_kernel_module('scsi_hostadapter', 'virtio_blk');
+ }
# Warn if any old-HV specific kernel modules weren't updated
foreach my $module (keys(%hvs_modules)) {
diff --git a/lib/Sys/VirtV2V/GuestOS/RedHat.pm b/lib/Sys/VirtV2V/GuestOS/RedHat.pm
index 3f5d90a..8664c2d 100644
--- a/lib/Sys/VirtV2V/GuestOS/RedHat.pm
+++ b/lib/Sys/VirtV2V/GuestOS/RedHat.pm
@@ -964,54 +964,52 @@ sub remap_block_devices
# same names as used by the guest. However, if the guest is using libata,
# IDE drives could be renamed.
- # Look for IDE and SCSI devices in fstab for the guest
- my %guestif;
- foreach my $spec ($g->aug_match('/files/etc/fstab/*/spec')) {
- my $device = $g->aug_get($spec);
-
- next unless($device =~ m{^/dev/(sd|hd)([a-z]+)});
- $guestif{$1} ||= {};
- $guestif{$1}->{$1.$2} = 1;
+ # Modern distros use libata, and IDE devices are presented as sdX
+ my $libata = 1;
+
+ # RHEL 2, 3 and 4 didn't use libata
+ # RHEL 5 does use libata, but udev rules call IDE devices hdX anyway
+ if ($desc->{distro} eq 'rhel') {
+ if ($desc->{major_version} eq '2' ||
+ $desc->{major_version} eq '3' ||
+ $desc->{major_version} eq '4' ||
+ $desc->{major_version} eq '5')
+ {
+ $libata = 0;
+ }
}
+ # Fedora has used libata since FC7, which is long out of support. We assume
+ # that all Fedora distributions in use use libata.
+
+ if ($libata) {
+ # Look for IDE and SCSI devices in fstab for the guest
+ my %guestif;
+ foreach my $spec ($g->aug_match('/files/etc/fstab/*/spec')) {
+ my $device = $g->aug_get($spec);
+
+ next unless($device =~ m{^/dev/(sd|hd)([a-z]+)});
+ $guestif{$1} ||= {};
+ $guestif{$1}->{$1.$2} = 1;
+ }
- # If fstab contains references to sdX, these could refer to IDE or SCSI
- # devices. Need to look at the domain config for clues.
- if (exists($guestif{sd})) {
- # Look for IDE and SCSI devices from the domain definition
- my %domainif;
- foreach my $device (@$devices) {
- foreach my $type ('hd', 'sd') {
- if ($device =~ m{^$type([a-z]+)}) {
- $domainif{$type} ||= {};
- $domainif{$type}->{$device} = 1;
+ # If fstab contains references to sdX, these could refer to IDE or SCSI
+ # devices. We may need to update them.
+ if (exists($guestif{sd})) {
+ # Look for IDE and SCSI devices from the domain definition
+ my %domainif;
+ foreach my $device (@$devices) {
+ foreach my $type ('hd', 'sd') {
+ if ($device =~ m{^$type([a-z]+)}) {
+ $domainif{$type} ||= {};
+ $domainif{$type}->{$device} = 1;
+ }
}
}
- }
-
- # If domain defines both IDE and SCSI drives, and fstab contains
- # references on sdX, but not hdX, we don't know if the guest is using
- # libata or not. This means that we don't know if sdX in fstab refers
- # to hdX or sdX in the domain. Warn and assume that libata device
- # renaming is not in use.
- if (exists($domainif{hd}) && exists($domainif{sd}) &&
- !exists($guestif{hd}))
- {
- print STDERR user_message(__"WARNING: Unable to determine whether ".
- "sdX devices in /etc/fstab refer to ".
- "IDE or SCSI devices. Assuming they ".
- "refer to SCSI devices. /etc/fstab ".
- "may be incorrect after conversion if ".
- "guest uses libata.");
- }
- # If we've got only IDE devices in the domain, and only sdX devices in
- # fstab, the guest is renaming them.
- elsif (exists($domainif{hd}) && !exists($domainif{sd}) &&
- !exists($guestif{hd}))
- {
my %map;
my $letter = 'a';
+ # IDE drives are presented first
foreach my $old (sort { _drivecmp('hd', $a, $b) }
keys(%{$domainif{hd}}))
{
@@ -1019,10 +1017,16 @@ sub remap_block_devices
$letter++;
}
+ # Followed by SCSI drives
+ foreach my $old (sort { _drivecmp('sd', $a, $b) }
+ keys(%{$domainif{sd}}))
+ {
+ $map{$old} = "sd$letter";
+ $letter++;
+ }
+
map { $_ = $map{$_} } @$devices;
}
-
- # Otherwise we leave it alone
}
# We now assume that $devices contains an ordered list of device names, as
@@ -1030,8 +1034,16 @@ sub remap_block_devices
# device names.
my %map;
- # Everything will be converted to either vdX or sdX
- my $prefix = $virtio ? 'vd' : 'sd';
+ # Everything will be converted to either vdX, sdX or hdX
+ my $prefix;
+ if ($virtio) {
+ $prefix = 'vd';
+ } elsif ($libata) {
+ $prefix = 'sd';
+ } else {
+ $prefix = 'hd'
+ }
+
my $letter = 'a';
foreach my $device (@$devices) {
$map{$device} = $prefix.$letter;
--
1.6.6.1
14 years, 5 months
[PATCH] Add LocalCopy transfer method to transfer local files to a target
by Matthew Booth
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
14 years, 5 months
The exit code makes user confused while using '-' prefixed subcommands
by Qixiang Wan
guestfish returns 0 if the commands completed without error, or 1 if there was an error.
But the rule break when we use '-' prefix subcommands:
------------------------------------
$ guestfish <<\EOF
> blabla
> EOF
blabla: unknown command
$ echo $?
1
$ guestfish <<\EOF
-blabla
EOF
blabla: unknown command
$ echo $?
0
------------------------------------
I think it should be more reasonable to make the behaviours keeping identical with the rule.
14 years, 5 months
Re: [Libguestfs] libguestfs-1.0.85-1.el5.5 - seems no KVM acceleration support included
by Richard W.M. Jones
On Wed, Mar 31, 2010 at 11:24:30PM +0300, Active Systems OÜ wrote:
[Please CC your messages to libguestfs mailing list, since the
answer may help others in future.]
> One more error popped up during building - any clues what might be wrong or how to bypass that test? It might be related to locale - Im using by default:
> LANG=et_EE.utf8
> LC_CTYPE="et_EE.utf8"
> LC_NUMERIC="et_EE.utf8"
> LC_TIME="et_EE.utf8"
> LC_COLLATE="et_EE.utf8"
> LC_MONETARY="et_EE.utf8"
> LC_MESSAGES="et_EE.utf8"
> LC_PAPER="et_EE.utf8"
> LC_NAME="et_EE.utf8"
> LC_ADDRESS="et_EE.utf8"
> LC_TELEPHONE="et_EE.utf8"
> LC_MEASUREMENT="et_EE.utf8"
> LC_IDENTIFICATION="et_EE.utf8"
> LC_ALL=
>
> Ill try to change back to en_US.
>
> Build log:
>
> PASS: test-getdtablesize
> PASS: test-getgroups
> PASS: test-getline
> test-getlogin_r.c:55: assertion failed
> /bin/sh: line 5: 7764 Aborted EXEEXT='' srcdir='.' LOCALE_FR='fr_FR' LOCALE_FR_UTF8='fr_FR.UTF-8' LOCALE_JA='ja_JP' LOCALE_ZH_CN='zh_CN.GB18030' LOCALE_FR_UTF8='fr_FR.UTF-8' LOCALE_FR='fr_FR' LOCALE_FR_UTF8='fr_FR.UTF-8' LOCALE_JA='ja_JP' LOCALE_ZH_CN='zh_CN.GB18030' ${dir}$tst
> FAIL: test-getlogin_r
> PASS: test-gettimeofday
> PASS: test-glob
> PASS: test-hash
> PASS: test-inet_ntop
> PASS: test-inet_pton
> PASS: test-lchown
> PASS: test-lseek.sh
> PASS: test-lstat
> PASS: test-mbrtowc1.sh
> PASS: test-mbrtowc2.sh
> PASS: test-mbrtowc3.sh
> PASS: test-mbrtowc4.sh
> PASS: test-mbsinit.sh
> PASS: test-mbsrtowcs1.sh
> PASS: test-mbsrtowcs2.sh
> PASS: test-mbsrtowcs3.sh
> PASS: test-mbsrtowcs4.sh
> PASS: test-memchr
> PASS: test-memrchr
> PASS: test-mkdir
> PASS: test-netdb
> PASS: test-netinet_in
> PASS: test-open
> PASS: test-fchownat
> PASS: test-fstatat
> PASS: test-mkdirat
> PASS: test-openat
> PASS: test-unlinkat
> PASS: test-perror.sh
> PASS: test-pread.sh
> PASS: test-priv-set
> PASS: test-rawmemchr
> PASS: test-readlink
> PASS: test-rmdir
> Unconnected socket test... passed
> Connected sockets test... passed
> General socket test with fork... passed
> Pipe test... passed
> PASS: test-select
> ./test-select-in.sh: line 21: echo: write error: Broken pipe
> PASS: test-select-in.sh
> PASS: test-select-out.sh
> PASS: test-sleep
> PASS: test-snprintf
> PASS: test-sockets
> PASS: test-stat
> PASS: test-stat-time
> PASS: test-stdbool
> PASS: test-stddef
> PASS: test-stdint
> PASS: test-stdio
> PASS: test-stdlib
> PASS: test-strchrnul
> PASS: test-strerror
> PASS: test-string
> PASS: test-symlink
> PASS: test-symlinkat
> PASS: test-sys_ioctl
> PASS: test-sys_select
> PASS: test-sys_socket
> PASS: test-sys_stat
> PASS: test-sys_time
> PASS: test-sys_wait
> PASS: test-time
> PASS: test-dup-safer
> PASS: test-unistd
> PASS: test-unlink
> PASS: test-usleep
> skipping test: setting symlink time not supported on this file system
> SKIP: test-utimens
> PASS: test-vasnprintf
> PASS: test-vasprintf
> PASS: test-wchar
> PASS: test-wctype
> PASS: test-xalloc-die.sh
> ====================
> 1 of 93 tests failed
> (1 test was not run)
> ====================
> make[6]: *** [check-TESTS] Error 1
> make[6]: Leaving directory `/home/rpmbuilder/redhat/BUILD/libguestfs-1.0.85/daemon/tests'
> make[5]: *** [check-am] Error 2
> make[5]: Leaving directory `/home/rpmbuilder/redhat/BUILD/libguestfs-1.0.85/daemon/tests'
> make[4]: *** [check-recursive] Error 1
> make[4]: Leaving directory `/home/rpmbuilder/redhat/BUILD/libguestfs-1.0.85/daemon/tests'
> make[3]: *** [check] Error 2
> make[3]: Leaving directory `/home/rpmbuilder/redhat/BUILD/libguestfs-1.0.85/daemon/tests'
> make[2]: *** [check-recursive] Error 1
> make[2]: Leaving directory `/home/rpmbuilder/redhat/BUILD/libguestfs-1.0.85/daemon'
> make[1]: *** [check] Error 2
> make[1]: Leaving directory `/home/rpmbuilder/redhat/BUILD/libguestfs-1.0.85/daemon'
> make: *** [check-recursive] Error 1
> error: Bad exit status from /home/rpmbuilder/redhat/tmp/rpm-tmp.68538 (%check)
>
>
> RPM build errors:
> Bad exit status from /home/rpmbuilder/redhat/tmp/rpm-tmp.68538 (%check)
It's a gnulib bug. I'm not sure what, but you can generally skip
these, or report the bug directly to the gnulib upstream:
http://www.gnu.org/software/gnulib/
Rich.
--
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
New in Fedora 11: Fedora Windows cross-compiler. Compile Windows
programs, test, and build Windows installers. Over 70 libraries supprt'd
http://fedoraproject.org/wiki/MinGW http://www.annexia.org/fedora_mingw
14 years, 5 months