[et-mgmt-tools] [PATCH 07 of 11] virt-convert: improve disk handling

john.levon at sun.com john.levon at sun.com
Thu Jul 10 13:48:37 UTC 2008


# HG changeset patch
# User john.levon at sun.com
# Date 1215697571 25200
# Node ID 8ffd21ddf5c72ddbe8a30f65f0761e2f39705d57
# Parent  91d463e8e9921844271a90f416e0e6d94fd2af40
virt-convert: improve disk handling

A whole bunch of fixes for more accurate disk handling.

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

diff --git a/virt-convert b/virt-convert
--- a/virt-convert
+++ b/virt-convert
@@ -25,7 +25,6 @@
 import os
 import logging
 import errno
-import platform
 from optparse import OptionParser
 
 import virtinst.cli as cli
@@ -125,15 +124,15 @@
     logging.error(msg)
 
     try:
+        for disk in vmdef.disks.values():
+            disk.cleanup()
+
         paths.reverse()
         for path in paths:
             if os.path.isdir(path):
                 os.rmdir(path)
             elif os.path.isfile(path):
                 os.remove(path)
-
-        for disk in vmdef.disks:
-            disk.cleanup()
     except OSError, e:
         logging.error("Couldn't clean up output directory \"%s\": %s" %
             (options.output_dir, e.strerror))
@@ -196,18 +195,18 @@
         (options.output_format, options.output_dir))
 
     try:
-        for d in vmdef.disks:
+        for d in vmdef.disks.values():
             format = options.disk_format
 
-            # no auto-conversion on Solaris for VMDK disks
+            # VMDK disks on Solaris converted to vdisk by default
             if (d.format == diskcfg.DISK_FORMAT_VMDK and
-                not format and platform.system() == "SunOS"):
-                continue
+                not format and vmcfg.host() == "SunOS"):
+                format = "vdisk"
 
             if not format:
                 format = "raw"
 
-            if format != "none":
+            if d.path and format != "none":
                 verbose(options, "Converting disk \"%s\" to type %s..." %
                     (d.path, format))
 
diff --git a/virtconv/diskcfg.py b/virtconv/diskcfg.py
--- a/virtconv/diskcfg.py
+++ b/virtconv/diskcfg.py
@@ -64,11 +64,10 @@
 class disk(object):
     """Definition of an individual disk instance."""
 
