Remove HVSource, moving it into Converter. This makes the Converter more like a
'big script'.
At the same time, separate metadata changes from guest changes. Metadata changes
all move into Converter.pm. Guest changes move into Linux.pm.
---
MANIFEST | 2 -
lib/Sys/VirtV2V/Converter.pm | 352 +++++++++++++++++++++++++++++---
lib/Sys/VirtV2V/Converter/Linux.pm | 360 ++++++++++-----------------------
lib/Sys/VirtV2V/HVSource.pm | 178 ----------------
lib/Sys/VirtV2V/HVSource/Xen/Linux.pm | 273 -------------------------
5 files changed, 437 insertions(+), 728 deletions(-)
delete mode 100644 lib/Sys/VirtV2V/HVSource.pm
delete mode 100644 lib/Sys/VirtV2V/HVSource/Xen/Linux.pm
diff --git a/MANIFEST b/MANIFEST
index 8710a84..8603430 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -6,8 +6,6 @@ lib/Sys/VirtV2V.pm
lib/Sys/VirtV2V/ExecHelper.pm
lib/Sys/VirtV2V/GuestOS.pm
lib/Sys/VirtV2V/GuestOS/RedHat.pm
-lib/Sys/VirtV2V/HVSource.pm
-lib/Sys/VirtV2V/HVSource/Xen/Linux.pm
lib/Sys/VirtV2V/Converter.pm
lib/Sys/VirtV2V/Converter/Linux.pm
lib/Sys/VirtV2V/MetadataReader.pm
diff --git a/lib/Sys/VirtV2V/Converter.pm b/lib/Sys/VirtV2V/Converter.pm
index e78e940..3b0500b 100644
--- a/lib/Sys/VirtV2V/Converter.pm
+++ b/lib/Sys/VirtV2V/Converter.pm
@@ -26,6 +26,8 @@ use Module::Pluggable sub_name => 'modules',
use Locale::TextDomain 'virt-v2v';
+use Sys::VirtV2V::UserMessage qw(user_message);
+
=pod
=head1 NAME
@@ -49,6 +51,47 @@ OS, and uses it to convert the guest to run on KVM.
=over
+=cut
+
+# Default values for a KVM configuration
+use constant KVM_XML_VIRTIO => "
+<domain type='kvm'>
+ <os>
+ <type machine='pc'>hvm</type>
+ <boot dev='hd'/>
+ </os>
+ <devices>
+ <disk device='disk'>
+ <target bus='virtio'/>
+ </disk>
+ <interface type='network'>
+ <model type='virtio'/>
+ </interface>
+ <input type='mouse' bus='ps2'/>
+ <graphics type='vnc' port='-1' listen='127.0.0.1'/>
+ </devices>
+</domain>
+";
+
+use constant KVM_XML_NOVIRTIO => "
+<domain type='kvm'>
+ <os>
+ <type machine='pc'>hvm</type>
+ <boot dev='hd'/>
+ </os>
+ <devices>
+ <disk device='disk'>
+ <target bus='scsi'/>
+ </disk>
+ <interface type='network'>
+ <model type='e1000'/>
+ </interface>
+ <input type='mouse' bus='ps2'/>
+ <graphics type='vnc' port='-1' listen='127.0.0.1'/>
+ </devices>
+</domain>
+";
+
=item Sys::VirtV2V::Converter->convert(vmm, guestos, dom, desc)
Instantiate an appropriate backend and call convert on it.
@@ -85,60 +128,317 @@ sub convert
carp("convert called without dom argument") unless defined($dom);
carp("convert called without desc argument") unless defined($desc);
- # Find a module which can convert this guest and run it
+ my $guestcaps;
+
+ # Find a module which can convert the guest and run it
foreach my $module ($class->modules()) {
if($module->can_handle($desc)) {
- $module->convert($vmm, $guestos, $dom, $desc);
- return;
+ $guestcaps = $module->convert($vmm, $guestos, $dom, $desc);
+ last;
}
}
- die(__"Unable to find a module to convert this guest");
+ die(__"Unable to find a module to convert this guest")
+ unless (defined($guestcaps));
+
+ # Convert the metadata
+ _configure_metadata($vmm, $dom, $desc, $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));
+ }
}
-=back
+sub _configure_metadata
+{
+ my ($vmm, $dom, $desc, $guestcaps) = @_;
-=head1 BACKEND INTERFACE
+ my $arch = $guestcaps->{arch};
+ my $virtio = $guestcaps->{virtio};
-=over
+ my $default_dom;
+ if($virtio) {
+ $default_dom = new XML::DOM::Parser->parse(KVM_XML_VIRTIO);
+ } else {
+ $default_dom = new XML::DOM::Parser->parse(KVM_XML_NOVIRTIO);
+ }
-=item CLASS->can_handle(desc)
+ # Replace source hypervisor metadata with KVM defaults
+ _unconfigure_hvs($dom, $default_dom);
-Returns 1 if the backend can handle the guest described by $desc, 0 otherwise.
+ # Configure guest according to local hypervisor's capabilities
+ _configure_capabilities($dom, $vmm, $guestcaps);
-=over
+ # Remove any configuration related to a PV kernel bootloader
+ _unconfigure_bootloaders($dom);
-=item desc
+ # Configure network and block drivers in the guest
+ _configure_drivers($dom, $virtio);
-An OS description as returned by Sys::Guestfs::Lib.
+ # Add a default os section if none exists
+ _configure_os($dom, $default_dom, $arch);
+}
-=back
+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);
+
+ # Get a list of source HV specific metadata nodes
+ my @nodeinfo = _find_hv_metadata($dom);
+
+ for(my $i = 0; $i < $#nodeinfo; $i += 2) {
+ my $node = $nodeinfo[$i];
+ my $xpath = $nodeinfo[$i + 1]->[0];
+ my $required = $nodeinfo[$i + 1]->[1];
+
+ # Look for a replacement in the defaults
+ my ($default) = $default_dom->findnodes($xpath);
+ if(defined($default)) {
+ if($node->isa('XML::DOM::Attr')) {
+ $node->setNodeValue($default->getNodeValue());
+ } else {
+ my $replacement = $default->cloneNode(1);
+ $replacement->setOwnerDocument($dom);
+
+ $node->getParentNode()->replaceChild($replacement, $node);
+ }
+ }
-=item CLASS->convert(vmm, guestos, dom, desc)
+ else {
+ # Warn if a replacement is required, but none was found
+ print STDERR user_message
+ (__x("WARNING: No replacement found for {xpath} in ".
+ "domain XML. The node was removed.",
+ xpath => $xpath)) if($required);
-Convert the target guest to run on KVM.
+ $node->getParentNode()->removeChild($node);
+ }
+ }
+}
-can_handle() must have been checked prior to running convert().
+sub _configure_os
+{
+ my ($dom, $default_dom, $arch) = @_;
-=over
+ my ($os) = $dom->findnodes('/domain/os');
-=item vmm
+ # 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);
-A Sys::Virt connection.
+ my ($domain) = $dom->findnodes('/domain');
+ $domain->appendChild($os);
+ }
-=item guestos
+ my ($type) = $os->findnodes('type');
-An initialised Sys::VirtV2V::GuestOS object for the guest.
+ # 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);
-=item dom
+ $os->appendChild($type);
+ }
-An XML::DOM object resulting from parsing the guests's libvirt domain XML.
+ # Set type/@arch unless it's already set
+ my $arch_attr = $type->getAttributes()->getNamedItem('arch');
+ $type->setAttribute('arch', $arch) unless(defined($arch_attr));
+}
-=item desc
+sub _configure_capabilities
+{
+ my ($dom, $vmm, $guestcaps) = @_;
-The OS description returned by Sys::Guestfs::Lib.
+ # Parse the capabilities of the connected libvirt
+ my $caps = new XML::DOM::Parser->parse($vmm->get_capabilities());
-=back
+ 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}.",
+ machine => $machine->getValue()));
+
+ my ($type) = $dom->findnodes('/domain/os/type');
+ $type->getAttributes()->removeNamedItem('machine');
+ }
+ }
+
+ # 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 ($dom->findnodes('/domain/features/*')) {
+ if (!exists($features{$feature->getNodeName()})) {
+ print STDERR user_message
+ (__x("The connected hypervisor does not support ".
+ "feature {feature}", feature =>
$feature->getNodeName()));
+ $feature->getParentNode()->removeChild($feature);
+ }
+ }
+}
+
+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'
+ );
+
+ foreach my $path (@bootloader_paths) {
+ my ($node) = $dom->findnodes($path);
+ $node->getParentNode()->removeChild($node) if defined($node);
+ }
+}
+
+sub _configure_drivers
+{
+ my ($dom, $virtio) = @_;
+
+ # Convert disks
+ # N.B. <disk> is required to have a <target> element
+
+ # Convert alternate bus specifications
+ foreach my $bus ($dom->findnodes('/domain/devices/disk/target/@bus')) {
+ $bus->setNodeValue($virtio ? 'virtio' : 'scsi');
+ }
+
+ # Add an explicit bus specification to targets without one
+ foreach my $target
+ ($dom->findnodes('/domain/devices/disk/target[not(@bus)]'))
+ {
+ $target->setAttribute('bus', $virtio ? 'virtio' :
'scsi');
+ }
+
+ # 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($virtio ? 'virtio' : 'e1000');
+ }
+
+ # 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', $virtio ? 'virtio' :
'e1000');
+ $interface->appendChild($model);
+ }
+}
+
+sub _find_hv_metadata
+{
+ my ($dom) = @_;
+
+ return _find_xen_metadata($dom);
+}
+
+sub _find_xen_metadata
+{
+ my $dom = shift;
+ defined($dom) or carp("find_metadata called without dom argument");
+
+ # List of nodes requiring changes if they exist and match a particular
+ # pattern, and whether they need to be replaced for a guest to function
+ # Most of this is taken from inspection of domain.rng
+ my @check_nodes = (
+ [ '/domain/@type', 'xen', 1 ],
+ [ '/domain/devices/emulator', 'xen', 0 ],
+ [ '/domain/devices/input/@bus', 'xen', 1 ],
+ [ '/domain/devices/interface/script/@path', 'vif-bridge', 0],
+ [ '/domain/os/loader', 'xen', 0 ],
+ [ '/domain/os/type/@machine', '(xenpv|xenfv|xenner)', 0 ],
+ [ '/domain/devices/disk/target/@bus', 'xen', 0 ],
+ [ '/domain/bootloader', undef, 0],
+ [ '/domain/bootloader_args', undef, 0]
+ );
+
+ my @nodeinfo = ();
+ foreach my $check_node (@check_nodes) {
+ my $xpath = $check_node->[0];
+ my $pattern = $check_node->[1];
+ my $required = $check_node->[2];
+
+ foreach my $node ($dom->findnodes($xpath)) {
+ if(defined($pattern)) {
+ my $value;
+ if($node->isa('XML::DOM::Attr')) {
+ $value = $node->getNodeValue();
+ } else {
+ my ($text) = $node->findnodes('text()');
+ $value = $text->getNodeValue();
+ }
+
+ next unless($value =~ m{$pattern});
+ }
+
+ push(@nodeinfo, $node => [ $xpath, $required ]);
+ }
+ }
+
+ return @nodeinfo;
+}
=back
diff --git a/lib/Sys/VirtV2V/Converter/Linux.pm b/lib/Sys/VirtV2V/Converter/Linux.pm
index 070c4f3..db7a1ed 100644
--- a/lib/Sys/VirtV2V/Converter/Linux.pm
+++ b/lib/Sys/VirtV2V/Converter/Linux.pm
@@ -26,7 +26,6 @@ use Locale::TextDomain 'virt-v2v';
use XML::DOM;
use XML::DOM::XPath;
-use Sys::VirtV2V::HVSource;
use Sys::VirtV2V::UserMessage qw(user_message);
use Carp;
@@ -54,47 +53,6 @@ implementation of the Sys::VirtV2V::Converter interface.
=over
-=cut
-
-# Default values for a KVM configuration
-use constant KVM_XML_VIRTIO => "
-<domain type='kvm'>
- <os>
- <type machine='pc'>hvm</type>
- <boot dev='hd'/>
- </os>
- <devices>
- <disk device='disk'>
- <target bus='virtio'/>
- </disk>
- <interface type='network'>
- <model type='virtio'/>
- </interface>
- <input type='mouse' bus='ps2'/>
- <graphics type='vnc' port='-1' listen='127.0.0.1'/>
- </devices>
-</domain>
-";
-
-use constant KVM_XML_NOVIRTIO => "
-<domain type='kvm'>
- <os>
- <type machine='pc'>hvm</type>
- <boot dev='hd'/>
- </os>
- <devices>
- <disk device='disk'>
- <target bus='scsi'/>
- </disk>
- <interface type='network'>
- <model type='e1000'/>
- </interface>
- <input type='mouse' bus='ps2'/>
- <graphics type='vnc' port='-1' listen='127.0.0.1'/>
- </devices>
-</domain>
-";
-
=item Sys::VirtV2V::Converter::Linux->can_handle(desc)
See BACKEND INTERFACE in L<Sys::VirtV2V::Converter> for details.
@@ -129,7 +87,7 @@ sub convert
# Un-configure HV specific attributes which don't require a direct
# replacement
- Sys::VirtV2V::HVSource->unconfigure($guestos, $desc);
+ _unconfigure_hv($guestos, $desc);
# Get the best available kernel
my $kernel = _configure_kernel($guestos, $desc);
@@ -143,19 +101,12 @@ sub convert
_configure_kernel_modules($guestos, $desc, $virtio);
_configure_boot($guestos, $kernel, $virtio);
- # Configure libvirt
- _configure_metadata($vmm, $dom, $desc, $virtio);
+ my %guestcaps;
- my ($name) = $dom->findnodes('/domain/name/text()');
- $name = $name->getNodeValue();
+ $guestcaps{virtio} = $virtio;
+ $guestcaps{arch} = _get_os_arch($desc);
- if($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 _remap_block_devices
@@ -205,7 +156,7 @@ sub _configure_kernel_modules
# Get a list of all old-hypervisor specific kernel modules which need to be
# replaced or removed
my %hvs_modules;
- foreach my $module (Sys::VirtV2V::HVSource->find_kernel_modules($guestos)) {
+ foreach my $module (_find_hv_kernel_modules($guestos)) {
$hvs_modules{$module} = undef;
}
@@ -312,11 +263,14 @@ sub _configure_kernel
my @remove_kernels = ();
- # Remove old-HV kernels
- foreach my $kernel (Sys::VirtV2V::HVSource->find_kernels($desc)) {
+ # Remove foreign hypervisor specific kernels from the list of available
+ # kernels
+ foreach my $kernel (_find_hv_kernels($desc)) {
# Remove the kernel from our cache
delete($kernels{$kernel});
+ # Don't actually try to remove them yet in case we remove them all. This
+ # would make your dependency checker unhappy.
push(@remove_kernels, $kernel);
}
@@ -341,8 +295,7 @@ sub _configure_kernel
"specified in configuration.\nUnable to continue."))
unless(keys(%kernels) > 0 || defined($boot_kernel));
- # Remove old kernels. We do this after installing a new kernel to keep rpm
- # happy
+ # It's safe to remove kernels now
foreach my $kernel (@remove_kernels) {
# Uninstall the kernel from the guest
$guestos->remove_kernel($kernel);
@@ -397,240 +350,149 @@ sub _get_os_arch
return $arch;
}
-sub _configure_metadata
+# Return a list of foreign hypervisor specific kernels
+sub _find_hv_kernels
{
- my ($vmm, $dom, $desc, $virtio) = @_;
- die("configure_metadata called without vmm argument")
- unless defined($vmm);
- die("configure_metadata called without dom argument")
- unless defined($dom);
- die("configure_metadata called without desc argument")
- unless defined($desc);
- die("configure_metadata called without virtio argument")
- unless defined($virtio);
-
- my $default_dom;
- if($virtio) {
- $default_dom = new XML::DOM::Parser->parse(KVM_XML_VIRTIO);
- } else {
- $default_dom = new XML::DOM::Parser->parse(KVM_XML_NOVIRTIO);
- }
+ my $desc = shift;
- my $arch = _get_os_arch($desc);
+ my $boot = $desc->{boot};
+ return () unless(defined($boot));
- # Replace source hypervisor metadata with KVM defaults
- _unconfigure_hvs($dom, $default_dom);
+ my $configs = $desc->{boot}->{configs};
+ return () unless(defined($configs));
- # Configure guest according to local hypervisor's capabilities
- _configure_capabilities($dom, $vmm, $arch);
+ # Xen PV kernels can be distinguished from other kernels by their inclusion
+ # of the xennet driver
+ my @kernels = ();
+ foreach my $config (@$configs) {
+ my $kernel = $config->{kernel};
+ next unless(defined($kernel));
- # Remove any configuration related to a PV kernel bootloader
- _unconfigure_bootloaders($dom);
+ my $modules = $kernel->{modules};
+ next unless(defined($modules));
- # Configure network and block drivers in the guest
- _configure_drivers($dom, $virtio);
+ # Look for the xennet driver in the modules list
+ if(grep(/^xennet$/, @$modules) > 0) {
+ push(@kernels, $kernel->{version});
+ }
+ }
- # Add a default os section if none exists
- _configure_os($dom, $default_dom, $arch);
+ return @kernels;
}
-sub _unconfigure_hvs
+sub _unconfigure_hv
{
- 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);
-
- # Get a list of source HV specific metadata nodes
- my @nodeinfo = Sys::VirtV2V::HVSource->find_metadata($dom);
-
- for(my $i = 0; $i < $#nodeinfo; $i += 2) {
- my $node = $nodeinfo[$i];
- my $xpath = $nodeinfo[$i + 1]->[0];
- my $required = $nodeinfo[$i + 1]->[1];
-
- # Look for a replacement in the defaults
- my ($default) = $default_dom->findnodes($xpath);
- if(defined($default)) {
- if($node->isa('XML::DOM::Attr')) {
- $node->setNodeValue($default->getNodeValue());
- } else {
- my $replacement = $default->cloneNode(1);
- $replacement->setOwnerDocument($dom);
-
- $node->getParentNode()->replaceChild($replacement, $node);
- }
- }
-
- else {
- # Warn if a replacement is required, but none was found
- print STDERR user_message
- (__x("WARNING: No replacement found for {xpath} in ".
- "domain XML. The node was removed.",
- xpath => $xpath)) if($required);
+ my ($guestos, $desc) = @_;
- $node->getParentNode()->removeChild($node);
- }
- }
+ _unconfigure_xen($guestos, $desc);
}
-sub _configure_os
+# Unconfigure Xen specific guest modifications
+sub _unconfigure_xen
{
- 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 ($guestos, $desc) = @_;
- my ($domain) = $dom->findnodes('/domain');
- $domain->appendChild($os);
- }
+ carp("unconfigure called without guestos argument")
+ unless defined($guestos);
+ carp("unconfigure called without desc argument")
+ unless defined($desc);
- my ($type) = $os->findnodes('type');
+ my $found_kmod = 0;
- # 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);
+ # Look for kmod-xenpv-*, which can be found on RHEL 3 machines
+ foreach my $app (@{$desc->{apps}}) {
+ my $name = $app->{name};
- $os->appendChild($type);
+ if($name =~ /^kmod-xenpv(-.*)?$/) {
+ $guestos->remove_application($name);
+ $found_kmod = 1;
+ }
}
- # Set type/@arch unless it's already set
- my $arch_attr = $type->getAttributes()->getNamedItem('arch');
- $type->setAttribute('arch', $arch) unless(defined($arch_attr));
-}
-
-sub _configure_capabilities
-{
- my ($dom, $vmm, $arch) = @_;
-
- # Parse the capabilities of the connected libvirt
- my $caps = new XML::DOM::Parser->parse($vmm->get_capabilities());
-
- (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));
+ # Undo related nastiness if kmod-xenpv was installed
+ if($found_kmod) {
+ # What follows is custom nastiness, so we need to use the libguestfs
+ # handle directly
+ my $g = $guestos->get_handle();
- # Ensure that /domain/@type = 'kvm'
- my ($type) = $dom->findnodes('/domain/@type');
- $type->setNodeValue('kvm');
+ # kmod-xenpv modules may have been manually copied to other kernels.
+ # Hunt them down and destroy them.
+ foreach my $dir (grep(m{/xenpv$}, $g->find('/lib/modules'))) {
+ $dir = '/lib/modules/'.$dir;
- # 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 it's a directory
+ next unless($g->is_dir($dir));
- # 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()");
+ # Check it's not owned by an installed application
+ eval {
+ $g->get_application_owner($dir);
+ };
- my $found = 0;
- foreach my $machine_cap (@machine_caps) {
- if($machine eq $machine_cap) {
- $found = 1;
- last;
+ # Remove it if get_application_owner didn't find an owner
+ if($@) {
+ $g->rm_rf($dir);
}
}
- # 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}.",
- machine => $machine->getValue()));
+ # rc.local may contain an insmod or modprobe of the xen-vbd driver
+ my @rc_local = ();
+ eval {
+ @rc_local = $g->read_lines('/etc/rc.local');
+ };
- my ($type) = $dom->findnodes('/domain/os/type');
- $type->getAttributes()->removeNamedItem('machine');
+ if($@) {
+ print STDERR user_message(__x("Unable to open /etc/rc.local: ".
+ "{error}", error => $@));
}
- }
- # 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;
- }
+ else {
+ my $size = 0;
- foreach my $feature ($dom->findnodes('/domain/features/*')) {
- if(!exists($features{$feature->getNodeName()})) {
- print STDERR user_message
- (__x("The connected hypervisor does not support ".
- "feature {feature}", feature =>
$feature->getNodeName()));
- $feature->getParentNode()->removeChild($feature);
+ foreach my $line (@rc_local) {
+ if($line =~ /\b(insmod|modprobe)\b.*\bxen-vbd/) {
+ $line = '#'.$line;
+ }
+
+ $size += length($line) + 1;
+ }
+
+ $g->write_file('/etc/rc.local', join("\n",
@rc_local)."\n", $size);
}
}
}
-sub _unconfigure_bootloaders
+# Get a list of all foreign hypervisor specific kernel modules which are being
+# used by the guest
+sub _find_hv_kernel_modules
{
- 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'
- );
-
- foreach my $path (@bootloader_paths) {
- my ($node) = $dom->findnodes($path);
- $node->getParentNode()->removeChild($node) if defined($node);
- }
+ my ($desc) = @_;
+
+ return _find_xen_kernel_modules($desc);
}
-sub _configure_drivers
+# Get a list of xen specific kernel modules which are being used by the guest
+sub _find_xen_kernel_modules
{
- my ($dom, $virtio) = @_;
-
- # Convert disks
- # N.B. <disk> is required to have a <target> element
-
- # Convert alternate bus specifications
- foreach my $bus ($dom->findnodes('/domain/devices/disk/target/@bus')) {
- $bus->setNodeValue($virtio ? 'virtio' : 'scsi');
- }
+ my ($desc) = @_;
+ carp("find_kernel_modules called without desc argument")
+ unless defined($desc);
- # Add an explicit bus specification to targets without one
- foreach my $target
- ($dom->findnodes('/domain/devices/disk/target[not(@bus)]'))
- {
- $target->setAttribute('bus', $virtio ? 'virtio' :
'scsi');
- }
+ my $aliases = $desc->{modprobe_aliases};
+ return unless defined($aliases);
- # Convert network adapters
- # N.B. <interface> is not required to have a <model> element, but
<model>
- # is required to have a type attribute
+ my @modules = ();
+ foreach my $alias (keys(%$aliases)) {
+ my $modulename = $aliases->{$alias}->{modulename};
- # Convert interfaces which already have a model element
- foreach my $type
- ($dom->findnodes('/domain/devices/interface/model/@type'))
- {
- $type->setNodeValue($virtio ? 'virtio' : 'e1000');
+ foreach my $xen_module qw(xennet xen-vnif xenblk xen-vbd) {
+ if($modulename eq $xen_module) {
+ push(@modules, $alias);
+ last;
+ }
+ }
}
- # 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', $virtio ? 'virtio' :
'e1000');
- $interface->appendChild($model);
- }
+ return @modules;
}
=back
diff --git a/lib/Sys/VirtV2V/HVSource.pm b/lib/Sys/VirtV2V/HVSource.pm
deleted file mode 100644
index 621d54b..0000000
--- a/lib/Sys/VirtV2V/HVSource.pm
+++ /dev/null
@@ -1,178 +0,0 @@
-# Sys::VirtV2V::HVSource
-# Copyright (C) 2009 Red Hat Inc.
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-package Sys::VirtV2V::HVSource;
-
-use strict;
-use warnings;
-
-use Module::Pluggable sub_name => 'modules',
- search_path => ['Sys::VirtV2V::HVSource'],
- require => 1;
-use Carp;
-
-=pod
-
-=head1 NAME
-
-Sys::VirtV2V::HVSource - Discover source hypervisor artifacts in a guest
-
-=head1 SYNOPSIS
-
- use Sys::VirtV2V::HVSource;
-
- my @modules = Sys::VirtV2V::HVSource->find_kernel_modules(desc);
- my @kernels = Sys::VirtV2V::HVSource->find_kernels(desc);
- my @xpaths = Sys::VirtV2V::HVSource->find_metadata(dom);
-
- Sys::VirtV2V::HVSource->unconfigure(g, guestos, desc);
-
-=head1 DESCRIPTION
-
-Sys::VirtV2V::HVSource provides a mechanism for identifying properties of a
-guest operating system which relate specifically to a particular hypervisor. It
-is used by a Sys::VirtV2V::Converter when reconfiguring the guest.
-
-A call to any of these methods will call, and aggregate if relevant, all
-implemented Sys::VirtV2V::HVSource backends.
-
-=head1 METHODS
-
-In each of these methods, the desc argument is an OS description as returned by
-Sys::Guestfs::Lib.
-
-=over
-
-=item Sys::VirtV2V::HVSource->find_kernel_modules(desc)
-
-Return a list of modprobe aliases which load hypervisor-specific modules.
-
-=cut
-
-sub find_kernel_modules
-{
- my $class = shift;
-
- my $desc = shift;
- carp("find_kernel_modules called without desc argument")
- unless defined($desc);
-
- my @modules = ();
- foreach my $module ($class->modules()) {
- push(@modules, $module->find_kernel_modules($desc));
- }
-
- return @modules;
-}
-
-=item Sys::VirtV2V::HVSource->find_kernels(desc)
-
-Return a list of version numbers of kernels which will only boot on a specific
-hypervisor.
-
-=cut
-
-sub find_kernels
-{
- my $class = shift;
-
- my $desc = shift;
- carp("find_kernels called without desc argument")
- unless defined($desc);
-
- my @kernels = ();
- foreach my $module ($class->modules()) {
- push(@kernels, $module->find_kernels($desc));
- }
-
- return @kernels;
-}
-
-=item Sys::VirtV2V::HVSource->find_metadata(dom)
-
-Return guest libvirt metadata which is specific to a particular hypervisor. The
-data is returned as a list of XPath paths which relate to the guest's libvirt
-domain XML.
-
-=over
-
-=item dom
-
-An XML::DOM resulting from parsing the guest's libvirt domain XML.
-
-=back
-
-=cut
-
-sub find_metadata
-{
- my $class = shift;
-
- my $dom = shift;
- carp("find_metadata called without dom argument") unless defined($dom);
-
- my @nodeinfo = ();
- foreach my $module ($class->modules()) {
- push(@nodeinfo, $module->find_metadata($dom));
- }
-
- return @nodeinfo;
-}
-
-=item Sys::VirtV2V::HVSource->unconfigure(guestos, desc)
-
-Perform custom unconfiguration tasks. These tasks differ from the above, in they
-require no replacement configuration. Examples are removing VMWare tools or Xen
-PV drivers.
-
-=cut
-
-sub unconfigure
-{
- my $class = shift;
-
- my ($guestos, $desc) = @_;
- carp("unconfigure called without guestos argument")
- unless defined($guestos);
- carp("unconfigure called without desc argument")
- unless defined($desc);
-
- foreach my $module ($class->modules()) {
- $module->unconfigure($guestos, $desc);
- }
-}
-
-=back
-
-=head1 COPYRIGHT
-
-Copyright (C) 2009 Red Hat Inc.
-
-=head1 LICENSE
-
-Please see the file COPYING.LIB for the full license.
-
-=head1 SEE ALSO
-
-L<virt-v2v(1)>,
-L<Sys::Guestfs::Lib(3pm)>,
-L<Sys::VirtV2V(3pm)>,
-L<http://libguestfs.org/>.
-
-=cut
-
-1;
diff --git a/lib/Sys/VirtV2V/HVSource/Xen/Linux.pm
b/lib/Sys/VirtV2V/HVSource/Xen/Linux.pm
deleted file mode 100644
index e7bcd11..0000000
--- a/lib/Sys/VirtV2V/HVSource/Xen/Linux.pm
+++ /dev/null
@@ -1,273 +0,0 @@
-# Sys::VirtV2V::HVSource::Xen::Linux
-# Copyright (C) 2009 Red Hat Inc.
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-package Sys::VirtV2V::HVSource::Xen::Linux;
-
-use strict;
-use warnings;
-
-use Locale::TextDomain 'virt-v2v';
-
-use XML::DOM;
-use XML::DOM::XPath;
-
-=pod
-
-=head1 NAME
-
-Sys::VirtV2V::HVSource::Xen::Linux - Discover Xen artifects in a Linux guest
-
-=head1 SYNOPSIS
-
- use Sys::VirtV2V::HVSource;
-
- my @modules = Sys::VirtV2V::HVSource->find_kernel_modules(desc);
- my @kernels = Sys::VirtV2V::HVSource->find_kernels(desc);
- my @xpaths = Sys::VirtV2V::HVSource->find_metadata(dom);
-
- Sys::VirtV2V::HVSource->unconfigure(g, guestos, desc);
-
-=head1 DESCRIPTION
-
-Sys::VirtV2V::HVSource::Xen::Linux is a backend to Sys::VirtV2V::HVSource which detects
properties of a Linux guest specific to the Xen hypervisor.
-
-=head1 METHODS
-
-=over
-
-=item Sys::VirtV2V::HVSource::Linux->find_kernel_modules(desc)
-
-See L<Sys::VirtV2V::HVSource> for details.
-
-=cut
-
-sub find_kernel_modules
-{
- my $class = shift;
-
- my $desc = shift;
- carp("find_kernel_modules called without desc argument")
- unless defined($desc);
-
- my $aliases = $desc->{modprobe_aliases};
- return unless defined($aliases);
-
- my @modules = ();
- foreach my $alias (keys(%$aliases)) {
- my $modulename = $aliases->{$alias}->{modulename};
-
- foreach my $xen_module qw(xennet xen-vnif xenblk xen-vbd) {
- if($modulename eq $xen_module) {
- push(@modules, $alias);
- last;
- }
- }
- }
-
- return @modules;
-}
-
-=item Sys::VirtV2V::HVSource::Linux->find_kernels(desc)
-
-See L<Sys::VirtV2V::HVSource> for details.
-
-=cut
-
-sub find_kernels
-{
- my $class = shift;
-
- my $desc = shift;
- carp("find_kernels called without desc argument")
- unless defined($desc);
-
- my $boot = $desc->{boot};
- return () unless(defined($boot));
-
- my $configs = $desc->{boot}->{configs};
- return () unless(defined($configs));
-
- my @kernels = ();
- foreach my $config (@$configs) {
- my $kernel = $config->{kernel};
- next unless(defined($kernel));
-
- my $modules = $kernel->{modules};
- next unless(defined($modules));
-
- # Look for the xennet driver in the modules list
- if(grep(/^xennet$/, @$modules) > 0) {
- push(@kernels, $kernel->{version});
- }
- }
-
- return @kernels;
-}
-
-=item Sys::VirtV2V::HVSource::Linux->find_metadata(dom)
-
-See L<Sys::VirtV2V::HVSource> for details.
-
-=cut
-
-sub find_metadata
-{
- my $class = shift;
-
- my $dom = shift;
- defined($dom) or carp("find_metadata called without dom argument");
-
- # List of nodes requiring changes if they exist and match a particular
- # pattern, and whether they need to be replaced for a guest to function
- # Most of this is taken from inspection of domain.rng
- my @check_nodes = (
- [ '/domain/@type', 'xen', 1 ],
- [ '/domain/devices/emulator', 'xen', 0 ],
- [ '/domain/devices/input/@bus', 'xen', 1 ],
- [ '/domain/devices/interface/script/@path', 'vif-bridge', 0],
- [ '/domain/os/loader', 'xen', 0 ],
- [ '/domain/os/type/@machine', '(xenpv|xenfv|xenner)', 0 ],
- [ '/domain/devices/disk/target/@bus', 'xen', 0 ],
- [ '/domain/bootloader', undef, 0],
- [ '/domain/bootloader_args', undef, 0]
- );
-
- my @nodeinfo = ();
- foreach my $check_node (@check_nodes) {
- my $xpath = $check_node->[0];
- my $pattern = $check_node->[1];
- my $required = $check_node->[2];
-
- foreach my $node ($dom->findnodes($xpath)) {
- if(defined($pattern)) {
- my $value;
- if($node->isa('XML::DOM::Attr')) {
- $value = $node->getNodeValue();
- } else {
- my ($text) = $node->findnodes('text()');
- $value = $text->getNodeValue();
- }
-
- next unless($value =~ m{$pattern});
- }
-
- push(@nodeinfo, $node => [ $xpath, $required ]);
- }
- }
-
- return @nodeinfo;
-}
-
-=item Sys::VirtV2V::HVSource::Linux->unconfigure(guestos, desc)
-
-See L<Sys::VirtV2V::HVSource> for details.
-
-=cut
-
-sub unconfigure
-{
- my $class = shift;
-
- my ($guestos, $desc) = @_;
- carp("unconfigure called without guestos argument")
- unless defined($guestos);
- carp("unconfigure called without desc argument")
- unless defined($desc);
-
- my $found_kmod = 0;
-
- # Look for kmod-xenpv-*, which can be found on RHEL 3 machines
- foreach my $app (@{$desc->{apps}}) {
- my $name = $app->{name};
-
- if($name =~ /^kmod-xenpv(-.*)?$/) {
- $guestos->remove_application($name);
- $found_kmod = 1;
- }
- }
-
- # Undo related nastiness if kmod-xenpv was installed
- if($found_kmod) {
- # What follows is custom nastiness, so we need to use the libguestfs
- # handle directly
- my $g = $guestos->get_handle();
-
- # kmod-xenpv modules may have been manually copied to other kernels.
- # Hunt them down and destroy them.
- foreach my $dir (grep(m{/xenpv$}, $g->find('/lib/modules'))) {
- $dir = '/lib/modules/'.$dir;
-
- # Check it's a directory
- next unless($g->is_dir($dir));
-
- # Check it's not owned by an installed application
- eval {
- $g->get_application_owner($dir);
- };
-
- # Remove it if get_application_owner didn't find an owner
- if($@) {
- $g->rm_rf($dir);
- }
- }
-
- # rc.local may contain an insmod or modprobe of the xen-vbd driver
- my @rc_local = ();
- eval {
- @rc_local = $g->read_lines('/etc/rc.local');
- };
-
- if($@) {
- print STDERR user_message(__x("Unable to open /etc/rc.local: ".
- "{error}", error => $@));
- }
-
- else {
- my $size = 0;
-
- foreach my $line (@rc_local) {
- if($line =~ /\b(insmod|modprobe)\b.*\bxen-vbd/) {
- $line = '#'.$line;
- }
-
- $size += length($line) + 1;
- }
-
- $g->write_file('/etc/rc.local', join("\n",
@rc_local)."\n", $size);
- }
- }
-}
-
-=back
-
-=head1 COPYRIGHT
-
-Copyright (C) 2009 Red Hat Inc.
-
-=head1 LICENSE
-
-Please see the file COPYING.LIB for the full license.
-
-=head1 SEE ALSO
-
-L<Sys::VirtV2V::HVSource(3pm)>,
-L<virt-v2v(2)>,
-L<http://libguestfs.org/>.
-
-=cut
-
-1;
--
1.6.5.2