[Libguestfs] [PATCH 3/3] Converter: Move HVSource functionality into Converter

Matthew Booth mbooth at redhat.com
Mon Dec 21 17:56:55 UTC 2009


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




More information about the Libguestfs mailing list