[et-mgmt-tools] [PATCH 07 of 11] Implement network device handling

john.levon at sun.com john.levon at sun.com
Mon Jul 7 22:51:37 UTC 2008


# HG changeset patch
# User john.levon at sun.com
# Date 1215470437 25200
# Node ID 20daa7230dc53978e5f67fabf7950d961ed3bdd8
# Parent  a8f5d5be978f0237f41c35275936863c2787a73b
Implement network device handling

Also clean up disk processing somewhat.

Signed-off-by: John Levon <john.levon at sun.com>

diff --git a/virt-convert b/virt-convert
--- a/virt-convert
+++ b/virt-convert
@@ -24,8 +24,8 @@
 #
 # Convert between VM definition formats. TODO:
 #
-# support networking
 # port virtinstance.py to Solaris
+# networking support is nowhere near complete
 # better disk checking
 # support CD-ROM
 # add option for host selection
@@ -212,9 +212,9 @@ def main():
     try:
         for d in vmdef.disks:
             verbose(options, "Converting disk \"%s\" to type %s..." %
-                (d.path, vmconfig.qemu_formats[vmconfig.DISK_TYPE_RAW]))
+                (d.path, vmconfig.qemu_formats[vmconfig.DISK_FORMAT_RAW]))
             d.convert(options.input_dir, options.output_dir,
-                vmconfig.DISK_TYPE_RAW)
+                vmconfig.DISK_FORMAT_RAW)
     except OSError, e:
         cleanup("Couldn't convert disks: %s" % e.strerror, options, created_dir)
     except RuntimeError, e:
diff --git a/virtconv/parsers/virtimage.py b/virtconv/parsers/virtimage.py
--- a/virtconv/parsers/virtimage.py
+++ b/virtconv/parsers/virtimage.py
@@ -26,7 +26,7 @@ pv_boot_template = """
    <guest>
     <arch>%(arch)s</arch>
     <features>
-     <pae/>
+     <pae />
     </features>
    </guest>
    <os>
@@ -42,7 +42,7 @@ hvm_boot_template = """
     <arch>%(arch)s</arch>
    </guest>
    <os>
-    <loader dev="hd"/>
+    <loader dev="hd" />
    </os>
    %(hvm_disks)s
   </boot>
@@ -60,8 +60,8 @@ image_template = """
   <devices>
    <vcpu>%(nr_vcpus)s</vcpu>
    <memory>%(memory)s</memory>
-   <interface/>
-   <graphics/>
+   %(interface)s
+   <graphics />
   </devices>
  </domain>
  <storage>
@@ -114,18 +114,26 @@ class virtimage_parser(vmconfig.parser):
         storage_disks = []
 
         # create disk filename lists for xml template
-        for disk in vm.disks:
-            number = disk.number
+        for devid, disk in sorted(vm.disks.iteritems()):
+            if disk.type != vmconfig.DISK_TYPE_DISK:
+                continue
+
             path = disk.path
+            drive_nr = ascii_letters[int(devid[1]) % 26]
 
             # FIXME: needs updating for later Xen enhancements; need to
             # implement capabilities checking for max disks etc.
             pv_disks.append("""<drive disk="%s" target="xvd%s" />\n""" %
-                (path, ascii_letters[number % 26]))
+                (path, drive_nr))
             hvm_disks.append("""<drive disk="%s" target="hd%s" />\n""" %
-                (path, ascii_letters[number % 26]))
+                (path, drive_nr))
             storage_disks.append(
                 """<disk file="%s" use="system" format="raw"/>\n""" % path)
+
+        # Hmm.  Any interface is a good interface?
+        interface = None
+        if len(vm.netdevs):
+            interface = "<interface />"
 
         if vm.type == vmconfig.VM_TYPE_PV:
             boot_template = pv_boot_template
@@ -145,6 +153,7 @@ class virtimage_parser(vmconfig.parser):
             "nr_vcpus" : vm.nr_vcpus,
             # Mb to Kb
             "memory" : int(vm.memory) * 1024,
+            "interface" : interface,
             "storage" : "".join(storage_disks),
         }
 
diff --git a/virtconv/parsers/virtinstance.py b/virtconv/parsers/virtinstance.py
--- a/virtconv/parsers/virtinstance.py
+++ b/virtconv/parsers/virtinstance.py
@@ -54,6 +54,14 @@ disk_template = """
  <source dev='%(path)s' />
  <target dev='%(prefix)s%(dev)s' />
 </disk>
