virt-pack was creating a wrapper file for each disk rather than converting it. If we were to convert an image once and then try to reconvert that image to another format it will fail based on that wrapper file not being the actual disk. We can write an exception to handle this. It works perfectly in vmware to redirect it to the raw file with the appropriate properties but conflicts with qemu-img for now. It was more of a way to package multiple configurations using the same disk image file. Rediffed against current changeset 600 and conflicts resolved with the vmware to vmx changes reverted. Cole Robinson wrote: > Joey Boggs wrote: > >> This will replace the virt-pack command and supplemental Unware.py file >> and integrate them into virt-convert directly as a module. >> >> I'll create a patch to remove the deprecated files once tested by others. >> >> Other notes, only supports raw disks, adding in qcow support should be >> fairly easy, just wanted to get this out the door first. >> >> >> > > >> diff -r f87154697807 virt-convert >> --- a/virt-convert Thu Sep 18 16:44:30 2008 -0400 >> +++ b/virt-convert Mon Sep 22 12:17:55 2008 -0400 >> @@ -49,8 +49,7 @@ >> opts.add_option("-i", "--input-format", action="store", >> dest="input_format", help=("Input format, e.g. 'vmx'")) >> opts.add_option("-o", "--output-format", action="store", >> - dest="output_format", default="virt-image", >> - help=("Output format, e.g. 'virt-image'")) >> + dest="output_format", help=("Output format, e.g. 'virt-image / vmware'")) >> > > Since we are using 'vmx' as an input format type, we > should probably also use it as the name of the output > format (not 'vmware'). > > >> opts.add_option("-D", "--disk-format", action="store", >> dest="disk_format", help=("Output disk format")) >> opts.add_option("-v", "--hvm", action="store_true", dest="fullvirt", >> @@ -89,6 +88,10 @@ >> else: >> options.output_file = args[1] >> options.output_dir = os.path.dirname(os.path.realpath(args[1])) >> + >> + if not options.output_format: >> + opts.error("Output format must be specified") >> + sys.exit(1) >> >> if options.output_format not in formats.formats(): >> opts.error(("Unknown output format \"%s\"" % options.output_format)) >> @@ -170,7 +173,6 @@ >> logging.error("Couldn't import file \"%s\": %s" % >> (options.input_file, e)) >> sys.exit(1) >> - >> if options.paravirt: >> vmdef.type = vmcfg.VM_TYPE_PV >> else: >> @@ -214,11 +216,13 @@ >> try: >> for d in vmdef.disks.values(): >> format = options.disk_format >> - >> # VMDK disks on Solaris converted to vdisk by default >> if (d.format == diskcfg.DISK_FORMAT_VMDK and >> not format and vmcfg.host() == "SunOS"): >> format = "vdisk" >> + >> + if options.output_format == "vmware": >> + format = "vmdk" >> >> > > s/vmware/vmx/ > > >> if not format: >> format = "raw" >> diff -r f87154697807 virtconv/diskcfg.py >> --- a/virtconv/diskcfg.py Thu Sep 18 16:44:30 2008 -0400 >> +++ b/virtconv/diskcfg.py Mon Sep 22 12:17:55 2008 -0400 >> @@ -181,7 +181,7 @@ >> absout = os.path.join(outdir, relout) >> >> if not (out_format == DISK_FORMAT_VDISK or >> - out_format == DISK_FORMAT_RAW): >> + out_format == DISK_FORMAT_RAW or out_format == DISK_FORMAT_VMDK): >> raise NotImplementedError("Cannot convert to disk format %s" % >> output_format) >> >> diff -r f87154697807 virtconv/parsers/virtimage.py >> --- a/virtconv/parsers/virtimage.py Thu Sep 18 16:44:30 2008 -0400 >> +++ b/virtconv/parsers/virtimage.py Mon Sep 22 12:17:55 2008 -0400 >> @@ -21,10 +21,13 @@ >> import virtconv.formats as formats >> import virtconv.vmcfg as vmcfg >> import virtconv.diskcfg as diskcfg >> +import virtconv.netdevcfg as netdevcfg >> import virtinst.FullVirtGuest as fv >> - >> +import virtinst.ImageParser >> +from optparse import OptionParser, OptionValueError >> > > Doesn't look like optparse imports need to be here. > > >> from xml.sax.saxutils import escape >> from string import ascii_letters >> +import os >> import re >> >> pv_boot_template = """ >> @@ -183,16 +186,23 @@ >> """ >> name = "virt-image" >> suffix = ".virt-image.xml" >> - can_import = False >> + can_import = True >> can_export = True >> - can_identify = False >> + can_identify = True >> >> @staticmethod >> def identify_file(input_file): >> """ >> Return True if the given file is of this format. >> + Not able to guarantee <image> is on the top line rather than a blankline >> """ >> - raise NotImplementedError >> + infile = open(input_file, "r") >> + line = infile >> + for line in line.readlines(): >> + if re.match(r'^<image>', line): >> + return True >> + infile.close() >> + >> >> > > Hmm, might it be better just to do ImageParser.parse_file here, > and if it throws an exception, log it and return False? > > >> @staticmethod >> def import_file(input_file): >> @@ -200,7 +210,44 @@ >> Import a configuration file. Raises if the file couldn't be >> opened, or parsing otherwise failed. >> """ >> - raise NotImplementedError >> + vm = vmcfg.vm() >> + config = virtinst.ImageParser.parse_file(input_file) >> + domain = config.domain >> + boot = domain.boots[0] >> + >> + if not config.name: >> + raise ValueError("No Name defined in \"%s\"" % input_file) >> + vm.name = config.name >> + vm.memory = config.domain.memory >> + if config.descr: >> + vm.description = config.descr >> + vm.nr_vcpus = config.domain.vcpu >> + >> + bus="scsi" >> + diskinst = 0 >> + devid = (bus, diskinst) >> + >> + for d in boot.drives: >> + disk = d.disk >> + if disk.format == "raw": >> + disk.format = diskcfg.DISK_FORMAT_RAW >> + else: >> + raise ValueError("%s is not a supported disk format" % disk.format) >> + >> + vm.disks[devid] = diskcfg.disk(bus = bus, >> + type = diskcfg.DISK_TYPE_DISK) >> + vm.disks[devid].format = disk.format >> + vm.disks[devid].path = disk.file >> + diskinst = diskinst + 1 >> + >> + nics = domain.interface >> + nicinst = 0 >> + while nicinst < nics: >> + vm.netdevs[nicinst] = netdevcfg.netdev(type = netdevcfg.NETDEV_TYPE_UNKNOWN) >> + nicinst = nicinst + 1 >> + """ Eventually need to add support for mac addresses if given""" >> + vm.validate() >> + return vm >> >> > > Looks fine. > > >> @staticmethod >> def export_file(vm, output_file): >> diff -r f87154697807 virtconv/parsers/vmx.py >> --- a/virtconv/parsers/vmx.py Thu Sep 18 16:44:30 2008 -0400 >> +++ b/virtconv/parsers/vmx.py Mon Sep 22 12:17:55 2008 -0400 >> @@ -22,7 +22,8 @@ >> import virtconv.vmcfg as vmcfg >> import virtconv.diskcfg as diskcfg >> import virtconv.netdevcfg as netdevcfg >> - >> +import time >> +import sys >> import re >> import os >> >> @@ -97,10 +98,10 @@ >> resource being http://sanbarrow.com/vmx.html >> """ >> >> - name = "vmx" >> + name = "vmware" >> > > Drop this change, it would break passing 'vmx' as the input > format which is current behavior. > > >> suffix = ".vmx" >> can_import = True >> - can_export = False >> + can_export = True >> can_identify = True >> >> @staticmethod >> @@ -160,7 +161,7 @@ >> 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.lower() == "auto detect" or >> not os.path.exists(disk.path)): >> @@ -188,6 +189,142 @@ >> exception on failure to write the output file. >> """ >> >> - raise NotImplementedError >> + _VMX_MAIN_TEMPLATE = """#!/usr/bin/vmplayer >> + >> +# Generated %(now)s by %(progname)s >> +# http://virt-manager.et.redhat.com/ >> + >> +# This is a Workstation 5 or 5.5 config file >> +# It can be used with Player >> +config.version = "8" >> +virtualHW.version = "4" >> + >> +# Selected operating system for your virtual machine >> +guestOS = "other" >> + >> +# displayName is your own name for the virtual machine >> +displayName = "%(/image/name)s" >> + >> +# These fields are free text description fields >> +annotation = "%(/image/description)s" >> +guestinfo.vmware.product.long = "%(/image/label)s" >> +guestinfo.vmware.product.url = "http://virt-manager.et.redhat.com/" >> +guestinfo.vmware.product.class = "virtual machine" >> + >> +# Number of virtual CPUs. Your virtual machine will not >> +# work if this number is higher than the number of your physical CPUs >> +numvcpus = "%(/image/devices/vcpu)s" >> + >> +# Memory size and other memory settings >> +memsize = "%(/image/devices/memory)d" >> +MemAllowAutoScaleDown = "FALSE" >> +MemTrimRate = "-1" >> + >> +# Unique ID for the virtual machine will be created >> +uuid.action = "create" >> + >> +## For appliances where tools are installed already, this should really >> +## be false, but we don't have that ionfo in the metadata >> +# Remind to install VMware Tools >> +# This setting has no effect in VMware Player >> +tools.remindInstall = "TRUE" >> + >> +# Startup hints interfers with automatic startup of a virtual machine >> +# This setting has no effect in VMware Player >> +hints.hideAll = "TRUE" >> + >> +# Enable time synchronization between computer >> +# and virtual machine >> +tools.syncTime = "TRUE" >> + >> +# First serial port, physical COM1 is not available >> +serial0.present = "FALSE" >> + >> +# Optional second serial port, physical COM2 is not available >> +serial1.present = "FALSE" >> + >> +# First parallell port, physical LPT1 is not available >> +parallel0.present = "FALSE" >> + >> +# Logging >> +# This config activates logging, and keeps last log >> +logging = "TRUE" >> +log.fileName = "%(/image/name)s.log" >> +log.append = "TRUE" >> +log.keepOld = "3" >> + >> +# These settings decides interaction between your >> +# computer and the virtual machine >> +isolation.tools.hgfs.disable = "FALSE" >> +isolation.tools.dnd.disable = "FALSE" >> +isolation.tools.copy.enable = "TRUE" >> +isolation.tools.paste.enabled = "TRUE" >> + >> +# Settings for physical floppy drive >> +floppy0.present = "FALSE" >> +""" >> + >> + >> + >> + _VMX_ETHERNET_TEMPLATE = """ >> +ethernet%(dev)s.present = "TRUE" >> +ethernet%(dev)s.connectionType = "nat" >> +ethernet%(dev)s.addressType = "generated" >> +ethernet%(dev)s.generatedAddressOffset = "0" >> +ethernet%(dev)s.autoDetect = "TRUE" >> +""" >> + >> + _VMX_IDE_TEMPLATE = """ >> +# IDE disk >> +ide%(dev)s.present = "TRUE" >> +ide%(dev)s.fileName = "%(disk_filename)s" >> +ide%(dev)s.mode = "persistent" >> +ide%(dev)s.startConnected = "TRUE" >> +ide%(dev)s.writeThrough = "TRUE" >> +""" >> > > I'd prefer if these were moved out of the class definition. For > example, how they are listed in the virt-image parser file. > > >> + """ Cleanup spaces and multiple lines""" >> + vm.description = vm.description.strip() >> + vm.description = vm.description.replace("\n","|") >> + vmx_out_template = [] >> + vmx_dict = { >> + "now": time.strftime("%Y-%m-%dT%H:%M:%S %Z", time.localtime()), >> + "progname": os.path.basename(sys.argv[0]), >> + "/image/name": vm.name, >> + "/image/description": vm.description or "None", >> + "/image/label": vm.name, >> + "/image/devices/vcpu" : vm.nr_vcpus, >> + "/image/devices/memory": long(vm.memory)/1024 >> + } >> + vmx_out = _VMX_MAIN_TEMPLATE % vmx_dict >> + vmx_out_template.append(vmx_out) >> + >> + disk_out_template = [] >> + diskcount = 0 >> + for disk in vm.disks: >> + ide_count = 0 >> + dev = "%d:%d" % (ide_count / 2, ide_count % 2) >> + disk_dict = { >> + "dev": dev, >> + "disk_filename" : vm.disks[disk].path >> + } >> + disk_out = _VMX_IDE_TEMPLATE % disk_dict >> + disk_out_template.append(disk_out) >> + diskcount = diskcount + 1 >> + >> + eth_out_template = [] >> + if len(vm.netdevs): >> + for devnum in vm.netdevs: >> + eth_dict = { >> + "dev" : devnum >> + } >> + eth_out = _VMX_ETHERNET_TEMPLATE % eth_dict >> + eth_out_template.append(eth_out) >> + >> + outfile = open(output_file, "w") >> + outfile.writelines(vmx_out_template) >> + outfile.writelines(disk_out_template) >> + outfile.writelines(eth_out_template) >> + outfile.close() >> + >> >> > > Now, I'm pretty ignorant of how vmx files work, but > wasn't virt-pack writing separate descriptor files > for each disk? The above just seems to lump them > in the same file, is that fine? > > Also, can you rediff the patch against latest upstream? > I made some small changes to the virtconv stuff that > will collide with a couple sections of this patch, > nothing major though. > > Thanks, > Cole > > >
diff -r 58a909b4f71c virt-convert --- a/virt-convert Mon Sep 22 11:32:11 2008 -0400 +++ b/virt-convert Tue Sep 23 11:53:19 2008 -0400 @@ -48,10 +48,9 @@ opts.add_option("-d", "--debug", action="store_true", dest="debug", help=("Print debugging information")) opts.add_option("-i", "--input-format", action="store", - dest="input_format", help=("Input format, e.g. 'vmx'")) + dest="input_format", help=("Input format, e.g. 'vmx / virt-image'")) opts.add_option("-o", "--output-format", action="store", - dest="output_format", default="virt-image", - help=("Output format, e.g. 'virt-image'")) + dest="output_format", help=("Output format, e.g. 'virt-image / vmx'")) opts.add_option("-D", "--disk-format", action="store", dest="disk_format", help=("Output disk format")) opts.add_option("-v", "--hvm", action="store_true", dest="fullvirt", @@ -211,11 +210,13 @@ try: for d in vmdef.disks.values(): format = options.disk_format - # VMDK disks on Solaris converted to vdisk by default if (d.format == diskcfg.DISK_FORMAT_VMDK and not format and vmcfg.host() == "SunOS"): format = "vdisk" + + if options.output_format == "vmx": + format = "vmdk" if not format: format = "raw" diff -r 58a909b4f71c virtconv/diskcfg.py --- a/virtconv/diskcfg.py Mon Sep 22 11:32:11 2008 -0400 +++ b/virtconv/diskcfg.py Tue Sep 23 11:53:19 2008 -0400 @@ -181,7 +181,7 @@ absout = os.path.join(outdir, relout) if not (out_format == DISK_FORMAT_VDISK or - out_format == DISK_FORMAT_RAW): + out_format == DISK_FORMAT_RAW or out_format == DISK_FORMAT_VMDK): raise NotImplementedError("Cannot convert to disk format %s" % output_format) diff -r 58a909b4f71c virtconv/formats.py --- a/virtconv/formats.py Mon Sep 22 11:32:11 2008 -0400 +++ b/virtconv/formats.py Tue Sep 23 11:53:19 2008 -0400 @@ -44,6 +44,7 @@ Import a configuration file. Raises if the file couldn't be opened, or parsing otherwise failed. """ + print "importing file" raise NotImplementedError @staticmethod diff -r 58a909b4f71c virtconv/parsers/virtimage.py --- a/virtconv/parsers/virtimage.py Mon Sep 22 11:32:11 2008 -0400 +++ b/virtconv/parsers/virtimage.py Tue Sep 23 11:53:19 2008 -0400 @@ -21,10 +21,13 @@ import virtconv.formats as formats import virtconv.vmcfg as vmcfg import virtconv.diskcfg as diskcfg +import virtconv.netdevcfg as netdevcfg import virtinst.FullVirtGuest as fv - +import virtinst.ImageParser as ImageParser +from virtinst.cli import fail from xml.sax.saxutils import escape from string import ascii_letters +import os import re pv_boot_template = """ @@ -75,6 +78,10 @@ </storage> </image> """ + +def parse_disk_entry(vm, fullkey, value): + return + def export_os_params(vm): """ @@ -183,16 +190,23 @@ """ name = "virt-image" suffix = ".virt-image.xml" - can_import = False + can_import = True can_export = True - can_identify = False + can_identify = True @staticmethod def identify_file(input_file): """ Return True if the given file is of this format. + Not able to guarantee <image> is on the top line rather than a blankline """ - raise NotImplementedError + infile = open(input_file, "r") + line = infile + for line in line.readlines(): + if re.match(r'^<image>', line): + return True + infile.close() + @staticmethod def import_file(input_file): @@ -200,7 +214,48 @@ Import a configuration file. Raises if the file couldn't be opened, or parsing otherwise failed. """ - raise NotImplementedError + vm = vmcfg.vm() + try: + config = ImageParser.parse_file(input_file) + except Exception, e: + fail("Couldn't import file \"%s\": %s" % (input_file, e)) + + domain = config.domain + boot = domain.boots[0] + + if not config.name: + raise ValueError("No Name defined in \"%s\"" % input_file) + vm.name = config.name + vm.memory = config.domain.memory + if config.descr: + vm.description = config.descr + vm.nr_vcpus = config.domain.vcpu + + bus="scsi" + diskinst = 0 + devid = (bus, diskinst) + + for d in boot.drives: + disk = d.disk + if disk.format == "raw": + disk.format = diskcfg.DISK_FORMAT_RAW + else: + raise ValueError("%s is not a supported disk format" % disk.format) + + vm.disks[devid] = diskcfg.disk(bus = bus, + type = diskcfg.DISK_TYPE_DISK) + vm.disks[devid].format = disk.format + vm.disks[devid].path = disk.file + diskinst = diskinst + 1 + + nics = domain.interface + nicinst = 0 + while nicinst < nics: + vm.netdevs[nicinst] = netdevcfg.netdev(type = netdevcfg.NETDEV_TYPE_UNKNOWN) + nicinst = nicinst + 1 + """ Eventually need to add support for mac addresses if given""" + vm.validate() + return vm @staticmethod def export_file(vm, output_file): diff -r 58a909b4f71c virtconv/parsers/vmx.py --- a/virtconv/parsers/vmx.py Mon Sep 22 11:32:11 2008 -0400 +++ b/virtconv/parsers/vmx.py Tue Sep 23 11:53:19 2008 -0400 @@ -22,9 +22,105 @@ import virtconv.vmcfg as vmcfg import virtconv.diskcfg as diskcfg import virtconv.netdevcfg as netdevcfg - +import time +import sys import re import os + +_VMX_MAIN_TEMPLATE = """ +#!/usr/bin/vmplayer + +# Generated %(now)s by %(progname)s +# http://virt-manager.et.redhat.com/ + +# This is a Workstation 5 or 5.5 config file +# It can be used with Player +config.version = "8" +virtualHW.version = "4" + +# Selected operating system for your virtual machine +guestOS = "other" + +# displayName is your own name for the virtual machine +displayName = "%(/image/name)s" + +# These fields are free text description fields +annotation = "%(/image/description)s" +guestinfo.vmware.product.long = "%(/image/label)s" +guestinfo.vmware.product.url = "http://virt-manager.et.redhat.com/" +guestinfo.vmware.product.class = "virtual machine" + +# Number of virtual CPUs. Your virtual machine will not +# work if this number is higher than the number of your physical CPUs +numvcpus = "%(/image/devices/vcpu)s" + +# Memory size and other memory settings +memsize = "%(/image/devices/memory)d" +MemAllowAutoScaleDown = "FALSE" +MemTrimRate = "-1" + +# Unique ID for the virtual machine will be created +uuid.action = "create" + +## For appliances where tools are installed already, this should really +## be false, but we don't have that ionfo in the metadata +# Remind to install VMware Tools +# This setting has no effect in VMware Player +tools.remindInstall = "TRUE" + +# Startup hints interfers with automatic startup of a virtual machine +# This setting has no effect in VMware Player +hints.hideAll = "TRUE" + +# Enable time synchronization between computer +# and virtual machine +tools.syncTime = "TRUE" + +# First serial port, physical COM1 is not available +serial0.present = "FALSE" + +# Optional second serial port, physical COM2 is not available +serial1.present = "FALSE" + +# First parallell port, physical LPT1 is not available +parallel0.present = "FALSE" + +# Logging +# This config activates logging, and keeps last log +logging = "TRUE" +log.fileName = "%(/image/name)s.log" +log.append = "TRUE" +log.keepOld = "3" + +# These settings decides interaction between your +# computer and the virtual machine +isolation.tools.hgfs.disable = "FALSE" +isolation.tools.dnd.disable = "FALSE" +isolation.tools.copy.enable = "TRUE" +isolation.tools.paste.enabled = "TRUE" + +# Settings for physical floppy drive +floppy0.present = "FALSE" +""" + + + +_VMX_ETHERNET_TEMPLATE = """ +ethernet%(dev)s.present = "TRUE" +ethernet%(dev)s.connectionType = "nat" +ethernet%(dev)s.addressType = "generated" +ethernet%(dev)s.generatedAddressOffset = "0" +ethernet%(dev)s.autoDetect = "TRUE" +""" + +_VMX_IDE_TEMPLATE = """ +# IDE disk +ide%(dev)s.present = "TRUE" +ide%(dev)s.fileName = "%(disk_filename)s" +ide%(dev)s.mode = "persistent" +ide%(dev)s.startConnected = "TRUE" +ide%(dev)s.writeThrough = "TRUE" +""" def parse_netdev_entry(vm, fullkey, value): """ @@ -70,14 +166,13 @@ # 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 @@ -100,7 +195,7 @@ name = "vmx" suffix = ".vmx" can_import = True - can_export = False + can_export = True can_identify = True @staticmethod @@ -160,7 +255,7 @@ 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 is None or disk.path.lower() == "auto detect" or @@ -180,6 +275,8 @@ @staticmethod def export_file(vm, output_file): + + """ Export a configuration file. @vm vm configuration instance @@ -189,6 +286,56 @@ exception on failure to write the output file. """ - raise NotImplementedError + """ Cleanup spaces and multiple lines""" + vm.description = vm.description.strip() + vm.description = vm.description.replace("\n","|") + vmx_out_template = [] + vmx_dict = { + "now": time.strftime("%Y-%m-%dT%H:%M:%S %Z", time.localtime()), + "progname": os.path.basename(sys.argv[0]), + "/image/name": vm.name, + "/image/description": vm.description or "None", + "/image/label": vm.name, + "/image/devices/vcpu" : vm.nr_vcpus, + "/image/devices/memory": long(vm.memory)/1024 + } + vmx_out = _VMX_MAIN_TEMPLATE % vmx_dict + vmx_out_template.append(vmx_out) + + disk_out_template = [] + diskcount = 0 + for disk in vm.disks: + ide_count = 0 + dev = "%d:%d" % (ide_count / 2, ide_count % 2) + disk_dict = { + "dev": dev, + "disk_filename" : vm.disks[disk].path + } + disk_out = _VMX_IDE_TEMPLATE % disk_dict + disk_out_template.append(disk_out) + diskcount = diskcount + 1 + + eth_out_template = [] + if len(vm.netdevs): + for devnum in vm.netdevs: + eth_dict = { + "dev" : devnum + } + eth_out = _VMX_ETHERNET_TEMPLATE % eth_dict + eth_out_template.append(eth_out) + + + + + + +### write out generated templates, will sequence these better so we can write inline ### + outfile = open(output_file, "w") + outfile.writelines(vmx_out_template) + outfile.writelines(disk_out_template) + outfile.writelines(eth_out_template) + outfile.close() + + #raise NotImplementedError formats.register_parser(vmx_parser)
_______________________________________________ et-mgmt-tools mailing list et-mgmt-tools@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/et-mgmt-tools