# HG changeset patch # User john.levon@xxxxxxx # 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@xxxxxxx> 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() _______________________________________________ et-mgmt-tools mailing list et-mgmt-tools@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/et-mgmt-tools