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