[Libguestfs] [PATCH 1/6] Convert config file to XML, and translate networks/bridge for all connections

Matthew Booth mbooth at redhat.com
Tue Feb 9 16:01:14 UTC 2010


Previously, only the LibVirtXML connection translated network and bridge names
in imported metadata. This change moves this functionality in Converter, making
it available to LibVirt connections as well.

At the same time, the format of the config file is switched to XML. The primary
driver for this is that the allowable syntax of a foreign network/bridge name is
not known. Rather than create a new format which deals with this, I have
switched to an existing one.

Note that this change doesn't update GuestOS's use of the config file. Until
this is restored it is not possible to install software in a guest, and
therefore not possible to convert a PV xen guest.
---
 MANIFEST                                 |    2 +-
 lib/Sys/VirtV2V/Connection/LibVirtXML.pm |   73 +++-----------------------
 lib/Sys/VirtV2V/Converter.pm             |   81 +++++++++++++++++++++++++++++-
 v2v/virt-v2v.conf                        |   35 -------------
 v2v/virt-v2v.pl                          |   29 ++++++----
 v2v/virt-v2v.xml                         |   62 +++++++++++++++++++++++
 6 files changed, 169 insertions(+), 113 deletions(-)
 delete mode 100644 v2v/virt-v2v.conf
 create mode 100644 v2v/virt-v2v.xml

diff --git a/MANIFEST b/MANIFEST
index 3d6bf00..2513714 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -35,6 +35,6 @@ t/003-syntax.t
 t/004-ExecHelper.t
 TODO
 v2v/run-v2v-locally
-v2v/virt-v2v.conf
+v2v/virt-v2v.xml
 v2v/virt-v2v.conf.pod
 v2v/virt-v2v.pl
diff --git a/lib/Sys/VirtV2V/Connection/LibVirtXML.pm b/lib/Sys/VirtV2V/Connection/LibVirtXML.pm
index 6867a9b..5d0ebbc 100644
--- a/lib/Sys/VirtV2V/Connection/LibVirtXML.pm
+++ b/lib/Sys/VirtV2V/Connection/LibVirtXML.pm
@@ -39,7 +39,7 @@ Sys::VirtV2V::Connection::LibVirtXML - Read libvirt XML from a file
 
  use Sys::VirtV2V::Connection::LibVirtXML;
 
- $conn = Sys::VirtV2V::Connection::LibVirtXML->new($config, $path);
+ $conn = Sys::VirtV2V::Connection::LibVirtXML->new($path);
  $dom = $conn->get_dom();
 
 =head1 DESCRIPTION
@@ -52,10 +52,9 @@ file.
 
 =over
 
-=item new(config, path)
+=item new(path)
 
-Create a new LibVirtXML connection. Configuration for transforming the metadata
-is taken from I<config>, and the metadata itself is read from I<path>.
+Create a new LibVirtXML connection. The metadata itself is read from I<path>.
 
 =cut
 
@@ -63,38 +62,13 @@ sub new
 {
     my $class = shift;
 
-    my ($config, $path) = @_;
+    my ($path) = @_;
 
     my %obj = ();
     my $self = \%obj;
 
     bless($self, $class);
 
-    if(defined($config)) {
-        my %bridges;
-        my %networks;
-
-        $self->{bridges} = \%bridges;
-        $self->{networks} = \%networks;
-
-        # Split bridges and networks into separate hashes
-        foreach my $directive (keys(%$config)) {
-            if($directive =~ /^bridge\.(.*)$/) {
-                $bridges{$1} = $config->{$directive};
-            }
-
-            elsif($directive =~ /^network\.(.*)$/) {
-                $networks{$1} = $config->{$directive};
-            }
-
-            else {
-                die(__x("WARNING: unknown configuration directive ".
-                        "{directive} in {name} section.",
-                        directive => $directive, name => 'libvirtxml'));
-            }
-        }
-    }
-
     $self->_get_dom($path);
 
     # No transfer methods defined yet
@@ -109,12 +83,9 @@ sub _get_dom
 
     # Open the input file
     my $xml; # Implicitly closed on function exit
-    if(!open($xml, '<', $self->{path})) {
-        print STDERR user_message
-            (__x("Failed to open {path}: {error}",
-                 path => $self->{path}, error => $!));
-        return undef;
-    }
+    open($xml, '<', $self->{path})
+        or die(user_message(__x("Failed to open {path}: {error}",
+                                path => $self->{path}, error => $!)));
 
     # Parse the input file
     my $parser = new XML::DOM::Parser;
@@ -122,34 +93,8 @@ sub _get_dom
     eval { $dom = $parser->parse ($xml); };
 
     # Display any parse errors
-    if ($@) {
-        print STDERR user_message
-            (__x("Unable to parse {path}: {error}",
-                 path => $self->{path}, error => $@));
-        return undef;
-    }
-
-    # Rewrite bridge names
-    foreach my $bridge
-        ($dom->findnodes("/domain/devices/interface[\@type='bridge']/".
-                         "source/\@bridge"))
-    {
-        my $name = $bridge->getNodeValue();
-        if(exists($self->{bridges}->{$name})) {
-            $bridge->setNodeValue($self->{bridges}->{$name});
-        }
-    }
-
-    # Rewrite network names
-    foreach my $network
-        ($dom->findnodes("/domain/devices/interface[\@type='network']/".
-                         "source/\@network"))
-    {
-        my $name = $network->getNodeValue();
-        if(exists($self->{networks}->{$name})) {
-            $network->setNodeValue($self->{networks}->{$name});
-        }
-    }
+    die(user_message(__x("Unable to parse {path}: {error}",
+                         path => $self->{path}, error => $@))) if ($@);
 
     return $dom;
 }