-    def __init__(self, path = None, number = 0, format = None, bus = "ide",
+    def __init__(self, path = None, format = None, bus = "ide",
         type = DISK_TYPE_DISK):
         self.path = path
         self.format = format
-        self.number = number
         self.bus = bus
         self.type = type
         self.clean = []
@@ -140,6 +139,9 @@
         failures.
         """
 
+        if self.type != DISK_TYPE_DISK:
+            return
+
         out_format = disk_format_names[output_format]
         indir = os.path.normpath(os.path.abspath(indir))
         outdir = os.path.normpath(os.path.abspath(outdir))
@@ -160,11 +162,13 @@
             convert_cmd = ("""/usr/bin/vdiskadm import -t %s "%s" "%s" """ %
                 (qemu_formats[out_format], absin, absout)) 
         elif out_format == DISK_FORMAT_RAW:
-            convert_cmd = ("""qemu-img convert "%s" -O %s "%s" """ %
-                (absin, qemu_formats[out_format], absout))
+            convert_cmd = ("""qemu-img convert -O %s "%s" "%s" """ %
+                (qemu_formats[out_format], absin, absout))
         else:
             raise NotImplementedError("Cannot convert to disk format %s" %
                 output_format)
+
+        self.clean += [ absout ]
 
         ret = os.system(convert_cmd)
         if ret != 0:
diff --git a/virtconv/parsers/virtimage.py b/virtconv/parsers/virtimage.py
--- a/virtconv/parsers/virtimage.py
+++ b/virtconv/parsers/virtimage.py
@@ -37,7 +37,7 @@
    <os>
     <loader>pygrub</loader>
    </os>
-   %(pv_disks)s
+   %(disks)s
   </boot>
 """
 
@@ -49,7 +49,7 @@
    <os>
     <loader dev="hd"/>
    </os>
-   %(hvm_disks)s
+   %(disks)s
   </boot>
 """
 
@@ -74,6 +74,74 @@
  </storage>
 </image>
 """
+
+def export_disks(vm):
+    """
+    Export code for the disks.  Slightly tricky for two reasons.
+    
+    We can't handle duplicate disks: some vmx files define SCSI/IDE devices
+    that point to the same storage, and Xen isn't happy about that. We
+    just ignore any entries that have duplicate paths.
+
+    Since there is no SCSI support in rombios, and the SCSI emulation is
+    troublesome with Solaris, we forcibly switch the disks to IDE, and expect
+    the guest OS to cope (which at least Linux does admirably).
+
+    Note that we even go beyond hdd: above that work if the domU has PV
+    drivers.
+    """
+
+    paths = []
+
+    disks = {}
+
+    for (bus, instance), disk in sorted(vm.disks.iteritems()):
+
+        if disk.path and disk.path in paths:
+            continue
+
+        if bus == "scsi":
+            instance = 0
+            while disks.get(("ide", instance)):
+                instance += 1
+
+        disks[("ide", instance)] = disk
+
+        if disk.path:
+            paths += [ disk.path ]
+
+    diskout = []
+    storage = []
+
+    for (bus, instance), disk in sorted(disks.iteritems()):
+
+        # virt-image XML cannot handle an empty CD device
+        if not disk.path:
+            continue
+
+        path = disk.path
+        drive_nr = ascii_letters[int(instance) % 26]
+
+        disk_prefix = "xvd"
+        if vm.type == vmcfg.VM_TYPE_HVM:
+            if bus == "ide":
+                disk_prefix = "hd"
+            else:
+                disk_prefix = "sd"
+
+        # FIXME: needs updating for later Xen enhancements; need to
+        # implement capabilities checking for max disks etc.
+        diskout.append("""<drive disk="%s" target="%s%s" />\n""" %
+            (path, disk_prefix, drive_nr))
+
+        type = "raw"
+        if disk.type == diskcfg.DISK_TYPE_ISO:
+            type = "iso"
+        storage.append(
+            """<disk file="%s" use="system" format="%s"/>\n""" %
+                (path, type))
+
+    return storage, diskout
 
 class virtimage_parser(formats.parser):
     """
@@ -117,33 +185,15 @@
         # xend wants the name to match r'^[A-Za-z0-9_\-\.\:\/\+]+$'
         vmname = re.sub(r'[^A-Za-z0-9_.:/+-]+',  '_', vm.name)
 
-        pv_disks = []
-        hvm_disks = []
-        storage_disks = []
-
-        # create disk filename lists for xml template
-        for disk in vm.disks:
-            number = disk.number
-            path = disk.path
-
-            # 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]))
-            hvm_disks.append("""<drive disk="%s" target="hd%s" />\n""" %
-                (path, ascii_letters[number % 26]))
-            storage_disks.append(
-                """<disk file="%s" use="system" format="%s"/>\n"""
-                    % (path, diskcfg.qemu_formats[disk.format]))
-
         if vm.type == vmcfg.VM_TYPE_PV:
             boot_template = pv_boot_template
         else:
             boot_template = hvm_boot_template
 
+        (storage, disks) = export_disks(vm)
+
         boot_xml = boot_template % {
-            "pv_disks" : "".join(pv_disks),
-            "hvm_disks" : "".join(hvm_disks),
+            "disks" : "".join(disks),
             "arch" : vm.arch,
         }
 
@@ -154,7 +204,7 @@
             "nr_vcpus" : vm.nr_vcpus,
             # Mb to Kb
             "memory" : int(vm.memory) * 1024,
-            "storage" : "".join(storage_disks),
+            "storage" : "".join(storage),
         }
 
         outfile = open(output_file, "w")
diff --git a/virtconv/parsers/vmx.py b/virtconv/parsers/vmx.py
--- a/virtconv/parsers/vmx.py
+++ b/virtconv/parsers/vmx.py
@@ -23,6 +23,47 @@
 import virtconv.diskcfg as diskcfg
 
 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
+
+    _, bus, bus_nr, inst, key = re.split(r"^(scsi|ide)([0-9]+):([0-9]+)\.",
+        fullkey)
+
+    lvalue = value.lower()
+
+    if key == "present" and lvalue == "false":
+        return
+
+    # Does anyone else think it's scary that we're still doing things
+    # like this?
+    if bus == "ide":
+        inst = int(inst) + int(bus_nr) * 2
+
+    devid = (bus, inst)
+    if not vm.disks.get(devid):
+        vm.disks[devid] = diskcfg.disk(bus = bus,
+            type = diskcfg.DISK_TYPE_DISK)
+
+    if key == "deviceType":
+        if lvalue == "atapi-cdrom" or lvalue == "cdrom-raw":
+            vm.disks[devid].type = diskcfg.DISK_TYPE_CDROM
+        elif lvalue == "cdrom-image":
+            vm.disks[devid].type = diskcfg.DISK_TYPE_ISO
+
+    if key == "fileName":
+        vm.disks[devid].path = value
+        vm.disks[devid].format = diskcfg.DISK_FORMAT_RAW
+        if lvalue.endswith(".vmdk"):
+            vm.disks[devid].format = diskcfg.DISK_FORMAT_VMDK
 
 class vmx_parser(formats.parser):
     """
@@ -74,22 +115,29 @@
                 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)
             except:
                 raise Exception("Syntax error at line %d: %s" %
                     (line_nr + 1, line.strip()))
