We previously used libvirt domain XML directly as the internal representation of
domain metadata. This change replaces this with a custom representation,
described in metadata-format.txt.
There are several reasons for this change:
* Code to query and modify the new representation is simpler than code to handle
XML.
* By explicitly parsing only required information from a libvirt source, we can
remove a significant amount of code to handle problematic source domain XML
which would otherwise have been blindly copied to the output.
* By having our own format, we can better serve the needs of multiple source and
target hypervisors.
---
MANIFEST | 1 +
lib/Sys/VirtV2V/Connection/LibVirt.pm | 67 +++++-
lib/Sys/VirtV2V/Connection/LibVirtSource.pm | 20 +-
lib/Sys/VirtV2V/Connection/LibVirtTarget.pm | 260 +++++++++++------
lib/Sys/VirtV2V/Connection/LibVirtXMLSource.pm | 27 +-
lib/Sys/VirtV2V/Connection/RHEVTarget.pm | 92 ++----
lib/Sys/VirtV2V/Connection/Source.pm | 136 ++--------
lib/Sys/VirtV2V/Converter.pm | 361 +-----------------------
lib/Sys/VirtV2V/Converter/RedHat.pm | 85 +++----
lib/Sys/VirtV2V/Converter/Windows.pm | 32 +--
metadata-format.txt | 28 ++
v2v/virt-v2v.pl | 36 ++--
12 files changed, 414 insertions(+), 731 deletions(-)
create mode 100644 metadata-format.txt
diff --git a/MANIFEST b/MANIFEST
index 9c2a5df..71e9a19 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -24,6 +24,7 @@ lib/Sys/VirtV2V/Transfer/SSH.pm
lib/Sys/VirtV2V/Util.pm
MANIFEST.SKIP
MANIFEST This list of files
+metadata-format.txt
META.yml
po/es.po
po/it.po
diff --git a/lib/Sys/VirtV2V/Connection/LibVirt.pm
b/lib/Sys/VirtV2V/Connection/LibVirt.pm
index 9d322ee..354f227 100644
--- a/lib/Sys/VirtV2V/Connection/LibVirt.pm
+++ b/lib/Sys/VirtV2V/Connection/LibVirt.pm
@@ -1,5 +1,5 @@
# Sys::VirtV2V::Connection::LibVirt
-# Copyright (C) 2009,2010 Red Hat Inc.
+# Copyright (C) 2009-2011 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
@@ -151,9 +151,72 @@ sub _get_transfer
$format, $is_sparse);
}
+sub _parse_dom
+{
+ my ($dom) = @_;
+
+ my %meta;
+ my $root = $dom->getDocumentElement();
+
+ $meta{name} = _node_val($root, 'name/text()');
+ $meta{memory} = _node_val($root, 'memory/text()') * 1024;
+ $meta{cpus} = _node_val($root, 'vcpu/text()');
+ $meta{arch} = _node_val($root, 'os/type/@arch');
+
+ $meta{features} = [];
+ foreach my $feature ($root->findnodes('features/*')) {
+ push(@{$meta{features}}, $feature->getNodeName());
+ }
+
+ $meta{disks} = [];
+ foreach my $disk
($root->findnodes('devices/disk[@device=\'disk\']')) {
+ my %info;
+
+ $info{device} = _node_val($disk, 'target/@dev');
+ $info{path} = _node_val($disk, 'source/@file | source/@dev');
+ $info{is_block} = _node_val($disk, '@type') eq 'file' ? 0 : 1;
+ $info{format} = _node_val($disk, 'driver/@type');
+
+ push(@{$meta{disks}}, \%info);
+ }
+
+ $meta{removables} = [];
+ foreach my $disk ($root->findnodes('devices/disk[@device=\'cdrom\' or
'.
+ '@device=\'floppy\']'))
+ {
+ my %info;
+
+ $info{name} = _node_val($disk, 'target/@dev');
+ $info{type} = _node_val($disk, '@device');
+
+ push(@{$meta{removables}}, \%info);
+ }
+
+ $meta{nics} = [];
+ foreach my $nic ($root->findnodes('devices/interface')) {
+ my %info;
+
+ $info{mac} = _node_val($nic, 'mac/@address');
+ $info{vnet} = _node_val($nic, 'source/@network | source/@bridge');
+ $info{vnet_type} = _node_val($nic, '@type');
+
+ push(@{$meta{nics}}, \%info);
+ }
+
+ return \%meta;
+}
+
+sub _node_val
+{
+ my ($root, $xpath) = @_;
+
+ my ($node) = $root->findnodes($xpath);
+ return defined($node) ? $node->getNodeValue() : undef;
+}
+
=head1 COPYRIGHT
-Copyright (C) 2009,2010 Red Hat Inc.
+Copyright (C) 2009-2011 Red Hat Inc.
=head1 LICENSE
diff --git a/lib/Sys/VirtV2V/Connection/LibVirtSource.pm
b/lib/Sys/VirtV2V/Connection/LibVirtSource.pm
index f35feb5..87783e5 100644
--- a/lib/Sys/VirtV2V/Connection/LibVirtSource.pm
+++ b/lib/Sys/VirtV2V/Connection/LibVirtSource.pm
@@ -1,5 +1,5 @@
-# Sys::VirtV2V::Connection::LibVirt
-# Copyright (C) 2009,2010 Red Hat Inc.
+# Sys::VirtV2V::Connection::LibVirtSource
+# Copyright (C) 2009-2011 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
@@ -21,7 +21,7 @@ use strict;
use warnings;
use URI;
-use XML::DOM;
+use XML::DOM::XPath;
use Sys::Virt;
@@ -49,7 +49,7 @@ Sys::VirtV2V::Connection::LibVirtSource - Get storage and metadata from
libvirt
$conn = Sys::VirtV2V::Connection::LibVirtSource->new
("xen+ssh://xenserver.example.com/", $name);
- $dom = $conn->get_dom();
+ $meta = $conn->get_meta();
=head1 DESCRIPTION
@@ -78,7 +78,7 @@ sub new
$self->{name} = $name;
$self->_check_shutdown();
- $self->_get_dom();
+ $self->_get_meta();
return $self;
}
@@ -227,7 +227,7 @@ sub _get_domain
return $domain;
}
-sub _get_dom
+sub _get_meta
{
my $self = shift;
@@ -240,17 +240,15 @@ sub _get_dom
# Warn and exit if we didn't find it
return undef unless(defined($domain));
- my $xml = $domain->get_xml_description();
-
- my $dom = new XML::DOM::Parser->parse($xml);
- $self->{dom} = $dom;
+ my $dom = new XML::DOM::Parser->parse($domain->get_xml_description());
+ $self->{meta} = Sys::VirtV2V::Connection::LibVirt::_parse_dom($dom);
}
=back
=head1 COPYRIGHT
-Copyright (C) 2009,2010 Red Hat Inc.
+Copyright (C) 2009-2011 Red Hat Inc.
=head1 LICENSE
diff --git a/lib/Sys/VirtV2V/Connection/LibVirtTarget.pm
b/lib/Sys/VirtV2V/Connection/LibVirtTarget.pm
index a74c978..7d00921 100644
--- a/lib/Sys/VirtV2V/Connection/LibVirtTarget.pm
+++ b/lib/Sys/VirtV2V/Connection/LibVirtTarget.pm
@@ -1,5 +1,5 @@
# Sys::VirtV2V::Connection::LibVirtTarget
-# Copyright (C) 2010 Red Hat Inc.
+# Copyright (C) 2010-2011 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
@@ -261,7 +261,7 @@ sub guest_exists
return 1;
}
-=item create_guest(desc, dom, guestcaps)
+=item create_guest(desc, meta, config, guestcaps)
Create the guest in the target
@@ -270,19 +270,164 @@ Create the guest in the target
sub create_guest
{
my $self = shift;
- my ($desc, $dom, $guestcaps) = @_;
+ my ($desc, $meta, $config, $guestcaps) = @_;
my $vmm = $self->{vmm};
- _unconfigure_incompatible_devices($dom);
- _configure_capabilities($vmm, $dom, $guestcaps);
+ _configure_capabilities($vmm, $meta, $guestcaps);
- $vmm->define_domain($dom->toString());
+ $vmm->define_domain(_meta_to_domxml($meta, $config, $guestcaps));
# Guest is successfully created, don't remove its volumes
@cleanup_vols = ();
}
+sub _meta_to_domxml
+{
+ my ($meta, $config, $guestcaps) = @_;
+
+ my $dom = new XML::DOM::Parser->parse(<<DOM);
+<domain type='kvm'>
+ <os>
+ <type>hvm</type>
+ <boot dev='hd'/>
+ </os>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>restart</on_crash>
+ <devices>
+ <input type='tablet' bus='usb'/>
+ <input type='mouse' bus='ps2'/>
+ <graphics type='vnc' port='-1' listen='127.0.0.1'/>
+ <video>
+ <model type='cirrus' vram='9216' heads='1'/>
+ </video>
+ <console type='pty'/>
+ </devices>
+</domain>
+DOM
+
+ my $root = $dom->getDocumentElement();
+
+ _append_elem($root, 'name', $meta->{name});
+ _append_elem($root, 'memory', $meta->{memory} / 1024);
+ _append_elem($root, 'vcpu', $meta->{cpus});
+
+ my ($ostype) = $root->findnodes('os/type');
+ $ostype->setAttribute('arch', $guestcaps->{arch});
+
+ my $features = _append_elem($root, 'features');
+ foreach my $feature (@{$meta->{features}}) {
+ _append_elem($features, $feature);
+ }
+
+ my $virtio = $guestcaps->{block} eq 'virtio' ? 1 : 0;
+ my $prefix = $virtio == 1 ? 'vd' : 'hd';
+ my $suffix = 'a';
+
+ my $nide = 0;
+
+ my ($devices) = $root->findnodes('devices');
+ foreach my $disk (sort { $a->{device} cmp $b->{device} } @{$meta->{disks}})
+ {
+ my $is_block = $disk->{is_block};
+
+ my $diskE = _append_elem($devices, 'disk');
+ $diskE->setAttribute('device', 'disk');
+ $diskE->setAttribute('type', $is_block ? 'block' :
'file');
+
+ my $driver = _append_elem($diskE, 'driver');
+ $driver->setAttribute('name', 'qemu');
+ $driver->setAttribute('type', $disk->{format});
+
+ my $source = _append_elem($diskE, 'source');
+ $source->setAttribute($is_block ? 'dev' : 'file',
$disk->{path});
+
+ my $target = _append_elem($diskE, 'target');
+ $target->setAttribute('dev', $prefix.$suffix); $suffix++;
+ $target->setAttribute('bus', $guestcaps->{block});
+
+ $nide++ unless $virtio;
+ }
+
+ # Add the correct number of cdrom and floppy drives with appropriate new
+ # names
+ $suffix = 'a' if ($virtio);
+ my $fdn = 0;
+ foreach my $removable (@{$meta->{removables}}) {
+ my $name;
+ my $bus;
+ if ($removable->{type} eq 'cdrom') {
+ $bus = 'ide';
+ $name = 'hd'.$suffix; $suffix++;
+ $nide++;
+ } elsif ($removable->{type} eq 'floppy') {
+ $bus = 'fdc';
+ $name = 'fd'.$fdn; $fdn++;
+ } else {
+ logmsg WARN, __x('Ignoring removable device {name} with unknown '.
+ 'type {type}.',
+ name => $removable->{name},
+ type => $removable->{type});
+ next;
+ }
+
+ my $diskE = _append_elem($devices, 'disk');
+ $diskE->setAttribute('device', $removable->{type});
+ $diskE->setAttribute('type', 'file');
+
+ my $driver = _append_elem($diskE, 'driver');
+ $driver->setAttribute('name', 'qemu');
+ $driver->setAttribute('type', 'raw');
+
+ my $target = _append_elem($diskE, 'target');
+ $target->setAttribute('dev', $name);
+ $target->setAttribute('bus', $bus);
+
+ _append_elem($diskE, 'readonly') if ($removable->{type} eq
'cdrom');
+ }
+
+ logmsg WARN, __x('Only 4 IDE devices are supported, but this guest has '.
+ '{number}. The guest will not operate correctly without '.
+ 'manual reconfiguration.', number => $nide) if $nide >
4;
+
+ foreach my $nic (@{$meta->{nics}}) {
+ # Find an appropriate mapped network
+ my ($vnet, $vnet_type) =
+ $config->map_network($nic->{vnet}, $nic->{vnet_type});
+ $vnet ||= $nic->{vnet};
+ $vnet_type ||= $nic->{vnet_type};
+
+ my $interface = _append_elem($devices, 'interface');
+ $interface->setAttribute('type', $vnet_type);
+
+ my $mac = _append_elem($interface, 'mac');
+ $mac->setAttribute('address', $nic->{mac});
+
+ my $source = _append_elem($interface, 'source');
+ $source->setAttribute($vnet_type, $vnet);
+
+ my $model = _append_elem($interface, 'model');
+ $model->setAttribute('type', $guestcaps->{net});
+ }
+
+ return $dom->toString();
+}
+
+sub _append_elem
+{
+ my ($parent, $name, $text) = @_;
+
+ my $doc = $parent->getOwnerDocument();
+ my $e = $doc->createElement($name);
+ my $textE = $doc->createTextNode($text) if defined($text);
+
+ $parent->appendChild($e);
+ $e->appendChild($textE) if defined($text);
+
+ return $e;
+}
+
sub DESTROY
{
my $self = shift;
@@ -303,31 +448,10 @@ sub DESTROY
}
}
-sub _unconfigure_incompatible_devices
-{
- my ($dom) = @_;
-
- foreach my $path (
- # We have replaced the SCSI controller with either VirtIO or IDE.
- # Additionally, attempting to start a guest converted from ESX, which
- # has an lsilogic SCSI controller, will fail on RHEL 5.
-
$dom->findnodes("/domain/devices/controller[\@type='scsi']"),
-
- # XXX: We have no current way of detecting which sound card models are
- # supported by the target hypervisor. As an unsupported sound card model
- # can prevent the guest from starting, we simply remove sound cards for
- # the moment.
- $dom->findnodes("/domain/devices/sound")
- )
- {
- $path->getParentNode()->removeChild($path);
- }
-}
-
# Configure guest according to target hypervisor's capabilities
sub _configure_capabilities
{
- my ($vmm, $dom, $guestcaps) = @_;
+ my ($vmm, $meta, $guestcaps) = @_;
# Parse the capabilities of the connected libvirt
my $caps = new XML::DOM::Parser->parse($vmm->get_capabilities());
@@ -340,94 +464,48 @@ sub _configure_capabilities
v2vdie __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) {
- logmsg WARN, __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
+ if (exists($meta->{features})) {
+ # Check that requested 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})) {
+ my @new_features = ();
+ foreach my $feature (@{$meta->{features}}) {
+ if (!exists($features{$feature})) {
logmsg WARN, __x('The connected hypervisor does not '.
'support feature {feature}.',
- feature => $name);
- $feature->getParentNode()->removeChild($feature);
+ feature => $feature);
}
- if ($name eq 'acpi' && !$guestcaps->{acpi}) {
+ elsif ($feature eq 'acpi' && !$guestcaps->{acpi}) {
logmsg WARN, __('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);
+ else {
+ push(@new_features, $feature);
+ }
+
+ $meta->{features} = \@new_features;
+ }
}
# Add acpi support if the guest supports it
if ($guestcaps->{acpi}) {
- $domfeatures->appendChild($dom->createElement('acpi'));
+ push(@{$meta->{features}}, 'acpi') unless $meta->{features} ~~
'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));
+ next if $meta->{features} ~~ $feature;
my ($c) = $guestcap->findnodes("features/$feature");
- if (defined($c)) {
- $domfeatures->appendChild($dom->createElement($feature));
- }
+ push(@{$meta->{features}}, $feature) if defined($c);
}
}
@@ -435,7 +513,7 @@ sub _configure_capabilities
=head1 COPYRIGHT
-Copyright (C) 2010 Red Hat Inc.
+Copyright (C) 2010-2011 Red Hat Inc.
=head1 LICENSE
diff --git a/lib/Sys/VirtV2V/Connection/LibVirtXMLSource.pm
b/lib/Sys/VirtV2V/Connection/LibVirtXMLSource.pm
index 596450d..dabebe3 100644
--- a/lib/Sys/VirtV2V/Connection/LibVirtXMLSource.pm
+++ b/lib/Sys/VirtV2V/Connection/LibVirtXMLSource.pm
@@ -1,5 +1,5 @@
# Sys::VirtV2V::Connection::LibVirtXMLSource
-# Copyright (C) 2009,2010 Red Hat Inc.
+# Copyright (C) 2009-2011 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
@@ -62,7 +62,7 @@ sub new
bless($self, $class);
- $self->_get_dom($path);
+ $self->_get_meta($path);
return $self;
}
@@ -75,13 +75,12 @@ Return the name of the domain.
sub get_name
{
- my $dom = shift->{dom};
+ my $meta = shift->{meta};
- my ($name) = $dom->findnodes('/domain/name');
- return $name;
+ return $meta->{name};
}
-sub _get_dom
+sub _get_meta
{
my $self = shift;
@@ -92,16 +91,19 @@ sub _get_dom
path => $self->{path}, error => $!);
# Parse the input file
- eval { $self->{dom} = new XML::DOM::Parser->parse ($xml); };
+ my $dom;
+ eval { $dom = new XML::DOM::Parser->parse ($xml); };
# Display any parse errors
v2vdie __x('Unable to parse domain from file {path}: {error}',
path => $self->{path}, error => $@) if $@;
# Check it looks like domain XML
- my ($dummy) = $self->{dom}->findnodes('/domain/name');
+ my ($dummy) = $dom->findnodes('/domain/name');
v2vdie __x('{path} doesn\'t look like a libvirt domain XML file',
path => $self->{path}) unless defined($dummy);
+
+ $self->{meta} = Sys::VirtV2V::Connection::LibVirt::_parse_dom($dom);
}
=item get_volume(path)
@@ -151,11 +153,14 @@ sub get_volume
$is_block = 0;
my $st = stat($path);
$usage = $st->blocks * 512;
+
+ # Usage can be reported greater than size for large files due to the
+ # requirement for indirect blocks
+ $usage = $size if $usage > $size;
+
$is_sparse = $usage < $size ? 1 : 0;
}
- die("size ($size) < usage ($usage)") if $size < $usage;
-
my $transfer = new Sys::VirtV2V::Transfer::Local($path, $format,
$is_sparse);
@@ -169,7 +174,7 @@ sub get_volume
=head1 COPYRIGHT
-Copyright (C) 2009,2010 Red Hat Inc.
+Copyright (C) 2009-2011 Red Hat Inc.
=head1 LICENSE
diff --git a/lib/Sys/VirtV2V/Connection/RHEVTarget.pm
b/lib/Sys/VirtV2V/Connection/RHEVTarget.pm
index 5fde58b..6d7b89c 100644
--- a/lib/Sys/VirtV2V/Connection/RHEVTarget.pm
+++ b/lib/Sys/VirtV2V/Connection/RHEVTarget.pm
@@ -1,5 +1,5 @@
# Sys::VirtV2V::Connection::RHEVTarget
-# Copyright (C) 2010 Red Hat Inc.
+# Copyright (C) 2010-2011 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
@@ -606,7 +606,7 @@ sub guest_exists
return 0;
}
-=item create_guest(dom)
+=item create_guest(desc, meta, config, guestcaps)
Create the guest in the target
@@ -615,20 +615,16 @@ Create the guest in the target
sub create_guest
{
my $self = shift;
- my ($desc, $dom, $guestcaps) = @_;
+ my ($desc, $meta, $config, $guestcaps) = @_;
# Get the name of the guest
- my ($name) = $dom->findnodes('/domain/name/text()');
- $name = $name->getNodeValue();
+ my $name = $meta->{name};
# Get the number of virtual cpus
- my ($ncpus) = $dom->findnodes('/domain/vcpu/text()');
- $ncpus = $ncpus->getNodeValue();
+ my $ncpus = $meta->{cpus};
# Get the amount of memory in MB
- my ($memsize) = $dom->findnodes('/domain/memory/text()');
- $memsize = $memsize->getNodeValue();
- $memsize = ceil($memsize / 1024);
+ my $memsize = ceil($meta->{memory}/1024/1024);
# Generate a creation date
my $vmcreation = _format_time(gmtime());
@@ -710,8 +706,8 @@ sub create_guest
</ovf:Envelope>
EOF
- $self->_disks($ovf, $dom);
- $self->_networks($ovf, $dom);
+ $self->_disks($ovf, $meta, $guestcaps);
+ $self->_networks($ovf, $meta, $config, $guestcaps);
my $mountdir = $self->{mountdir};
my $domainuuid = $self->{domainuuid};
@@ -900,7 +896,7 @@ sub _format_time
sub _disks
{
my $self = shift;
- my ($ovf, $dom) = @_;
+ my ($ovf, $meta, $guestcaps) = @_;
my ($references) = $ovf->findnodes('/ovf:Envelope/References');
die("no references") unless (defined($references));
@@ -916,19 +912,12 @@ sub _disks
my $driveno = 1;
- foreach my $disk
- ($dom->findnodes("/domain/devices/disk[\@device='disk']"))
- {
- my ($path) = $disk->findnodes('source/@file');
- $path = $path->getNodeValue();
+ foreach my $disk (@{$meta->{disks}}) {
+ my $vol = Sys::VirtV2V::Connection::RHEVTarget::Vol->_get_by_path
+ ($disk->{path});
- my ($bus) = $disk->findnodes('target/@bus');
- $bus = $bus->getNodeValue();
-
- my $vol = Sys::VirtV2V::Connection::RHEVTarget::Vol->_get_by_path($path);
-
- die("dom contains path not written by virt-v2v: $path\n".
- $dom->toString()) unless (defined($vol));
+ die('metadata contains path not written by virt-v2v: ',
$disk->{path})
+ unless defined($vol);
my $fileref = catdir($vol->_get_imageuuid(), $vol->_get_voluuid());
my $size_gb = ceil($vol->get_size()/1024/1024/1024);
@@ -959,7 +948,7 @@ sub _disks
$diske->setAttribute('ovf:format',
'http://en.wikipedia.org/wiki/Byte');
# IDE = 0, SCSI = 1, VirtIO = 2
$diske->setAttribute('ovf:disk-interface',
- $bus eq 'virtio' ? 'VirtIO' :
'IDE');
+ $guestcaps->{block} eq 'virtio' ?
'VirtIO' : 'IDE');
# The libvirt QEMU driver marks the first disk (in document order) as
# bootable
$diske->setAttribute('ovf:boot', $driveno == 1 ? 'True' :
'False');
@@ -1026,7 +1015,7 @@ sub _disks
sub _networks
{
my $self = shift;
- my ($ovf, $dom) = @_;
+ my ($ovf, $meta, $config, $guestcaps) = @_;
my ($networksection) = $ovf->findnodes("/ovf:Envelope/Section".
"[\@xsi:type =
'ovf:NetworkSection_Type']");
@@ -1037,41 +1026,22 @@ sub _networks
die("no virtualhardware") unless (defined($virtualhardware));
my $i = 0;
+ foreach my $if (@{$meta->{nics}}) {
+ my $dev = "eth$i"; $i++;
- foreach my $if
- ($dom->findnodes('/domain/devices/interface'))
- {
- # Extract relevant info about this NIC
- my $type = $if->getAttribute('type');
-
- my $name;
- if ($type eq 'bridge') {
- ($name) = $if->findnodes('source/@bridge');
- } elsif ($type eq 'network') {
- ($name) = $if->findnodes('source/@network');
- } else {
- # Should have been picked up in Converter
- die("Unknown interface type");
- }
- $name = $name->getNodeValue();
-
- my ($driver) = $if->findnodes('model/@type');
- $driver &&= $driver->getNodeValue();
-
- my ($mac) = $if->findnodes('mac/@address');
- $mac &&= $mac->getNodeValue();
-
- my $dev = "eth$i";
+ # Find an appropriate mapped network
+ my ($vnet, undef) = $config->map_network($if->{vnet},
$if->{vnet_type});
+ $vnet ||= $if->{vnet};
my $e = $ovf->createElement("Network");
- $e->setAttribute('ovf:name', $name);
+ $e->setAttribute('ovf:name', $vnet);
$networksection->appendChild($e);
my $item = $ovf->createElement('Item');
$virtualhardware->appendChild($item);
$e = $ovf->createElement('rasd:Caption');
- $e->addText("Ethernet adapter on $name");
+ $e->addText('Ethernet adapter on '.$vnet);
$item->appendChild($e);
$e = $ovf->createElement('rasd:InstanceId');
@@ -1083,16 +1053,16 @@ sub _networks
$item->appendChild($e);
$e = $ovf->createElement('rasd:ResourceSubType');
- if ($driver eq 'rtl8139') {
+ if ($guestcaps->{net} eq 'rtl8139') {
$e->addText('1');
- } elsif ($driver eq 'e1000') {
+ } elsif ($guestcaps->{net} eq 'e1000') {
$e->addText('2');
- } elsif ($driver eq 'virtio') {
+ } elsif ($guestcaps->{net} eq 'virtio') {
$e->addText('3');
} else {
logmsg WARN, __x('Unknown NIC model {driver} for {dev}. '.
'NIC will be {default} when imported.',
- driver => $driver,
+ driver => $guestcaps->{net},
dev => $dev,
default => 'rtl8139');
$e->addText('1');
@@ -1100,7 +1070,7 @@ sub _networks
$item->appendChild($e);
$e = $ovf->createElement('rasd:Connection');
- $e->addText($name);
+ $e->addText($vnet);
$item->appendChild($e);
$e = $ovf->createElement('rasd:Name');
@@ -1108,10 +1078,8 @@ sub _networks
$item->appendChild($e);
$e = $ovf->createElement('rasd:MACAddress');
- $e->addText($mac) if (defined($mac));
+ $e->addText($if->{mac});
$item->appendChild($e);
-
- $i++;
}
}
@@ -1119,7 +1087,7 @@ sub _networks
=head1 COPYRIGHT
-Copyright (C) 2010 Red Hat Inc.
+Copyright (C) 2010-2011 Red Hat Inc.
=head1 LICENSE
diff --git a/lib/Sys/VirtV2V/Connection/Source.pm b/lib/Sys/VirtV2V/Connection/Source.pm
index 8cbfe25..7926822 100644
--- a/lib/Sys/VirtV2V/Connection/Source.pm
+++ b/lib/Sys/VirtV2V/Connection/Source.pm
@@ -1,5 +1,5 @@
# Sys::VirtV2V::Connection::Source
-# Copyright (C) 2009,2010 Red Hat Inc.
+# Copyright (C) 2009-2011 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
@@ -31,16 +31,14 @@ use Locale::TextDomain 'virt-v2v';
=head1 NAME
-Sys::VirtV2V::Connection - Obtain domain metadata
+Sys::VirtV2V::Source - A source connection
=head1 SYNOPSIS
use Sys::VirtV2V::Connection::LibVirtSource;
$conn = Sys::VirtV2V::Connection::LibVirtSource->new($uri, $name, $target);
- $dom = $conn->get_dom();
- $storage = $conn->get_storage_paths();
- $devices = $conn->get_storage_devices();
+ $meta = $conn->get_meta();
=head1 DESCRIPTION
@@ -55,49 +53,19 @@ subclasses:
=over
-=item get_storage_paths
+=item get_meta()
-Return an arrayref of local paths to the guest's storage devices. This list is
-guaranteed to be in the same order as the list returned by get_storage_devices.
+Return guest metadata.
-=cut
-
-sub get_storage_paths
-{
- my $self = shift;
-
- return $self->{paths};
-}
-
-=item get_storage_devices
-
-Return an arrayref of libvirt device names for the guest's storage prior to
-conversion. This list is guaranteed to be in the same order as the list returned
-by get_storage_paths.
-
-=cut
-
-sub get_storage_devices
-{
- my $self = shift;
-
- return $self->{devices};
-}
-
-=item get_dom()
-
-Returns an XML::DOM::Document describing a libvirt configuration equivalent to
-the input.
-
-Returns undef and displays an error if there was an error
+Returns undef and displays an error if there was an error.
=cut
-sub get_dom
+sub get_meta
{
my $self = shift;
- return $self->{dom};
+ return $self->{meta};
}
sub _volume_copy
@@ -172,7 +140,7 @@ sub _volume_copy
return $dst;
}
-=item copy_storage(target)
+=item copy_storage(target, format, is_sparse)
Copy all of a guests storage devices to I<target>. Update the guest metadata to
reflect their new locations and properties.
@@ -184,26 +152,10 @@ sub copy_storage
my $self = shift;
my ($target, $output_format, $output_sparse) = @_;
- my $dom = $self->get_dom();
-
- # An list of local paths to guest storage
- my @paths;
- # A list of libvirt target device names
- my @devices;
-
- foreach my $disk
($dom->findnodes("/domain/devices/disk[\@device='disk']"))
- {
- my ($source_e) = $disk->findnodes('source');
+ my $meta = $self->get_meta();
- my ($source) = $source_e->findnodes('@file | @dev');
- defined($source) or die("source element has neither dev nor file: \n".
- $dom->toString());
-
- my ($dev) = $disk->findnodes('target/@dev');
- defined($dev) or die("disk does not have a target device: \n".
- $dom->toString());
-
- my $src = $self->get_volume($source->getValue());
+ foreach my $disk (@{$meta->{disks}}) {
+ my $src = $self->get_volume($disk->{path});
my $dst;
if ($target->volume_exists($src->get_name())) {
logmsg WARN, __x('Storage volume {name} already exists on the '.
@@ -219,72 +171,24 @@ sub copy_storage
defined($output_sparse) ? $output_sparse : $src->is_sparse()
);
- _volume_copy($src, $dst);
- }
-
- # This will die if libguestfs can't use the result directly, so we do it
- # before copying all the data.
- push(@paths, $dst->get_local_path());
-
- # Export the new path
- my $path = $dst->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', $dst->get_format());
-
- # Remove the @file or @dev attribute before adding a new one
- $source_e->removeAttributeNode($source);
+ # This will die if libguestfs can't use the result directly, so we
+ # do it before copying all the data.
+ $disk->{local_path} = $dst->get_local_path();
- # Set @file or @dev as appropriate
- if ($dst->is_block()) {
- $disk->setAttribute('type', 'block');
- $source_e->setAttribute('dev', $path);
- } else {
- $disk->setAttribute('type', 'file');
- $source_e->setAttribute('file', $path);
+ _volume_copy($src, $dst);
}
- push(@devices, $dev->getNodeValue());
+ # Update the volume path to point to the copy
+ $disk->{path} = $dst->get_path();
+ $disk->{is_block} = $dst->is_block();
}
-
- # Blank the source of floppies or cdroms
- foreach my $disk ($dom->findnodes('/domain/devices/disk'.
- "[\@device='floppy' or
\@device='cdrom']"))
- {
- my ($source_e) = $disk->findnodes('source');
-
- # Nothing to do if there's no source element
- next unless (defined($source_e));
-
- # Blank file or dev as appropriate
- my ($source) = $source_e->findnodes('@file | @dev');
- defined($source) or die("source element has neither dev nor file: \n".
- $dom->toString());
-
- $source_e->setAttribute($source->getName(), '');
- }
-
- v2vdie __'Guest doesn\'t define any recognised storage devices'
- unless @paths > 0;
-
- $self->{paths} = \@paths;
- $self->{devices} = \@devices;
}
=back
=head1 COPYRIGHT
-Copyright (C) 2009,2010 Red Hat Inc.
+Copyright (C) 2009-2011 Red Hat Inc.
=head1 LICENSE
diff --git a/lib/Sys/VirtV2V/Converter.pm b/lib/Sys/VirtV2V/Converter.pm
index c4adb49..dd0c337 100644
--- a/lib/Sys/VirtV2V/Converter.pm
+++ b/lib/Sys/VirtV2V/Converter.pm
@@ -1,5 +1,5 @@
# Sys::VirtV2V::Converter
-# Copyright (C) 2009 Red Hat Inc.
+# Copyright (C) 2009-2011 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
@@ -40,7 +40,7 @@ Sys::VirtV2V::Converter - Convert a guest to run on KVM
use Sys::VirtV2V::Converter;
- Sys::VirtV2V::Converter->convert($g, $config, $desc, $dom, $devices);
+ Sys::VirtV2V::Converter->convert($g, $config, $desc, $meta);
=head1 DESCRIPTION
@@ -51,28 +51,7 @@ OS, and uses it to convert the guest to run on KVM.
=over
-=cut
-
-# Default values for a KVM configuration
-use constant KVM_DEFAULT_XML => "
-<domain type='kvm'>
- <os>
- <type machine='pc'>hvm</type>
- <boot dev='hd'/>
- </os>
- <devices>
- <input type='tablet' bus='usb'/>
- <input type='mouse' bus='ps2'/>
- <graphics type='vnc' port='-1' listen='127.0.0.1'/>
- <video>
- <model type='cirrus' vram='9216' heads='1'/>
- </video>
- <console type='pty'/>
- </devices>
-</domain>
-";
-
-=item Sys::VirtV2V::Converter->convert(g, config, desc, dom, devices)
+=item Sys::VirtV2V::Converter->convert(g, config, desc, meta)
Instantiate an appropriate backend and call convert on it.
@@ -90,14 +69,9 @@ An initialised Sys::VirtV2V::Config object.
The OS description returned by Sys::Guestfs::Lib.
-=item dom
-
-An XML::DOM object resulting from parsing the guests's libvirt domain XML.
-
-=item devices
+=item meta
-An arrayref of libvirt storage device names, in the order they will be presented
-to the guest.
+Guest metadata.
=back
@@ -107,19 +81,18 @@ sub convert
{
my $class = shift;
- my ($g, $config, $desc, $dom, $devices) = @_;
+ my ($g, $config, $desc, $meta) = @_;
croak("convert called without g argument") unless defined($g);
croak("convert called without config argument") unless defined($config);
croak("convert called without desc argument") unless defined($desc);
- croak("convert called without dom argument") unless defined($dom);
- croak("convert called without devices argument") unless defined($devices);
+ croak("convert called without meta argument") unless defined($meta);
my $guestcaps;
# Find a module which can convert the guest and run it
foreach my $module ($class->modules()) {
if($module->can_handle($desc)) {
- $guestcaps = $module->convert($g, $config, $desc, $dom, $devices);
+ $guestcaps = $module->convert($g, $config, $desc, $meta);
last;
}
}
@@ -146,330 +119,14 @@ sub convert
};
}
- # Map network names from config
- _map_networks($dom, $config);
-
- # Convert the metadata
- _convert_metadata($dom, $desc, $devices, $guestcaps);
-
return $guestcaps;
}
-sub _convert_metadata
-{
- my ($dom, $desc, $devices, $guestcaps) = @_;
-
- my $default_dom = new XML::DOM::Parser->parse(KVM_DEFAULT_XML);
-
- # Replace source hypervisor metadata with KVM defaults
- _unconfigure_hvs($dom, $default_dom);
-
- # Remove any configuration related to a PV kernel bootloader
- _unconfigure_bootloaders($dom);
-
- # Update storage devices and drivers
- _configure_storage($dom, $devices, $guestcaps->{block});
-
- # Configure network drivers
- _configure_network($dom, $guestcaps->{net});
-
- # Ensure guest has a standard set of default devices
- _configure_default_devices($dom, $default_dom);
-
- # Add a default os section if none exists
- _configure_os($dom, $default_dom, $guestcaps->{arch});
-
- # Check for weird configs and sanitise them
- _sanity_check($dom);
-}
-
-sub _configure_os
-{
- my ($dom, $default_dom, $arch) = @_;
-
- my ($os) = $dom->findnodes('/domain/os');
-
- # If there's no os element, copy one from the default
- if(!defined($os)) {
- ($os) = $default_dom->findnodes('/domain/os');
- $os = $os->cloneNode(1);
- $os->setOwnerDocument($dom);
-
- my ($domain) = $dom->findnodes('/domain');
- $domain->appendChild($os);
- }
-
- my ($type) = $os->findnodes('type');
-
- # If there's no type element, copy one from the default
- if(!defined($type)) {
- ($type) = $default_dom->findnodes('/domain/os/type');
- $type = $type->cloneNode(1);
- $type->setOwnerDocument($dom);
-
- $os->appendChild($type);
- }
-
- # Set type/@arch based on the detected OS architecture
- $type->setAttribute('arch', $arch) if (defined($arch));
-}
-
-sub _configure_default_devices
-{
- my ($dom, $default_dom) = @_;
-
- my ($devices) = $dom->findnodes('/domain/devices');
-
- # Remove any existing input, graphics or video devices
- foreach my $input ($devices->findnodes('input | video | graphics')) {
- $devices->removeChild($input);
- }
-
- my ($input_devices) = $default_dom->findnodes('/domain/devices');
-
- # Add new default devices from default XML
- foreach my $input ($input_devices->findnodes('input | video | '.
- 'graphics | console')) {
- my $new = $input->cloneNode(1);
- $new->setOwnerDocument($devices->getOwnerDocument());
- $devices->appendChild($new);
- }
-}
-
-sub _unconfigure_bootloaders
-{
- my ($dom) = @_;
-
- # A list of paths which relate to assisted booting of a kernel on hvm
- my @bootloader_paths = (
- '/domain/os/loader',
- '/domain/os/kernel',
- '/domain/os/initrd',
- '/domain/os/root',
- '/domain/os/cmdline',
- '/domain/bootloader',
- '/domain/bootloader_args'
- );
-
- foreach my $path (@bootloader_paths) {
- my ($node) = $dom->findnodes($path);
- $node->getParentNode()->removeChild($node) if defined($node);
- }
-}
-
-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, $block) = @_;
-
- my $virtio = $block eq 'virtio' ? 1 : 0;
- my $prefix = $virtio == 1 ? 'vd' : 'hd';
-
- my @removed = ();
-
- my $suffix = 'a';
- foreach my $device (@$devices) {
- my ($target) =
$dom->findnodes("/domain/devices/disk[\@device='disk']/".
- "target[\@dev='$device']");
-
- die("Previously detected drive $device is no longer present in domain
".
- "XML: ".$dom->toString())
- unless (defined($target));
-
- # Don't add more than 4 IDE disks
- if (!$virtio && _suffixcmp($suffix, 'd') > 0) {
- push(@removed, "$device(disk)");
- } else {
- $target->setAttribute('bus', $block);
- $target->setAttribute('dev', $prefix.$suffix);
- $suffix++; # Perl magic means 'z'++ == 'aa'
- }
- }
-
- # Convert CD-ROM devices to IDE.
- $suffix = 'a' if ($virtio);
- foreach my $target
-
($dom->findnodes("/domain/devices/disk[\@device='cdrom']/target"))
- {
- if (_suffixcmp($suffix, 'd') <= 0) {
- $target->setAttribute('bus', 'ide');
- $target->setAttribute('dev', "hd$suffix");
- $suffix++;
- } else {
- push(@removed, $target->getAttribute('dev')."(cdrom)");
-
- my $disk = $target->getParentNode();
- $disk->getParentNode()->removeChild($disk);
- }
- }
-
- if (@removed > 0) {
- logmsg WARN, __x('Only 4 IDE devices are supported. The following '.
- 'drives have been removed: {list}',
- list => join(' ', @removed));
- }
-
- # As we just changed and unified all their underlying controllers, device
- # addresses are no longer relevant
- foreach my $address ($dom->findnodes('/domain/devices/disk/address')) {
- $address->getParentNode()->removeChild($address);
- }
-}
-
-sub _configure_network
-{
- my ($dom, $net) = @_;
-
- # Convert network adapters
- # N.B. <interface> is not required to have a <model> element, but
<model>
- # is required to have a type attribute
-
- # Convert interfaces which already have a model element
- foreach my $type
- ($dom->findnodes('/domain/devices/interface/model/@type'))
- {
- $type->setNodeValue($net);
- }
-
- # Add a model element to interfaces which don't have one
- foreach my $interface
- ($dom->findnodes('/domain/devices/interface[not(model)]'))
- {
- my $model = $dom->createElement('model');
- $model->setAttribute('type', $net);
- $interface->appendChild($model);
- }
-}
-
-sub _unconfigure_hvs
-{
- my ($dom, $default_dom) = @_;
- die("unconfigure_hvs called without dom argument")
- unless defined($dom);
- die("unconfigure_hvs called without default_dom argument")
- unless defined($default_dom);
-
- # Remove emulator if it is defined
- foreach my $emulator ($dom->findnodes('/domain/devices/emulator')) {
- $emulator->getParent()->removeChild($emulator);
- }
-
- # Remove any disk driver element other than 'qemu'
- foreach my $driver
- ($dom->findnodes('/domain/devices/disk/driver[@name !=
\'qemu\']'))
- {
- $driver->getParentNode()->removeChild($driver);
- }
-
- _unconfigure_xen_metadata($dom);
-}
-
-sub _unconfigure_xen_metadata
-{
- my ($dom) = @_;
-
- # The list of target xen-specific nodes is mostly taken from inspection of
- # domain.rng
-
- # Remove machine if it has a xen-specific value
- # We could replace it with the generic 'pc', but 'pc' is a moving
target
- # across QEMU releases. By removing it entirely, libvirt will automatically
- # add the latest machine type (e.g. pc-0.11), which is stable.
- foreach my $machine_type ($dom->findnodes('/domain/os/type/@machine')) {
- if ($machine_type->getNodeValue() =~ /(xenpv|xenfv|xenner)/) {
- my ($type) = $dom->findnodes('/domain/os/type[@machine = "'.
-
$machine_type->getNodeValue().'"]');
- $type->getAttributes()->removeNamedItem("machine");
- }
- }
-
- # Remove the script element if its path attribute is 'vif-bridge'
- foreach my $script ($dom->findnodes('/domain/devices/interface/script[@path =
"vif-bridge"]'))
- {
- $script->getParent()->removeChild($script);
- }
-
- # Other Xen related metadata is handled separately
- # /domain/@type
- # /domain/devices/input/@bus = xen
- # /domain/devices/disk/target/@bus = 'xen'
- # /domain/os/loader = 'xen'
- # /domain/bootloader
- # /domain/bootloader_args
-}
-
-sub _map_networks
-{
- my ($dom, $config) = @_;
-
- # Iterate over interfaces
- foreach my $if ($dom->findnodes('/domain/devices/interface'))
- {
- my $type = $if->getAttribute('type');
-
- my $name;
- if ($type eq 'bridge') {
- ($name) = $if->findnodes('source/@bridge');
- } elsif ($type eq 'network') {
- ($name) = $if->findnodes('source/@network');
- } else {
- v2vdie __x('Unknown interface type {type} in domain XML: {domain}',
- type => $type, domain => $dom->toString());
- }
-
- my ($newname, $newtype) = $config->map_network($name->getValue(),
- $type);
- next unless (defined($newname) && defined($newtype));
-
- my ($source) = $if->findnodes('source');
-
- # Replace @bridge or @network in the source element with the correct
- # mapped attribute name and value
- $source->removeAttributeNode($name);
- $source->setAttribute($newtype, $newname);
-
- # Update the type of the interface
- $if->setAttribute('type', $newtype);
- }
-}
-
-sub _sanity_check
-{
- my ($dom) = shift;
-
- # Check for multiple boot devices of the same type, which will cause KVM not
- # to start
- # Seen on RHEL 5 Xen
- my %devs;
- foreach my $boot ($dom->findnodes('/domain/os/boot')) {
- my $dev = $boot->getAttribute('dev');
-
- if (defined($dev) && !exists($devs{$dev})) {
- $devs{$dev} = 1;
- next;
- }
-
- # Delete nodes with no dev attribute, or that we've seen before
- $boot->getParentNode()->removeChild($boot);
- }
-}
-
=back
=head1 COPYRIGHT
-Copyright (C) 2009,2010 Red Hat Inc.
+Copyright (C) 2009-2011 Red Hat Inc.
=head1 LICENSE
diff --git a/lib/Sys/VirtV2V/Converter/RedHat.pm b/lib/Sys/VirtV2V/Converter/RedHat.pm
index ed52189..4f8bf2d 100644
--- a/lib/Sys/VirtV2V/Converter/RedHat.pm
+++ b/lib/Sys/VirtV2V/Converter/RedHat.pm
@@ -1,5 +1,5 @@
# Sys::VirtV2V::Converter::RedHat
-# Copyright (C) 2009,2010 Red Hat Inc.
+# Copyright (C) 2009-2011 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
@@ -41,7 +41,7 @@ Sys::VirtV2V::Converter::RedHat - Convert a Red Hat based guest to run
on KVM
use Sys::VirtV2V::Converter;
- Sys::VirtV2V::Converter->convert($g, $dom, $os);
+ Sys::VirtV2V::Converter->convert($g, $meta, $os);
=head1 DESCRIPTION
@@ -69,7 +69,7 @@ sub can_handle
$desc->{distro} =~ /^(rhel|fedora)$/);
}
-=item Sys::VirtV2V::Converter::RedHat->convert(g, config, dom, desc, $devices)
+=item Sys::VirtV2V::Converter::RedHat->convert(g, config, meta, desc)
Convert a Red Hat based guest. Assume that can_handle has previously returned 1.
@@ -87,14 +87,9 @@ An initialised Sys::VirtV2V::Config
A description of the guest OS as returned by Sys::Guestfs::Lib.
-=item dom
+=item meta
-A DOM representation of the guest's libvirt domain metadata
-
-=item devices
-
-An arrayref of libvirt storage device names, in the order they will be presented
-to the guest.
+Guest metadata.
=back
@@ -104,12 +99,11 @@ sub convert
{
my $class = shift;
- my ($g, $config, $desc, $dom, $devices) = @_;
+ my ($g, $config, $desc, $meta) = @_;
croak("convert called without g argument") unless defined($g);
croak("convert called without config argument") unless defined($config);
croak("convert called without desc argument") unless defined($desc);
- croak("convert called without dom argument") unless defined($dom);
- croak("convert called without devices argument") unless defined($devices);
+ croak("convert called without meta argument") unless defined($meta);
_init_selinux($g);
_init_augeas($g);
@@ -120,15 +114,15 @@ sub convert
_unconfigure_hv($g, $desc);
# Try to install the virtio capability
- my $virtio = _install_capability('virtio', $g, $config, $dom, $desc);
+ my $virtio = _install_capability('virtio', $g, $config, $meta, $desc);
# Get an appropriate kernel, and remove non-bootable kernels
- my $kernel = _configure_kernel($virtio, $g, $config, $desc, $dom);
+ my $kernel = _configure_kernel($virtio, $g, $config, $desc, $meta);
# Configure the rest of the system
_configure_console($g);
_configure_display_driver($g);
- _remap_block_devices($devices, $virtio, $g, $desc);
+ _remap_block_devices($meta, $virtio, $g, $desc);
_configure_kernel_modules($g, $desc, $virtio, $modpath);
_configure_boot($kernel, $virtio, $g, $desc);
@@ -534,7 +528,7 @@ sub _list_kernels
sub _configure_kernel
{
- my ($virtio, $g, $config, $desc, $dom) = @_;
+ my ($virtio, $g, $config, $desc, $meta) = @_;
# Pick first appropriate kernel returned by _list_kernels
my $boot_kernel;
@@ -555,7 +549,7 @@ sub _configure_kernel
# If none of the installed kernels are appropriate, install a new one
if(!defined($boot_kernel)) {
- $boot_kernel = _install_good_kernel($g, $config, $desc, $dom);
+ $boot_kernel = _install_good_kernel($g, $config, $desc, $meta);
}
# Check we have a bootable kernel.
@@ -807,7 +801,7 @@ sub _find_xen_kernel_modules
sub _install_capability
{
- my ($name, $g, $config, $dom, $desc) = @_;
+ my ($name, $g, $config, $meta, $desc) = @_;
my $cap;
eval {
@@ -869,7 +863,7 @@ sub _install_capability
# normal kernel replacement
if ($kernel_pkg eq "kernel-xen" || $kernel_pkg eq
"kernel-xenU") {
$kernel_pkg =
- _get_replacement_kernel_name($kernel_arch, $desc, $dom);
+ _get_replacement_kernel_name($kernel_arch, $desc, $meta);
# Check if we've got already got an appropriate kernel
my ($installed) =
@@ -1331,7 +1325,7 @@ sub _discover_kernel
sub _get_replacement_kernel_name
{
- my ($arch, $desc, $dom) = @_;
+ my ($arch, $desc, $meta) = @_;
# Make an informed choice about a replacement kernel for distros we know
# about
@@ -1355,24 +1349,14 @@ sub _get_replacement_kernel_name
# RHEL 4
elsif ($desc->{distro} eq 'rhel' && $desc->{major_version} eq
'4') {
- my ($ncpus) = $dom->findnodes('/domain/vcpu/text()');
- if (defined($ncpus)) {
- $ncpus = $ncpus->getData()
- } else {
- $ncpus = 1;
- }
-
if ($arch eq 'i686') {
- my ($mem_kb) = $dom->findnodes('/domain/memory/text()');
- $mem_kb = $mem_kb->getData();
-
# If the guest has > 10G RAM, give it a hugemem kernel
- if ($mem_kb > 10 * 1024 * 1024) {
+ if ($meta->{memory} > 10 * 1024 * 1024 * 1024) {
return 'kernel-hugemem';
}
# SMP kernel for guests with >1 CPU
- elsif ($ncpus > 1) {
+ elsif ($meta->{cpus} > 1) {
return 'kernel-smp';
}
@@ -1382,11 +1366,11 @@ sub _get_replacement_kernel_name
}
else {
- if ($ncpus > 8) {
+ if ($meta->{cpus} > 8) {
return 'kernel-largesmp';
}
- elsif ($ncpus > 1) {
+ elsif ($meta->{cpus} > 1) {
return 'kernel-smp';
}
else {
@@ -1405,14 +1389,14 @@ sub _get_replacement_kernel_name
sub _install_good_kernel
{
- my ($g, $config, $desc, $dom) = @_;
+ my ($g, $config, $desc, $meta) = @_;
my ($kernel_pkg, $kernel_rpmver, $kernel_arch) = _discover_kernel($desc);
# If the guest is using a Xen PV kernel, choose an appropriate
# normal kernel replacement
if ($kernel_pkg eq "kernel-xen" || $kernel_pkg eq "kernel-xenU")
{
- $kernel_pkg = _get_replacement_kernel_name($kernel_arch, $desc, $dom);
+ $kernel_pkg = _get_replacement_kernel_name($kernel_arch, $desc, $meta);
# Check there isn't already one installed
my ($kernel) = _get_installed("$kernel_pkg.$kernel_arch", $g);
@@ -1718,12 +1702,18 @@ sub _rpmvercmp
sub _remap_block_devices
{
- my ($devices, $virtio, $g, $desc) = @_;
+ my ($meta, $virtio, $g, $desc) = @_;
- # $devices contains an order list of devices, as named by the host. Because
+ my @devices = map { $_->{device} } @{$meta->{disks}};
+ @devices = sort @devices;
+
+ # @devices contains an ordered list of libvirt device names. Because
# libvirt uses a similar naming scheme to Linux, these will mostly be the
- # same names as used by the guest. However, if the guest is using libata,
- # IDE drives could be renamed.
+ # same names as used by the guest. They are ordered as they were passed to
+ # libguestfs, which means their device name in the appliance can be
+ # inferred.
+
+ # If the guest is using libata, IDE drives could be renamed.
# Modern distros use libata, and IDE devices are presented as sdX
my $libata = 1;
@@ -1778,7 +1768,7 @@ sub _remap_block_devices
if (exists($guestif{sd})) {
# Look for IDE and SCSI devices from the domain definition
my %domainif;
- foreach my $device (@$devices) {
+ foreach my $device (@devices) {
foreach my $type ('hd', 'sd') {
if ($device =~ m{^$type([a-z]+)}) {
$domainif{$type} ||= {};
@@ -1806,9 +1796,8 @@ sub _remap_block_devices
$letter++;
}
- # Be careful not to modify the original device list
my @newdevices;
- foreach my $device (@$devices) {
+ foreach my $device (@devices) {
my $map = $map{$device};
unless (defined($map)) {
@@ -1817,11 +1806,11 @@ sub _remap_block_devices
}
push(@newdevices, $map);
}
- $devices = \@newdevices;
+ @devices = @newdevices;
}
}
- # We now assume that $devices contains an ordered list of device names, as
+ # We now assume that @devices contains an ordered list of device names, as
# used by the guest. Create a map of old guest device names to new guest
# device names.
my %map;
@@ -1837,7 +1826,7 @@ sub _remap_block_devices
}
my $letter = 'a';
- foreach my $device (@$devices) {
+ foreach my $device (@devices) {
$map{$device} = $prefix.$letter;
$letter++;
}
@@ -2067,7 +2056,7 @@ sub _supports_virtio
=head1 COPYRIGHT
-Copyright (C) 2009,2010 Red Hat Inc.
+Copyright (C) 2009-2011 Red Hat Inc.
=head1 LICENSE
diff --git a/lib/Sys/VirtV2V/Converter/Windows.pm b/lib/Sys/VirtV2V/Converter/Windows.pm
index bda40a6..c9499f2 100644
--- a/lib/Sys/VirtV2V/Converter/Windows.pm
+++ b/lib/Sys/VirtV2V/Converter/Windows.pm
@@ -1,5 +1,5 @@
# Sys::VirtV2V::Converter::Windows
-# Copyright (C) 2009-2010 Red Hat Inc.
+# Copyright (C) 2009-2011 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
@@ -46,7 +46,7 @@ Sys::VirtV2V::Converter::Windows - Pre-convert a Windows guest to run on
KVM
use Sys::VirtV2V::Converter;
- Sys::VirtV2V::Converter->convert($g, $config, $desc, $dom, $devices);
+ Sys::VirtV2V::Converter->convert($g, $config, $desc, $meta);
=head1 DESCRIPTION
@@ -80,7 +80,7 @@ sub can_handle
return ($desc->{os} eq 'windows');
}
-=item Sys::VirtV2V::Converter::Windows->convert($g, $guestos, $desc, $devices,
$config)
+=item Sys::VirtV2V::Converter::Windows->convert($g, $guestos, $desc, $config)
(Pre-)convert a Windows guest. Assume that can_handle has previously
returned 1.
@@ -99,14 +99,9 @@ An initialised Sys::VirtV2V::Config object.
A description of the guest OS as returned by Sys::Guestfs::Lib.
-=item dom
+=item meta
-A DOM representation of the guest's libvirt domain metadata
-
-=item devices
-
-An arrayref of libvirt storage device names, in the order they will be
-presented to the guest.
+Guest metadata.
=back
@@ -116,21 +111,20 @@ sub convert
{
my $class = shift;
- my ($g, $config, $desc, undef, $devices) = @_;
+ my ($g, $config, $desc, undef) = @_;
croak("convert called without g argument") unless defined($g);
croak("convert called without config argument") unless defined($config);
croak("convert called without desc argument") unless defined($desc);
- croak("convert called without devices argument") unless defined($devices);
my $tmpdir = tempdir (CLEANUP => 1);
# Note: disks are already mounted by main virt-v2v script.
- _upload_files ($g, $tmpdir, $desc, $devices, $config);
- _add_viostor_to_registry ($g, $tmpdir, $desc, $devices, $config);
- _add_service_to_registry ($g, $tmpdir, $desc, $devices, $config);
+ _upload_files ($g, $tmpdir, $desc, $config);
+ _add_viostor_to_registry ($g, $tmpdir, $desc, $config);
+ _add_service_to_registry ($g, $tmpdir, $desc, $config);
my ($block, $net) =
- _prepare_virtio_drivers ($g, $tmpdir, $desc, $devices, $config);
+ _prepare_virtio_drivers ($g, $tmpdir, $desc, $config);
# Return guest capabilities.
my %guestcaps;
@@ -152,7 +146,6 @@ sub _add_viostor_to_registry
my $g = shift;
my $tmpdir = shift;
my $desc = shift;
- my $devices = shift;
my $config = shift;
# Locate and download the system registry.
@@ -255,7 +248,6 @@ sub _add_service_to_registry
my $g = shift;
my $tmpdir = shift;
my $desc = shift;
- my $devices = shift;
my $config = shift;
# Locate and download the system registry.
@@ -314,7 +306,6 @@ sub _prepare_virtio_drivers
my $g = shift;
my $tmpdir = shift;
my $desc = shift;
- my $devices = shift;
my $config = shift;
# Copy the target VirtIO drivers to the guest
@@ -439,7 +430,6 @@ sub _upload_files
my $g = shift;
my $tmpdir = shift;
my $desc = shift;
- my $devices = shift;
my $config = shift;
# Check we have all required files
@@ -487,7 +477,7 @@ sub _upload_files
=head1 COPYRIGHT
-Copyright (C) 2009-2010 Red Hat Inc.
+Copyright (C) 2009-2011 Red Hat Inc.
=head1 LICENSE
diff --git a/metadata-format.txt b/metadata-format.txt
new file mode 100644
index 0000000..6ae544c
--- /dev/null
+++ b/metadata-format.txt
@@ -0,0 +1,28 @@
+virt-v2v uses its own representation of a domain's metadata. It is based on, but
+differs significantly from, the XML representation used by libvirt.
+
+%
+ name The name of the domain
+ memory The memory assigned to the domain, in bytes
+ cpus The number of cpus assigned to the domain
+ arch The architecture of the domain (eg i686,x86_64)
+ features[] An array containing 'features', as defined by libvirt
+ disks[] An array containing hashrefs of disk descriptions
+ device The name of the disk as seen by the guest (eg sda)
+ path The path to the device's storage, as known to the source
+ or target hypervisor. This will probably not be a valid
+ local path
+ (local_path) A local path to the device's storage, as usable by
+ libguestfs during conversion. This is populated by
+ copy_storage()
+ is_block 1 if the device uses block storage, 0 if it uses a file.
+ format The file format used by the underlying storage, as known to
+ qemu (eg raw,qcow)
+ removables[] An array containing hashrefs of removable media devices
+ name The name of the device, as seen by the guest (eg fd0)
+ type The device type, as defined by libvirt (eg floppy,cdrom)
+ nics[] An array containing hashrefs of NIC descriptions
+ mac The mac address
+ vnet The name of the virtual network the NIC will connect to
+ vnet_type The type of virtual network the NIC will connect to, as
+ defined by libvirt (eg network,bridge)
diff --git a/v2v/virt-v2v.pl b/v2v/virt-v2v.pl
index 5212fab..9f603d3 100755
--- a/v2v/virt-v2v.pl
+++ b/v2v/virt-v2v.pl
@@ -1,6 +1,6 @@
#!/usr/bin/perl
# virt-v2v
-# Copyright (C) 2009 Red Hat Inc.
+# Copyright (C) 2009-2011 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
@@ -426,19 +426,23 @@ v2vdie __x('Domain {name} already exists on the target.',
$source->copy_storage($target, $output_format, $output_sparse);
# Get a libvirt configuration for the guest
-my $dom = $source->get_dom();
-exit(1) unless(defined($dom));
+my $meta = $source->get_meta();
+exit(1) unless(defined($meta));
-# Get a list of the guest's transfered storage devices
-my $storage = $source->get_storage_paths();
+v2vdie __('Guest doesn\'t define any storage devices')
+ unless @{$meta->{disks}} > 0;
# Create the transfer iso if required
my $transferiso;
$transferiso = $config->get_transfer_iso();
# Open a libguestfs handle on the guest's storage devices
-my $g = new Sys::VirtV2V::GuestfsHandle($storage, $transferiso,
- $output_method eq 'rhev');
+my @localpaths = map { $_->{local_path} } @{$meta->{disks}};
+my $g = new Sys::VirtV2V::GuestfsHandle(
+ \@localpaths,
+ $transferiso,
+ $output_method eq 'rhev'
+);
my $os;
my $guestcaps;
@@ -447,8 +451,7 @@ eval {
$os = inspect_guest($g);
# Modify the guest and its metadata
- $guestcaps = Sys::VirtV2V::Converter->convert($g, $config, $os, $dom,
- $source->get_storage_devices());
+ $guestcaps = Sys::VirtV2V::Converter->convert($g, $config, $os, $meta);
};
# If any of the above commands result in failure, we need to ensure that the
@@ -462,21 +465,20 @@ if ($@) {
$g->close();
-$target->create_guest($os, $dom, $guestcaps);
+$target->create_guest($os, $meta, $config, $guestcaps);
-my ($name) = $dom->findnodes('/domain/name/text()');
-$name = $name->getNodeValue();
if($guestcaps->{block} eq 'virtio' && $guestcaps->{net} eq
'virtio') {
- logmsg NOTICE, __x('{name} configured with virtio drivers.', name =>
$name);
+ logmsg NOTICE, __x('{name} configured with virtio drivers.',
+ name => $meta->{name});
} elsif ($guestcaps->{block} eq 'virtio') {
logmsg NOTICE, __x('{name} configured with virtio storage only.',
- name => $name);
+ name => $meta->{name});
} elsif ($guestcaps->{net} eq 'virtio') {
logmsg NOTICE, __x('{name} configured with virtio networking only.',
- name => $name);
+ name => $meta->{name});
} else {
logmsg NOTICE, __x('{name} configured without virtio drivers.',
- name => $name);
+ name => $meta->{name});
}
exit(0);
@@ -920,7 +922,7 @@ Matthew Booth <mbooth(a)redhat.com>
=head1 COPYRIGHT
-Copyright (C) 2009,2010 Red Hat Inc.
+Copyright (C) 2009-2011 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
--
1.7.4