diff --git a/lib/Sys/VirtV2V/Converter.pm b/lib/Sys/VirtV2V/Converter.pm
index 81abb02..4b11efd 100644
--- a/lib/Sys/VirtV2V/Converter.pm
+++ b/lib/Sys/VirtV2V/Converter.pm
@@ -130,9 +130,10 @@ sub convert
 {
     my $class = shift;
 
-    my ($vmm, $guestos, $dom, $desc) = @_;
+    my ($vmm, $guestos, $config, $dom, $desc) = @_;
     carp("convert called without vmm argument") unless defined($vmm);
     carp("convert called without guestos argument") unless defined($guestos);
+    carp("convert called without config argument") unless defined($config);
     carp("convert called without dom argument") unless defined($dom);
     carp("convert called without desc argument") unless defined($desc);
 
@@ -149,6 +150,9 @@ sub convert
     die(user_message(__"Unable to find a module to convert this guest"))
         unless (defined($guestcaps));
 
+    # Map network names from config
+    _map_networks($dom, $config);
+
     # Convert the metadata
     _convert_metadata($vmm, $dom, $desc, $guestcaps);
 
@@ -469,6 +473,81 @@ sub _unconfigure_xen_metadata
     # /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 {
+            print STDERR user_message (__x("Unknown interface type {type} in ".
+                                           "domain XML: {domain}",
+                                           type => $type,
+                                           domain => $dom->toString()));
+            exit(1);
+        }
+
+        _update_interface($if, $name, $type, $config);
+    }
+}
+
+sub _update_interface
+{
+    my ($if, $oldname, $oldtype, $config) = @_;
+
+    my $oldnameval = $oldname->getValue();
+    my ($mapping) = $config->findnodes
+        ("/virt-v2v/network[\@type='$oldtype' and \@name='$oldnameval']".
+         "/network");
+
+    unless (defined($mapping)) {
+        print STDERR user_message(__x("No mapping found for '{type}' ".
+                                      "interface: {name}",
+                                      type => $oldtype,
+                                      name => $oldnameval));
+        next;
+    }
+
+    my $newtype = $mapping->getAttributeNode('type');
+    $newtype &&= $newtype->getValue();
+    my $newname = $mapping->getAttributeNode('name');
+    $newname &&= $newname->getValue();
+
+    # Check type and name are defined for the mapping
+    unless (defined($newtype) && defined($newname)) {
+        print STDERR user_message(__x("WARNING: Invalid network ".
+                                      "mapping in config: {config}",
+                                      config => $mapping->toString()));
+        return;
+    }
+
+    # Check type is something we recognise
+    unless ($newtype eq 'network' || $newtype eq 'bridge') {
+        print STDERR user_message(__x("WARNING: Unknown interface type ".
+                                      "{type} in network mapping: {config}",
+                                      type => $newtype,
+                                      config => $mapping->toString()));
+    }
+
+    my ($source) = $if->findnodes('source');
+
+    # Replace @bridge or @network in the source element with the correct mapped
+    # attribute name and value
+    $source->removeAttributeNode($oldname);
+    $source->setAttribute($newtype, $newname);
+
+    # Update the type of the interface
+    $if->setAttribute('type', $newtype);
+}
+
 =back
 
 =head1 COPYRIGHT
