hivex: Exported foreign symbols in libhivex.so.0.0.0
by TJ
The symbols file generated in the Debian package to aid the package
maintainer automatically detect future changes shows internal library
and helper function names, whereas it should only be exporting hivex_*
function names. This page may help with this:
http://gcc.gnu.org/wiki/Visibility
Symbols:
asnprintf@Base 1.2.1
c_isalnum@Base 1.2.1
c_isalpha@Base 1.2.1
c_isascii@Base 1.2.1
c_isblank@Base 1.2.1
c_iscntrl@Base 1.2.1
c_isdigit@Base 1.2.1
c_isgraph@Base 1.2.1
c_islower@Base 1.2.1
c_isprint@Base 1.2.1
c_ispunct@Base 1.2.1
c_isspace@Base 1.2.1
c_isupper@Base 1.2.1
c_isxdigit@Base 1.2.1
c_tolower@Base 1.2.1
c_toupper@Base 1.2.1
exit_failure@Base 1.2.1
full_read@Base 1.2.1
full_write@Base 1.2.1
printf_fetchargs@Base 1.2.1
printf_parse@Base 1.2.1
program_name@Base 1.2.1
safe_read@Base 1.2.1
safe_write@Base 1.2.1
set_program_name@Base 1.2.1
vasnprintf@Base 1.2.1
xstrtol@Base 1.2.1
xstrtol_fatal@Base 1.2.1
xstrtoll@Base 1.2.1
xstrtoul@Base 1.2.1
xstrtoull@Base 1.2.1
14 years, 7 months
hivex: hivexsh dependency on libncurses?
by TJ
Hopefully this is my last issue raised as a result of the Debian
packaging effort!
I see a warning from debhelper which suggests the linked library
"libncurses" may actually be no longer required by "hivexsh". Is this
so, or is it in preparation for some future use of the library?
dpkg-shlibdeps: warning: dependency on libncurses.so.5 could be avoided
if "debian/hivex/usr/bin/hivexsh" were not uselessly linked against it
(they use none of its symbols).
14 years, 7 months
hivex: Bug: RPATH in Perl SO
by TJ
Whilst cleaning up the lintian reports in preparation for the
Debian/Ubuntu hivex package one of the issues is:
E: libhivex-perl: binary-or-shlib-defines-rpath
./usr/lib/perl/5.10.1/auto/Win/Hivex/Hivex.so
/tmp/buildd/hivex-1.2.1/perl/../lib/.libs
I've temporarily dealt with this by using chrpath in the build and a
rule to delete the RPATH from "auto/Win/Hivex/Hivex.so".
I wonder if this is an oversight since it is non-portable and refers to
a build-time location? There are some helpful notes about this at:
http://wiki.debian.org/RpathIssue
14 years, 7 months
hivex: lintain being pedantic about spelling!
by TJ
Thanks very much for the responses on my last three issues. I've
incorporated the appropriate changes into the budding Debian package.
I feel embarrassed to raise this one, but in the interests of shutting
lintian up maybe this could be fixed at some point:
I: hivex: spelling-error-in-manpage usr/share/man/man1/hivexregedit.1.gz reencode re-encode
I: libhivex-perl: spelling-error-in-manpage usr/share/man/man3/Win::Hivex::Regedit.3pm.gz reencode re-encode
N:
N: Lintian found a spelling error in the manpage. Lintian has a list of
N: common misspellings that it looks for. It does not have a dictionary
N: like a spelling checker does.
N:
N: If the string containing the spelling error is translated with the help
N: of gettext (with the help of po4a, for example) or a similar tool,
N: please fix the error in the translations as well as the English text to
N: avoid making the translations fuzzy. With gettext, for example, this
N: means you should also fix the spelling mistake in the corresponding
N: msgids in the *.po files.
N:
N: Severity: minor, Certainty: possible
FYI: My initial aim in packaging hivex is not so much for VM guest usage
but because it offers a neat solution to os-probe obtaining the actual
default boot loader title from Windows BCD store partitions when
populating GRUB menus for multi-boot systems.
TJ.
14 years, 7 months
hivex: Copyright license(s)
by TJ
I note that LICENSES and README state LGPL v2.1 but there are other
files with other licenses, most obviously many shell script files such
as:
regedit/hivexregedit
sh/example*
Also some Makefiles:
perl/Makefile.am
sh/Makefile.am
...etc...
find . -type f | while read filename; \
do if grep -iqs 'general public license' $filename; then \
if grep -viqs 'lesser' $filename; then \
echo $filename; \
fi; fi; done | wc -l
223
Many of those 223 instances of 'general public license' without 'lesser'
are from the build tools but there remain a significant number that
appear to be project-specific.
Is this a simple oversight or are the package contents actually subject
to both LGPL and GPL? If so, should this be reflected in LICENSE and/or
README?
14 years, 7 months
hivex: pod2html complaints
by TJ
Whilst building hivex I'm seeing warnings/errors for the documentation:
pod2html complaints:
/usr/bin/pod2html: sh/hivexsh.pod: unknown pod directive 'encoding' in
paragraph 1. ignoring.
/usr/bin/pod2html: sh/hivexsh.pod: cannot resolve L<hivex(3)> in paragraph 8.
/usr/bin/pod2html: sh/hivexsh.pod: cannot resolve L<virt-cat(1)> in paragraph 8.
/usr/bin/pod2html: sh/hivexsh.pod: cannot resolve L<guestfish(1)> in
paragraph 8.
/usr/bin/pod2html: sh/hivexsh.pod: cannot resolve L<hivex(3)/WRITING
TO HIVE FILES> in paragraph 22.
/usr/bin/pod2html: sh/hivexsh.pod: cannot resolve L<hivex(3)> in paragraph 73.
/usr/bin/pod2html: sh/hivexsh.pod: cannot resolve L<hivexget(1)> in
paragraph 73.
/usr/bin/pod2html: sh/hivexsh.pod: cannot resolve L<hivexml(1)> in paragraph 73.
/usr/bin/pod2html: sh/hivexsh.pod: cannot resolve L<virt-win-reg(1)>
in paragraph 73.
/usr/bin/pod2html: sh/hivexsh.pod: cannot resolve L<guestfs(3)> in paragraph 73.
/usr/bin/pod2html: sh/hivexsh.pod: cannot resolve L<virt-cat(1)> in
paragraph 73.
/usr/bin/pod2html: sh/hivexsh.pod: cannot resolve L<virt-edit(1)> in
paragraph 73.
14 years, 7 months
hivex: Makefiles not fully cleaning up?
by TJ
I'm currently creating a Debian/Ubuntu package for hivex.
In the process I noticed that several files that are created during the
build process are left behind after a 'clean'.
I'm currently using the attached patch but wondered if this is something
that should be adopted, or have I misunderstood something?
14 years, 7 months
[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, 7 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, 7 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, 7 months