+
+        for devid, disk in vm.disks.iteritems():
+            if disk.type == diskcfg.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)
@@ -98,10 +146,7 @@
         vm.memory = config.get("memsize")
         vm.description = config.get("annotation")
         vm.nr_vcpus = config.get("numvcpus")
-
-        for (number, path) in enumerate(disks):
-            vm.disks += [ diskcfg.disk(path, number, diskcfg.DISK_FORMAT_VMDK) ]
-
+     
         vm.validate()
         return vm
 
diff --git a/virtconv/vmcfg.py b/virtconv/vmcfg.py
--- a/virtconv/vmcfg.py
+++ b/virtconv/vmcfg.py
@@ -17,6 +17,10 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 # MA 02110-1301 USA.
 #
+
+import platform
+from virtconv import diskcfg
+from virtinst import CapabilitiesParser
 
 VM_TYPE_UNKNOWN = 0
 VM_TYPE_PV = 1
@@ -47,7 +51,7 @@
         self.description = None
         self.memory = None
         self.nr_vcpus = None
-        self.disks = [ ]
+        self.disks = {}
         self.type = VM_TYPE_HVM
         self.arch = "i686"
 
@@ -67,3 +71,25 @@
             raise ValueError("VM type is not set")
         if not self.arch:
             raise ValueError("VM arch is not set")
+
+        for (bus, inst), disk in sorted(self.disks.iteritems()):
+            if disk.type == diskcfg.DISK_TYPE_DISK and not disk.path:
+                raise ValueError("Disk %s:%s storage does not exist"
+                    % (bus, inst))
+
+def host(conn=None):
+    """
+    Return the host, as seen in platform.system(), but possibly from a
+    hypervisor connection.  Note: use default_arch() in almost all
+    cases, unless you need to detect the OS.  In particular, this value
+    gives no indication of 32 vs 64 bitness.
+    """
+    if conn:
+        cap = CapabilitiesParser.parse(conn.getCapabilities())
+        if cap.host.arch == "i86pc":
+            return "SunOS"
+        else:
+            # or Linux-alike. Hmm.
+            return "Linux"
+
+    return platform.system()




More information about the et-mgmt-tools mailing list