diff --git a/v2v/virt-v2v.conf b/v2v/virt-v2v.conf
deleted file mode 100644
index 86e0b02..0000000
--- a/v2v/virt-v2v.conf
+++ /dev/null
@@ -1,35 +0,0 @@
-[aliases]
-#### RHEL 5
-# Install a regular kernel in place of a xen kernel
-rhel.5.kernel-xen=kernel
-rhel.4.kernel-xenU=kernel
-
-[files]
-rhel.5.i686.kernel=/var/lib/virt-v2v/kernel-2.6.18-128.1.14.el5.i686.rpm
-rhel.5.x86_64.kernel=/var/lib/virt-v2v/kernel-2.6.18-128.4.1.el5.x86_64.rpm
-
-# The RHEL 5.3 kernel conflicts with older versions of ecryptfs-utils
-rhel.5.i386.ecryptfs-utils=/var/lib/virt-v2v/ecryptfs-utils-56-8.el5.i386.rpm
-
-# The following userspace packages are required on RHEL 5 prior to RHEL 5.3 to
-# suport virtio
-rhel.5.i386.lvm2=/var/lib/virt-v2v/lvm2-2.02.40-6.el5.i386.rpm
-rhel.5.i386.device-mapper=/var/lib/virt-v2v/device-mapper-1.02.28-2.el5.i386.rpm
-rhel.5.i386.device-mapper-event=/var/lib/virt-v2v/device-mapper-event-1.02.28-2.el5.i386.rpm
-
-#### RHEL 4
-rhel.4.i686.kernel=/var/lib/virt-v2v/kernel-2.6.9-89.EL.i686.rpm
-rhel.4.x86_64.kernel=/var/lib/virt-v2v/kernel-2.6.9-89.0.3.EL.x86_64.rpm
-
-[deps]
-# Only update userspace on RHEL 5 prior to RHEL 5.3
-rhel.5.2.kernel=ecryptfs-utils lvm2
-rhel.5.1.kernel=ecryptfs-utils lvm2
-rhel.5.0.kernel=ecryptfs-utils lvm2
-
-# RPM version dependencies
-rhel.5.lvm2=device-mapper
-rhel.5.device-mapper=device-mapper-event
-
-[libvirtxml]
-bridge.xenbr1=virbr0
diff --git a/v2v/virt-v2v.pl b/v2v/virt-v2v.pl
index 091dc49..e475098 100755
--- a/v2v/virt-v2v.pl
+++ b/v2v/virt-v2v.pl
@@ -212,17 +212,21 @@ GetOptions ("help|?"      => sub {
 ) or pod2usage(2);
 
 # Read the config file if one was given
-my $config = {};
+my $config;
 if(defined($config_file)) {
-    $config = Config::Tiny->read($config_file);
+    # Check we can access the config file
+    die(user_message(__x("Config file {path} doesn't exist",
+                         path => $config_file))) unless (-e $config_file);
 
-    # Check we were able to read it
-    if(!defined($config)) {
-        print STDERR user_message(__x("Unable to parse {file}: {error}",
-                                      file => $config_file,
-                                      error => Config::Tiny->errstr));
-        exit(1);
-    }
+    die(user_message(__x("Don't have permissions to read {path}",
+                         path => $config_file))) unless (-r $config_file);
+
+    eval {
+        $config = new XML::DOM::Parser->parsefile($config_file);
+    };
+
+    die(user_message(__x("Unable to parse config file {path}: {error}",
+                         path => $config_file, error => $@))) if ($@);
 }
 
 # Connect to target libvirt
@@ -246,7 +250,7 @@ eval {
                      modulename => 'libvirtxml'));
         }
 