+"""
+
+netdev_template = """
+<interface type='%(type)s'>
+ %(source)s
+ %(mac)s
+ %(model)s
+</interface>
 """
 
 instance_template = """
@@ -77,6 +85,7 @@ instance_template = """
   <devices>
     %(emulator)s
     %(disks)s
+    %(netdevs)s
     <input type='mouse' bus='ps2' />
     <input type='tablet' bus='usb' />
     <graphics type='vnc' port='-1' />
@@ -95,6 +104,11 @@ class virtinstance_parser(vmconfig.parse
     example, the pygrub path.  For now, we'll assume it's for the current
     platform.  In the future we might want to consider adding a
     --host-type option.
+
+    Known limitations:
+
+    - Only handles bridge, ethernet, and network type netdevs
+    - doesn't handle netdev script or IP address
     """
 
     name = "virt-instance"
@@ -142,16 +156,54 @@ class virtinstance_parser(vmconfig.parse
             disk_prefix = "hd"
 
         disks = []
-
-        for disk in vm.disks:
-            drive_nr = ascii_letters[disk.number % 26]
+        netdevs = []
+
+        for devid, disk in sorted(vm.disks.iteritems()):
+            if disk.type != vmconfig.DISK_TYPE_DISK:
+                continue
 
             # FIXME: needs updating for later Xen enhancements; need to
             # implement capabilities checking for max disks etc.
+            drive_nr = ascii_letters[int(devid[1]) % 26]
+
             disks.append(disk_template % {
                 "path" : disk.path,
                 "prefix" : disk_prefix,
                 "dev" :  drive_nr
+            })
+
+        for number, netdev in sorted(vm.netdevs.iteritems()):
+            mac = ""
+            if netdev.mac != "auto":
+                mac = "<mac address='%s' />" % netdev.mac
+
+            nettype = {
+                vmconfig.NETDEV_TYPE_DEV : "ethernet",
+                vmconfig.NETDEV_TYPE_BRIDGE : "bridge",
+                vmconfig.NETDEV_TYPE_UNKNOWN : "bridge",
+                vmconfig.NETDEV_TYPE_NETWORK : "network",
+            }.get(netdev.type)
+
+            source = ""
+            if netdev.source:
+                srcattr = nettype
+                if netdev.type == vmconfig.NETDEV_TYPE_DEV:
+                    srcattr = "dev"
+                source = "<source %s='%s' />" % (srcattr, netdev.source)
+
+            # FIXME: should warn here
+            if not nettype:
+                continue
+
+            model = ""
+            if netdev.driver:
+                model = "<model type='%s' />" % netdev.driver
+
+            netdevs.append(netdev_template % {
+                "type" : nettype,
+                "source" : source,
+                "mac" : mac,
+                "model": model,
             })
 
         out = instance_template % {
@@ -164,6 +216,7 @@ class virtinstance_parser(vmconfig.parse
             "memory" : int(vm.memory) * 1024,
             "nr_vcpus" : vm.nr_vcpus,
             "emulator" : emulators[vm.type],
+            "netdevs" : "".join(netdevs),
             "disks" : "".join(disks),
             "console" : consoles[vm.type],
         }
diff --git a/virtconv/parsers/vmx.py b/virtconv/parsers/vmx.py
--- a/virtconv/parsers/vmx.py
+++ b/virtconv/parsers/vmx.py
@@ -20,7 +20,68 @@
 
 import virtconv.vmconfig as vmconfig
 import re
+import os
 
+def parse_disk_entry(vm, fullkey, value):
+    """
+    Parse a particular key/value for a disk.  FIXME: this should be a
+    lot smarter.
+    """
+
+    # skip bus values, e.g. 'scsi0.present = "TRUE"'
+    if re.match(r"^(scsi|ide)[0-9]+[^:]", fullkey):
+        return
+
+    # FIXME: we don't check bus number, we should
+    _, bus, _, inst, key = re.split(r"^(scsi|ide)([0-9]+):([0-9]+)\.",
+        fullkey)
+
+    lvalue = value.lower()
+
+    if key == "present" and lvalue == "false":
+        return
+
+    devid = (bus, inst)
+    if not vm.disks.get(devid):
+        vm.disks[devid] = vmconfig.disk(bus = bus,
+            type = vmconfig.DISK_TYPE_DISK)
+
+    if key == "deviceType":
+        if lvalue == "atapi-cdrom" or lvalue == "cdrom-raw":
+            vm.disks[devid].type = vmconfig.DISK_TYPE_CDROM
+        elif lvalue == "cdrom-image":
+            vm.disks[devid].type = vmconfig.DISK_TYPE_ISO
+
+    if key == "fileName":
+        vm.disks[devid].path = value
+        vm.disks[devid].format = vmconfig.DISK_FORMAT_RAW
+        if lvalue.endswith(".vmdk"):
+            vm.disks[devid].format = vmconfig.DISK_FORMAT_VMDK
+
+def parse_netdev_entry(vm, fullkey, value):
+    """
+    Parse a particular key/value for a network.  Throws ValueError.
+    """
+
+    _, _, inst, key = re.split("^(ethernet)([0-9]+).", fullkey)
+
+    lvalue = value.lower()
+
+    if key == "present" and lvalue == "false":
+        return
+
+    if not vm.netdevs.get(inst):
+        vm.netdevs[inst] = vmconfig.netdev(type = vmconfig.NETDEV_TYPE_UNKNOWN)
+
+    # "vlance", "vmxnet", "e1000"
+    if key == "virtualDev":
+        vm.netdevs[inst].driver = lvalue
+    if key == "addressType" and lvalue == "generated":
+        vm.netdevs[inst].mac = "auto"
+    # we ignore .generatedAddress for auto mode
+    if key == "address":
+        vm.netdevs[inst].mac = lvalue
+   
 class vmx_parser(vmconfig.parser):
     """
     Support for VMWare .vmx files.  Note that documentation is
@@ -71,22 +132,31 @@ class vmx_parser(vmconfig.parser):
                 lines.append(line)
     
         config = {}
-        disks = []
 
         # split out all remaining entries of key = value form
         for (line_nr, line) in enumerate(lines):
             try:
                 before_eq, after_eq = line.split("=", 1)
-                key = before_eq.replace(" ","")
-                value = after_eq.replace('"',"")
-                value = value.strip()
+                key = before_eq.strip()
+                value = after_eq.strip().strip('"')
                 config[key] = value
-                # FIXME: this should probably be a lot smarter.
-                if value.endswith(".vmdk"):
-                    disks += [ value ]
+
+                if key.startswith("scsi") or key.startswith("ide"):
+                    parse_disk_entry(vm, key, value)
+                if key.startswith("ethernet"):
+                    parse_netdev_entry(vm, key, value)
             except:
                 raise Exception("Syntax error at line %d: %s" %
                     (line_nr + 1, line.strip()))
+
+        for devid, disk in vm.disks.iteritems():
+            if disk.type == vmconfig.DISK_TYPE_DISK:
+                continue
+                
+            # vmx files often have dross left in path for CD entries
+            if (disk.path == "auto detect" or
+                not os.path.exists(disk.path)):
+                vm.disks[devid].path = None
 
         if not config.get("displayName"):
             raise ValueError("No displayName defined in \"%s\"" % input_file)
@@ -95,10 +165,7 @@ class vmx_parser(vmconfig.parser):
         vm.memory = config.get("memsize")
         vm.description = config.get("annotation")
         vm.nr_vcpus = config.get("numvcpus")
-
-        for (number, path) in enumerate(disks):
-            vm.disks += [ vmconfig.disk(path, number, vmconfig.DISK_TYPE_VMDK) ]
-
+     
         vm.validate()
         return vm
 
diff --git a/virtconv/vmconfig.py b/virtconv/vmconfig.py
--- a/virtconv/vmconfig.py
+++ b/virtconv/vmconfig.py
@@ -25,40 +25,54 @@ VM_TYPE_PV = 0
 VM_TYPE_PV = 0
 VM_TYPE_HVM = 1
 
-DISK_TYPE_RAW = 0
-DISK_TYPE_VMDK = 1
+NETDEV_TYPE_UNKNOWN = 0
+NETDEV_TYPE_DEV = 1
+NETDEV_TYPE_BRIDGE = 2
+NETDEV_TYPE_NETWORK = 3
+
+DISK_FORMAT_RAW = 0
+DISK_FORMAT_VMDK = 1
+
+DISK_TYPE_DISK = 0
+DISK_TYPE_CDROM = 1
+DISK_TYPE_ISO = 2
 
 disk_suffixes = {
-    DISK_TYPE_RAW: ".img",
-    DISK_TYPE_VMDK: ".vmdk",
+    DISK_FORMAT_RAW: ".img",
+    DISK_FORMAT_VMDK: ".vmdk",
 }
 
 qemu_formats = {
-    DISK_TYPE_RAW: "raw",
-    DISK_TYPE_VMDK: "vmdk",
+    DISK_FORMAT_RAW: "raw",
+    DISK_FORMAT_VMDK: "vmdk",
 }
 
 class disk(object):
     """Definition of an individual disk instance."""
 
-    def __init__(self, path = None, number = None, type = None):
+    def __init__(self, path = None, format = None, bus = "ide",
+        type = DISK_TYPE_DISK):
         self.path = path
-        self.number = number
+        self.format = format
+        self.bus = bus
         self.type = type
 
-    def convert(self, input_dir, output_dir, output_type):
+    def convert(self, input_dir, output_dir, output_format):
         """
         Convert a disk into the requested format if possible, in the
-        given output directory.  Raises NotImplementedError or other
+        given output directory.  Raises RuntimeError or other
         failures.
         """
 
-        if self.type == output_type:
+        if self.format == output_format:
             return
 
-        if output_type != DISK_TYPE_RAW:
-            raise NotImplementedError("Cannot convert to disk type %d" %
-                output_type)
+        if self.type != DISK_TYPE_DISK:
+            return
+
+        if output_format != DISK_FORMAT_RAW:
+            raise NotImplementedError("Cannot convert to disk format %d" %
+                output_format)
 
         infile = self.path
 
@@ -70,11 +84,11 @@ class disk(object):
         if os.path.isabs(outfile):
             outfile = os.path.basename(outfile)
 
-        outfile = outfile.replace(disk_suffixes[self.type],
-            disk_suffixes[output_type]).strip()
+        outfile = outfile.replace(disk_suffixes[self.format],
+            disk_suffixes[output_format]).strip()
 
         convert_cmd = ("qemu-img convert \"%s\" -O %s \"%s\"" %
-            (infile, qemu_formats[output_type],
+            (infile, qemu_formats[output_format],
             os.path.join(output_dir, outfile)))
 
         ret = os.system(convert_cmd)
@@ -83,8 +97,22 @@ class disk(object):
 
         # Note: this is the *relative* path still
         self.path = outfile
-        self.type = output_type
-
+        self.format = output_format
+
+class netdev(object):
+    """Definition of an individual network device."""
+
+    def __init__(self, mac = "auto", type = NETDEV_TYPE_UNKNOWN,
+        source = None, driver = None):
+        """@mac: either a MAC address, or "auto"
+           @type: NETDEV_TYPE_*
+           @source: bridge or net device, or network name
+           @driver: device emulated for VM (e.g. vmxnet)
+           """ 
+        self.mac = mac
+        self.type = type
+        self.source = source
+        self.driver = driver
 
 class vm(object):
     """
@@ -111,7 +139,8 @@ class vm(object):
         self.description = None
         self.memory = None
         self.nr_vcpus = None
-        self.disks = [ ]
+        self.disks = {}
+        self.netdevs = {}
         self.type = VM_TYPE_HVM
         self.arch = "i686" # FIXME?
 
@@ -132,7 +161,11 @@ class vm(object):
         if not self.arch:
             raise ValueError("VM arch is not set")
 
-        
+        for (bus, inst), disk in self.disks.iteritems():
+            if not disk.path and disk.type == DISK_TYPE_DISK:
+                raise ValueError("Disk device %s:%d has no path." %
+                    (bus, inst))
+
 class parser(object):
     """
     Base class for particular config file format definitions of




More information about the et-mgmt-tools mailing list