-        $conn = Sys::VirtV2V::Connection::LibVirtXML->new($config, $path);
+        $conn = Sys::VirtV2V::Connection::LibVirtXML->new($path);
     }
 
     elsif ($input_method eq "libvirt") {
@@ -293,7 +297,8 @@ if ($@) {
 }
 
 # Configure GuestOS ([files] and [deps] sections)
-Sys::VirtV2V::GuestOS->configure($config);
+# Need to fix GuestOS's usage of config for installing applications
+Sys::VirtV2V::GuestOS->configure({});
 
 
 ###############################################################################
@@ -316,7 +321,7 @@ my $os = inspect_guest($g);
 my $guestos = Sys::VirtV2V::GuestOS->instantiate($g, $os);
 
 # Modify the guest and its metadata for the target hypervisor
-Sys::VirtV2V::Converter->convert($vmm, $guestos, $dom, $os);
+Sys::VirtV2V::Converter->convert($vmm, $guestos, $config, $dom, $os);
 
 $g->umount_all();
 $g->sync();
diff --git a/v2v/virt-v2v.xml b/v2v/virt-v2v.xml
new file mode 100644
index 0000000..9d9a38b
--- /dev/null
+++ b/v2v/virt-v2v.xml
@@ -0,0 +1,62 @@
+<virt-v2v>
+  <path-root>/var/lib/virt-v2v/software</path-root>
+  <iso-path>/var/lib/virt-v2v/transfer.iso</iso-path>
+
+  <!-- RHEL 5
+       All of these RPMS are from RHEL 5.3, which was the first version of RHEL
+       5 to support VirtIO -->
+  <app os='rhel' major='5' arch='i686' name='kernel'>
+    <path>kernel-2.6.18-128.el5.i686.rpm</path>
+    <dep>ecryptfs-utils-56-8.el5.i386.rpm</dep>
+    <dep>lvm2-2.02.40-6.el5.i386.rpm</dep>
+    <dep>device-mapper-1.02.28-2.el5.i386.rpm</dep>
+    <dep>device-mapper-event-1.02.28-2.el5.i386.rpm</dep>
+  </app>
+  <app os='rhel' major='5' arch='i686' name='kernel-PAE'>
+    <path>kernel-PAE-2.6.18-128.el5.i686.rpm</path>
+    <dep>ecryptfs-utils-56-8.el5.i386.rpm</dep>
+    <dep>lvm2-2.02.40-6.el5.i386.rpm</dep>
+    <dep>device-mapper-1.02.28-2.el5.i386.rpm</dep>
+    <dep>device-mapper-event-1.02.28-2.el5.i386.rpm</dep>
+  </app>
+  <app os='rhel' major='5' arch='x86_64' name='kernel'>
+    <path>kernel-2.6.18-128.el5.x86_64.rpm</path>
+    <dep>ecryptfs-utils-56-8.el5.x86_64.rpm</dep>
+    <dep>lvm2-2.02.40-6.el5.x86_64.rpm</dep>
+    <dep>device-mapper-1.02.28-2.el5.x86_64.rpm</dep>
+    <dep>device-mapper-event-1.02.28-2.el5.x86_64.rpm</dep>
+  </app>
+
+  <!-- RHEL 4
+       All of these RPMs are from RHEL 4.8, which was the first version of RHEL
+       4 to support VirtIO -->
+  <app os='rhel' major='4' arch='i686' name='kernel'>
+    <path>kernel-2.6.9-89.EL.i686.rpm</path>
+  </app>
+  <app os='rhel' major='4' arch='i686' name='kernel-smp'>
+    <path>kernel-smp-2.6.9-89.EL.i686.rpm</path>
+  </app>
+  <app os='rhel' major='4' arch='i686' name='kernel-hugemem'>
+    <path>kernel-hugemem-2.6.9-89.EL.i686.rpm</path>
+  </app>
+  <app os='rhel' major='4' arch='x86_64' name='kernel'>
+    <path>kernel-2.6.9-89.EL.x86_64.rpm</path>
+  </app>
+  <app os='rhel' major='4' arch='x86_64' name='kernel-smp'>
+    <path>kernel-smp-2.6.9-89.EL.x86_64.rpm</path>
+  </app>
+  <app os='rhel' major='4' arch='x86_64' name='kernel-largesmp'>
+    <path>kernel-largesmp-2.6.9-89.EL.x86_64.rpm</path>
+  </app>
+
+  <!-- Networks -->
+  <!-- The default Xen bridge name -->
+  <network type='bridge' name='xenbr1'>
+    <network type='network' name='default'/>
+  </network>
+
+  <!-- The default ESX bridge name -->
+  <network type='bridge' name='VM Network'>
+    <network type='network' name='default'/>
+  </network>
+</virt-v2v>
-- 
1.6.6




More information about the Libguestfs mailing list