[virt-tools-list] [RFC] virt-disk : a command line tool for modify domain XML's disk of inactive domains.
Cole Robinson
crobinso at redhat.com
Mon Feb 21 15:08:46 UTC 2011
On 02/21/2011 03:23 AM, KAMEZAWA Hiroyuki wrote:
> Hi, now, with qemu, virsh attach-disk doesn't work with inactive disks and
> we need to edit XML with virsh edit.
> IIUC, libvirt and virsh is designed as it is.
>
> But I want to modify domain XML via commandline tools
> - for middleware, which modify domains by scripting.
> - for concsoles, where curses can't work correctly.
> - for me, I can't remember XML definition detaisl ;)
>
> So, I write one.
>
> Following script is a script for modify domain XML and allows
> - add disks
> - delete disks
> - show list of disks
>
> I think most of elements defined in http://libvirt.org/formatdomain.html#elementsDisks
> is supported. But I'm an only qemu user and didn't test with Xen and other VMs.
>
> What I wanted to hear opinions as 'RFC' is
> - Can this be shipped with libvirt as one of a tool ? (with more documents)
> (If so, we'll write other scripts for cpu,network,memory,etc...)
>
> - If not, what is the best way other than making this as my house script ?
> I'm grad if this is shipped with libvirt is because catching new definition
> of XML is easy.
>
> - Doesn't this one work with your environment ?
>
>
Thanks for taking a stab at this, I've been meaning to start a similar tool
for some time. However, you should be able to leverage virtinst to accomplish
nearly all the XML parsing, and reuse existing virt-install command line
options and documentation. Additionally the tool could build or edit any
arbitrary domain XML and wouldn't be specific to disks.
Take a look at tests/xmlparse.py in the virtinst repo to see how the parsing
works and what it's capable of.
Thanks,
Cole
>
> Example)
> [root at bluextal pydom]# ./virt-disk.py --domain Guest02 --virtio --source /dev/iscsi_lvm/disk02
> Name : vdb
> Device Type : block
> Media Type : disk
> Bus Type : virtio
> Driver Name : qemu
> Driver Type : raw
> Driver Cache : default
> ReadWrite : ReadWrite
> Source : /dev/iscsi_lvm/disk02
> Address: : AutoFill
> Add this device Y/N ? y
>
> [root at bluextal pydom]# virsh dumpxml Guest02
> <domain type='kvm'>
> .....
> <disk type='block' device='disk'>
> <driver name='qemu' type='raw'/>
> <source dev='/dev/iscsi_lvm/disk02'/>
> <target dev='vdb' bus='virtio'/>
> <address type='pci' domain='0x0000' bus='0x00' slot='0x08' function='0x0'/>
> </disk>
> ==
>
> Thanks,
> -Kame
>
> ==
> #!/usr/bin/python
> #
> #
> # 'virt-disk' is a command for maintaining disk entities in XML description
> # of libvirt's VM definition. Default vm assumes 'qemu'
> #
> # Copyright (C) 2011 Fujitsu LTD.
> #
> # KAMEZAWA Hiroyuki <kamezawa.hiroyu at jp.fujitsu.com>
> #
> # Usage:
> #
> # Brief help is
> # %virt-disk --help
> #
> # When you want to see list of disks of domain
> # %virt-disk --domain <domain name> --list
> #
> # When you want to remove 'vdb'
> # %virt-disk --domain <domain name> --delete vdb
> #
> # When you want to add block device as virtio block device.
> # %virt-disk --domain <domain name> --virtio --source /dev/to/mydisk
> #
> # When you want to add an IDE cdrom
> # %virt-disk --domain <domain name> --ide --media cdrom
> # --source /path/to/ISO.img
> #
> # When you want to add a network image as virtio disk.
> # %virt-disk --domain <domain name> --type network --protocol sheepdog
> # --host address:port --source resource_name --virtio
> #
> # This commad does
> #
> # - parse options.
> # - connect libvirt and get domain xml
> # - add all devices to disks[] List.
> # - add all pci address to PCIS[] List
> # - create new BlockDevice object with regard to options.
> # - create a XML entry of device
> # - insert and save it.
> #
>
> import xml.dom
> import xml.dom.minidom
> import os
> import stat
> import sys
> import libvirt
> import re,string
> from xml.dom import Node
> from optparse import OptionParser,OptionGroup
>
> PCIS = []
> disks = []
>
> class Address :
> pci = []
> drive = []
> def __init__(self, type) :
> self.type = type
> self.domain = None
> self.bus = None
> self.slot = None
> self.function = None
> self.controller = None
> self.function = None
> if type == 'pci' :
> PCIS.append(self)
>
> class BlockDevice :
> def __init__(self) :
> self.type = None
> self.media = None
> self.driver_cache = None
> self.driver_name = None
> self.driver_type = None
> self.driver_error_policy = None
> self.driver_io = None
> self.source = None
> self.protocol = None
> self.protocol_addr = None
> self.protocol_port = None
> self.target_dev = None
> self.target_bus = None
> self.readonly = False
> self.address_type = None
> self.address_bus = ''
> self.address_domain = ''
> self.address_function = ''
> self.address_slot = ''
> self.address_controller= ''
> self.address_unit=''
> self.boot_order = None
> self.shareable = False
> self.serial = None
> self.encrypt_format = None
> self.encrypt_type = None
> self.encrypt_uuid = None
>
> def get_attr(self, name, attr) :
> x = name.getAttribute(attr)
> if x == '' :
> return None
> return x
>
> def find_element(self, ent, name) :
> for member in ent.childNodes :
> if member.nodeName == name :
> return member
> return None
>
> def parse_info(self, name) :
> # parsing <disk .....
> self.type = self.get_attr(name, 'type')
> self.media = self.get_attr(name, 'device')
> # parsing <driver ....
> driver = self.find_element(name, 'driver')
> if driver != None :
> self.driver_name = self.get_attr(driver, 'name')
> if self.driver_name == 'qemu' :
> self.driver_type = self.get_attr(driver, 'type')
> self.driver_cache = self.get_attr(driver, 'cache')
> self.driver_error_policy =\
> self.get_attr(driver, 'error_policy')
> self.driver_io = self.get_attr(driver, 'io')
> # parsing <source
> source = self.find_element(name, 'source')
> if source != None :
> self.source = self.get_attr(source, 'dev')
> if self.source == None :
> self.source = self.get_attr(source, 'file')
> if self.source == None :
> self.protocol = self.get_attr(source, 'protocol')
> self.source = self.get_attr(source, 'name')
> else :
> self.source = None
> #
> # check protocol and host, port
> #
> if self.protocol != None :
> source = self.find_element(name, 'source')
> host = self.find_element(source, 'host')
> if host != None :
> self.protocol_host = self.get_attr(host, 'host')
> self.protocol_port = self.get_attr(host, 'port')
> # parsing <target
> target = self.find_element(name, 'target')
> if target != None :
> self.target_dev = self.get_attr(target, 'dev')
> self.target_bus = self.get_attr(target, 'bus')
> # check readonly
> if self.find_element(name, 'readonly') != None :
> self.readonly = True
> # check shareable
> if self.find_element(name, 'shareable') != None:
> self.shareable = True
> # check boot order
> boot = self.find_element(name, 'boot')
> if boot != None :
> self.boot_order = self.get_attr(boot, 'order')
> # check shareable
> if self.find_element(name, 'shareable') != None :
> self.shareable = True
> # check address
> address = self.find_element(name, 'address')
> if address != None :
> self.address_type = self.get_attr(address, 'type')
> if self.address_type == 'pci' :
> self.address_bus = self.get_attr(address, 'bus')
> self.address_slot = self.get_attr(address, 'slot')
> self.address_function = self.get_attr(address, 'function')
> self.address_domain = self.get_attr(address, 'domain')
> elif self.address_type == 'drive' :
> self.address_bus = self.get_attr(address, 'bus')
> self.address_controller = self.get_attr(address, 'controller')
> self.address_unit = self.get_attr(address, 'unit')
> # check serial ID
> serial = self.find_element(name, 'serial')
> if serial != None :
> for member in serial.childNodes :
> if member.nodeType == Node.TEXT_NODE :
> self.serial = member.data
> # check encryption
> encrypt = self.find_element(name, 'encryption')
> if encrypt != None :
> self.encrypt_format = self.get_attr(encrypt, 'format')
> sec = self.find_element(encrypt, 'secret')
> if sec != None :
> self.encrypt_type = self.get_attr(sec, 'type')
> self.encrypt_uuid = self.get_attr(sec, 'uuid')
>
> return True
>
> def show_ent(self, name, value) :
> if value != None :
> print '%-16s:\t%s' %(name, value)
>
> def show(self) :
> self.show_ent('Name', self.target_dev)
> self.show_ent('Device Type', self.type)
> self.show_ent('Media Type', self.media)
> self.show_ent('Bus Type', self.target_bus)
> self.show_ent('Driver Name', self.driver_name)
> self.show_ent('Driver Type', self.driver_type)
> self.show_ent('Driver Cache', self.driver_cache)
> self.show_ent('Driver I/O', self.driver_io)
> self.show_ent('Error Policy', self.driver_error_policy)
> if self.readonly :
> self.show_ent('ReadWrite', 'ReadOnly')
> else :
> self.show_ent('ReadWrite', 'ReadWrite')
> if self.shareable :
> self.show_ent('Shareable', 'Yes')
> self.show_ent('Device Boot Order',self.boot_order)
> self.show_ent('Protocol', self.protocol)
> self.show_ent('Hostname', self.protocol_addr)
> self.show_ent('Port', self.protocol_port)
> self.show_ent('Source', self.source)
>
> if self.address_type == None :
> self.show_ent('Address:', 'AutoFill')
> elif self.address_type == 'pci' :
> x = '%s/%s/%s' %\
> (self.address_bus, self.address_slot, self.address_function)
> self.show_ent('PCI Address', x)
> elif self.address_type == 'drive' :
> x = '%s/%s/%s'\
> %(self.address_controller, self.address_bus, self.address_unit)
> self.show_ent('Drive Index', x)
>
> if self.serial != None :
> self.show_ent('Serial', self.serial)
>
> if self.encrypt_format != None :
> self.show_ent('Encryption', self.encrypt_format)
> self.show_ent(' Type', self.encrypt_type)
> self.show_ent(' UUID', self.encrypt_uuid)
>
> def is_block_device(node) :
> if node.nodeName == 'disk' :
> return True
> return False
>
> def parse_address(addr) :
> type = addr.getAttribute('type')
> ent = Address(type)
> if ent.type == 'pci' :
> ent.bus = addr.getAttribute('bus')
> ent.slot = addr.getAttribute('slot')
> ent.function = addr.getAttribute('function')
> ent.domain = addr.getAttribute('domain')
> elif ent.type == 'drive' :
> ent.controller = addr.getAttribute('controller')
> ent.bus = addr.getAttribute('bus')
> ent.unit = addr.getAttribute('unit')
> # Nothing happens
> return True
>
> def check_pci_conflict(bus, slot, function) :
> sloti = int(slot, 16)
> for addr in PCIS :
> if int(addr.slot, 16) == sloti :
> return True
> return False
>
> def get_free_pci_slot(devname) :
> for i in range(3, 31) : #qemu allows only 32 slots
> x = str(i)
> if not check_pci_conflict('0x00', x, '0x00') :
> slot = '0x%02x'%(i)
> return ['0x00', slot, '0x00']
> return []
>
> def check_get_free_scsi_slot(index) :
> addr = scsi_index_to_addr(index)
> conflict = False
> for disk in disks :
> if disk.target_device != scsi:
> continue
> if disk.target_controller == addr[0] and\
> disk.target_bus == addr[1] and\
> disk.target_units == addr[2] :
> conflict = True
> break
> if conflict == False:
> return addr
> return []
>
> def check_drive_conflict(type, controller, bus, unit) :
> for addr in drives :
> conflict = False
> for disk in disks :
> if disk.target_bus != 'type' :
> continue
> if disk.address_controller == controller and\
> disk.address_bus == bus and\
> disk.address_unit == unit :
> conflict = True
> break
> return conflict
> return True
>
>
> def check_device_conflict(devices, name) :
> for dev in devices :
> if dev.target_dev == name :
> return True
> return False
>
> def gen_scsi_name_from_index(index) :
> name = 'sd'
> if index < 26 :
> name = name + chr(ord('a') + index)
> elif index < (26 + (26 * 26)) :
> x = index / 26 - 1
> y = index % 26
> name = name + chr(ord('a') + x)
> name = name + chr(ord('a') + y)
> else :
> x = (index / 26 - 1) / 26 - 1
> y = (index / 26 - 1) % 26
> x = index % 26
> name = name + chr(ord('a') + x)
> name = name + chr(ord('a') + y)
> name = name + chr(ord('a') + z)
> return name
>
> def gen_device_name(devices, head) :
> if head == 'hd' :
> for x in 'abcd' :
> name = 'hd' + x
> if not check_device_conflict(devices, name) :
> return name
> if head == 'vd' :
> for x in range(0, 26) :
> name = head + chr(ord('a') + x)
> if not check_device_conflict(devices, name) :
> return name
> if head == 'sd' :
> for index in range(0, 18278) :
> name = gen_scsi_name_from_index(index)
> if not check_device_conflict(devices, name) :
> return name
> return None
>
> #
> # Commandline Parser
> # Using optparse package. Deafault value is below.
> #
> usage='usage:%prog --domain Domain <options>....'
> parser = OptionParser(usage)
>
> def help_choice(help, list, default) :
> help = help + " "
> help = help + ",".join(list)
> if (default == True) :
> help = help + " default: %default"
> return help
>
> parser.add_option('-c', '--connect', action='store',
> type='string', dest='connect_uri', help='libvirtd connect URI')
>
> parser.add_option('-d', '--domain', action='store',
> type='string',dest="domain",
> help='domain name.')
>
> parser.add_option('-l', '--list', action='store_true', dest='show_list',
> help='see device list')
>
> parser.add_option('--delete', action='store', dest='delete', type='string'
> , help='remove device')
>
> #
> # 'block' or 'file' can be detected by --source option.
> #
> typeChoices=('block','file','network','dir')
> parser.add_option('--type', choices=typeChoices, dest='type', type='choice',
> help=help_choice('device type:', typeChoices, False))
>
> mediaChoices=('disk','cdrom')
> parser.add_option('--media', choices=mediaChoices,
> type='choice',dest="media",
> help=help_choice('media type:', mediaChoices, True))
>
> parser.add_option('--source', action='store', type='string', dest='source',
> help='select source file/device to be shown as disk')
>
> protocolChoices=('nbd','rbd', 'sheepdog')
> parser.add_option('--protocol', choices=protocolChoices,
> type='choice', dest='protocol',
> help=help_choice('netdisk protocol:', protocolChoices, False))
>
> parser.add_option('--host', action='store', type='string', dest='host',
> help='<host:port> for rbd and sheepdog, network devices')
>
> parser.add_option('--devname', action='store', type='string', dest='devname',
> help='device name to be shown to guest(auto at default)')
>
> qemu_group = OptionGroup(parser, "Qemu Options")
> qemuFormatChoices=('raw','bochs','qcow2','qed')
> qemu_group.add_option('--qemu_disk_format', choices=qemuFormatChoices,
> type='choice', dest='qemu_format',
> help=help_choice('disk format(qemu):',qemuFormatChoices, True))
>
> ioChoices=('threads','native')
> qemu_group.add_option('--io',choices=ioChoices, type='choice', dest='io',
> help=help_choice('io option(qemu):', ioChoices, False))
>
> cacheChoices=('none','writeback','writethrough','default')
> qemu_group.add_option('--cache', choices=cacheChoices, type='choice',
> dest='cache', help=help_choice('cache policy:(qemu)', cacheChoices, True))
>
> errorChoices=('stop','ignore','enospace')
> parser.add_option('--error_policy', choices=errorChoices, type='choice',
> dest='error_policy',
> help=help_choice('error policy:', errorChoices, False))
>
> xen_group = OptionGroup(parser, "Xen Options")
> xenDriverChoices=('tap','tap2','phy','file')
> xen_group.add_option('--driver', choices=xenDriverChoices,
> type='choice', dest='driver_name',
> help=help_choice('Xen driver type:', xenDriverChoices, False))
>
> xen_group.add_option('--aio', action='store_true',
> dest='xen_aio', help='using Xen aio driver')
>
> parser.add_option('--virtio', action='store_true',
> dest='virtio', help='create a virtio device')
>
> parser.add_option('--readonly', action='store_true',
> dest='readonly', help='attach device as read only')
>
> parser.add_option('--scsi', action='store_true',
> dest='scsi', help='create a scsi device')
>
> parser.add_option('--ide', action='store_true',
> dest='ide', help='create a ide device')
>
> parser.add_option('--pci', action='store', dest='pci', type='string',
> help='customize pci resource by hand as bus:slot:function')
>
> parser.add_option('--drive_id', action='store', dest='drive_id', type='string',
> help='customize ide/scsi resource ID as controller:bus:unit')
>
> parser.add_option('--yes', action='store_true', dest='yes',
> help='don\'t ask before save')
>
> parser.add_option('--nosave', action='store_true', dest='nosave',
> help='show created device XML instead of saving.')
>
> parser.add_option('--boot_order', action='store', dest='boot_order',
> type='int', help='boot order of the device', metavar='Num')
>
> parser.add_option('--shareable', action='store_true', dest='shareable',
> help='the device can be shareable between device')
>
> parser.add_option('--serial', action='store', dest='serial',
> type='string', help='optional serial ID of the device')
>
> EncChoices = ['qcow']
> qemu_group.add_option('--encryption_format', choices=EncChoices,
> type='choice', dest='encryption_format',
> metavar='qcow', help='Only \"qcow\" is supported')
>
> EncTypeChoices =['passphrase']
> qemu_group.add_option('--encryption_type', choices=EncTypeChoices,
> type='choice', dest='encryption_type',
> help=help_choice('encription_type', EncTypeChoices, True))
>
> qemu_group.add_option('--encryption_uuid', action='store',
> metavar='UUID', dest='encryption_uuid',
> help='UUID for encrypted deivce')
>
>
> parser.add_option_group(qemu_group)
> parser.add_option_group(xen_group)
> parser.set_defaults(list=False, media='disk')
> parser.set_defaults(qemu_format='raw', cache='default', media='disk')
> parser.set_defaults(virtio=False, readonly=False, scsi=False, ide=False)
> parser.set_defaults(xen_aio=False,)
> parser.set_defaults(yes=False)
>
>
> (options, args) = parser.parse_args(sys.argv)
>
> #
> # Confirm domain XML is not updated since we opened it.
> #
> def check_domain_unchanged(origin, domain):
> if origin != domain.XMLDesc(0) :
> print 'Domain file may be updated while we open'
> print 'please retry'
> exit(1)
> #
> # Connect libvirt and get domain information.
> #
> if options.domain == None :
> print 'please specify domain to be modified'
> exit(1)
>
> try :
> conn = libvirt.open(options.connect_uri)
> except:
> print 'Can\'t connect libvirt with URI %s'%(options.connect_uri)
> exit(1)
>
> conn_uri = conn.getURI()
> info = re.split(':/', conn_uri)
>
> # From the name of connection URI, we detect vmname.
> vmname = info[0]
>
> # domain look up.
> domain = conn.lookupByName(options.domain)
> if domain == None:
> print 'Can\'t find domain %s' %(options.domain)
> exit(1)
>
>
> # read XML description.
> origin = domain.XMLDesc(0)
> dom = xml.dom.minidom.parseString(domain.XMLDesc(0))
>
> # At 1st, we need to find <device> block.
> names = dom.getElementsByTagName('devices')
> if (names == None) :
> print '<device> element not found in %s\n' % filename
> exit(1)
>
> for devices in names :
> if devices.hasChildNodes() :
> for name in devices.childNodes :
> if (is_block_device(name)) :
> disk = BlockDevice()
> disk.parse_info(name)
> disks.append(disk)
> if name.hasChildNodes() :
> for elem in name.childNodes :
> if elem.nodeName == 'address' :
> addr = parse_address(elem)
>
> #
> # Show List.
> #
> if options.show_list == True :
> for disk in disks :
> disk.show()
> print ''
> exit(0)
>
> # delete is easy.
> if options.delete != None :
> disk = None
> for name in devices.childNodes :
> if (not is_block_device(name)) :
> continue
> disk = BlockDevice()
> disk.parse_info(name)
> if disk.target_dev == options.delete :
> devices.removeChild(name)
> break
> disk = None
> if disk == None :
> print 'no such device %s'%(options.delete)
> exit(1)
> if not options.yes :
> print 'You\'ll delete this device!'
> disk.show()
> x = raw_input('Really delete Y/N ? ')
> if x != 'Y' and x != 'y' :
> exit(0)
> str = dom.toxml()
> check_domain_unchanged(origin, domain)
> conn.defineXML(str)
> exit(1)
>
> #
> # Build a newdisk information from command line options
> # and default values.
> #
> newdisk = BlockDevice()
> if options.type != None :
> newdisk.type = options.type
>
> newdisk.media = options.media
>
> #
> # qemu only has a drive name as 'qemu'. 'type' and 'cache' are selectable.
> #
> if vmname == 'qemu' :
> newdisk.driver_name = 'qemu'
> newdisk.driver_type = options.qemu_format
> if options.cache == None :
> newdisk.driver_cache = 'default'
> else :
> newdisk.driver_cache = options.cache
> else : # Xen can select drive name.
> newdisk.driver_name = options.driver_name
> if options.xen_aio == True :
> newdisk.driver_type = 'aio'
>
> if options.error_policy != None :
> newdisk.driver_error_policy = options.error_policy
>
> if options.io != None :
> newdisk.io = options.io
>
> # Make readonly if crdom is specified.
> if options.media == 'cdrom' or options.readonly == True:
> newdisk.readonly = True;
>
> # Check Device Name and detect device bus from device name.
> if options.devname != None :
> if re.match('vd[a-z]', options.devname) :
> newdisk.target_dev = options.devname
> newdisk.target_bus = 'virtio'
> elif re.match('hd[a-d]', options.devname) :
> newdisk.target_dev = options.devname
> newdisk.target_bus = 'ide'
> elif re.match('sd[a-z]', options.devname) :
> newdisk.target_dev = options.devname
> newdisk.target_bus = 'scsi'
> else :
> newdisk.target_dev = options.devname
> #
> # Define device name automatically with regard to the bus.
> #
> if options.virtio == True :
> newdisk.target_bus = 'virtio'
> if options.devname == None:
> newdisk.target_dev = gen_device_name(disks, 'vd')
> if newdisk.target_dev == None:
> print 'failed to define virtio drive name as vd*'
> print 'please define by hand with --devname'
> exit(1)
> elif options.ide == True :
> newdisk.target_bus = 'ide'
> if options.devname == None:
> newdisk.target_dev = gen_device_name(disks, 'hd')
> if newdisk.target_dev == None :
> print 'failed to define ide drive name as hd*'
> print 'please define by hand with --devname'
> exit(1)
>
> elif options.scsi == True :
> newdisk.target_bus = 'scsi'
> if options.devname == None:
> newdisk.target_dev = gen_device_name(disks, 'sd')
> if newdisk.target_dev == None :
> print 'failed to define scsi drive name as sd*'
> print 'please define by hand with --devname'
> exit(1)
> #
> # If we can't detelct target bus, retrun error.
> #
> if newdisk.target_bus == None :
> print 'need to specify device name or target bus for drive'
> exit(1)
>
> #
> # If there is a device with the same name, error.
> #
> if check_device_conflict(disks, newdisk.target_dev) :
> print 'device name conflicts %s' %(newdisk.target_dev)
> print 'please specify an other name with --devname'
>
> #
> # Handle 'source' type.
> #
> if options.source != None and options.protocol == None:
> # No network case. check local FS.
> # Only absolute path is allowed.
> if options.source[0] != '/' :
> print 'please use absolute path for source %s:' %(options.source)
> exit(1)
> try:
> mode = os.stat(options.source)[stat.ST_MODE]
> except:
> print 'can\'t handle file %s' %(options.source)
> exit(1)
> #
> # check 'file' and 'block'
> #
> newdisk.source = options.source
> if stat.S_ISREG(mode) != 0:
> if newdisk.type == None :
> newdisk.type = 'file'
> if newdisk.type != 'file' :
> print '%s specified in --source is file' %(options.source)
> exit(1)
> if stat.S_ISBLK(mode) != 0 :
> if newdisk.type == None :
> newdisk.type = 'block'
> if newdisk.type != 'block' :
> print '%s specified in --source is blkdev' %(options.source)
> exit(1)
> if newdisk.type == None :
> print 'can\'t detect source type %s'%(options.source)
>
> elif options.type == 'network' :
> if options.protocol == None :
> print 'need to select protocol for network drive.'
> exit(1)
> newdisk.protocol = options.protocol
> if options.source == None :
> print 'source name for network should be privded --soruce'
> exit(1)
> newdisk.source = options.source
> if not re.match('[0-9a-zA-z\._-]+:[0-9]+', options.host) :
> print 'host should be specified in address:port manner'
> exit(1)
> (addr, port) = re.split(':', options.host)
> newdisk.protocol_addr = addr
> newdisk.protocol_port = port
>
> if options.media != 'cdrom' and options.source == None :
> print 'you need to specify source if not cdrom'
> exit(1)
>
> #
> # Define PCI/IDE/SCSI drive address.
> # (Usually, all this will be defined automatically.)
> #
>
> # format check
> if options.pci != None :
> if options.drive_id != None :
> print 'pci address and drive-id cannot be set at the same time'
> exit(1)
> if not re.match("0x[0-9a-f]+:0x[0-9a-f]+:0x[0-9-a-f]", options.pci) :
> print 'bad pci address ',options.pci
> print '0xXX:0xXX:0xXX is expected',options.pci
> exit(1)
>
> if options.drive_id != None :
> if not re.match("[0-9]+:[0-9]+:[0-9]+", options.drive_id) :
> print 'bad drive_id address', options.drive_id
> exit(1)
>
> # define BUS ID.
> #
> # In this case, device name should meet bus ID(especially IDE),
> # which the user specified. The user should specify device
> # name by himself.
> #
> if options.pci != None or options.drive_id != None :
> if options.devname == None :
> print 'We recommend that --drive_id should be used with --devname',
> print 'device name <-> drive ID relationship is at random, now'
>
> if options.pci != None :
> pci = re.split(':', options.pci)
> if '0x00' != pci[0] :
> print 'only bus 0x00 can be used in pci'
> exit(1)
> if check_pci_conflict(pci[0], pci[1], pci[2]) :
> print 'bus %s conflicts' % (options.pci)
> newdisk.address_type = 'pci'
> newdisk.address_bus = pci[0]
> newdisk.address_slot = pci[1]
> newdisk.address_function = pci[2]
>
> elif options.drive_id != None :
> drive = re.split(':', options.drive_id)
>
> if options.ide == True :
> if check_drive_conflict('ide', drive[0],drive[1],drive[2]) :
> print 'drive %s conflicts' % (options.drive_id)
> else :
> if check_drive_conflict('scsi',drive[0], drive[1], drive[2]) :
> print 'drive %s conflicts' % (options.drive_id)
>
> newdisk.address_type = 'drive'
> newdisk.address_controller = drive[0]
> newdisk.address_bus = drive[1]
> newdisk.address_unit = drive[2]
>
> if options.boot_order != None :
> newdisk.boot_order = str(options.boot_order)
>
> if options.shareable == True:
> newdisk.shareab;e = True
>
> if options.serial != None :
> newdisk.serial = options.serial
> #
> # Handle encryption
> #
>
> if options.encryption_format != None :
> if options.encryption_type == None or\
> options.encryption_uuid == None :
> print 'encryption passphrase or UUID is not specified'
> exit(1)
> newdisk.encrypt_format = options.encryption_format
> newdisk.encrypt_type = options.encryption_type
> newdisk.encrypt_uuid = options.encryption_uuid
>
> #
> # Okay, we got all required information. Show the newdisk.
> #
> if options.yes != True :
> newdisk.show()
> #
> # Build XML from 'newdisk'
> #
> def append_attribute(doc, elem, attr, value) :
> x = doc.createAttribute(attr)
> elem.setAttributeNode(x)
> elem.setAttribute(attr, value)
>
> def append_text(doc, elem, text) :
> x = doc.createTextNode(text)
> elem.appendChild(x)
>
> def append_element(doc, parent, name, level) :
> append_text(doc, parent, '\n')
> while level > 0 :
> append_text(doc, parent, ' ')
> level = level - 1
> element = doc.createElement(name)
> parent.appendChild(element)
> return element
>
> # <disk ....
> element = dom.createElement('disk')
> append_attribute(dom, element, 'device', newdisk.media)
> append_attribute(dom, element, 'type', newdisk.type)
>
> #
> # <driver ...
> #
> child = append_element(dom, element, 'driver', 3)
> append_attribute(dom, child, 'name', newdisk.driver_name)
> append_attribute(dom, child, 'type', newdisk.driver_type)
> if newdisk.driver_cache != None :
> append_attribute(dom, child, 'cache', newdisk.driver_cache)
> if newdisk.driver_error_policy != None :
> append_attribute(dom, child, 'error_policy', newdisk.driver_error_policy)
> if newdisk.driver_io != None :
> append_attribute(dom, child, 'io', newdisk.driver_io)
>
> #
> # <source....
> #
> if newdisk.type == 'file' and newdisk.source != None:
> child = append_element(dom, element, 'source', 3)
> append_attribute(dom, child, 'file', options.source)
> elif newdisk.type == 'block' and newdisk.source != None:
> child = append_element(dom, element, 'source', 3)
> append_attribute(dom, child, 'dev', options.source)
> elif newdisk.type == 'network' and newdisk.protocol != None:
> child = append_element(dom, element, 'source', 3)
> append_attribute(dom, child, 'protocol', options.protocol)
> append_attribute(dom, child, 'name', options.source)
> host = append_element(dom, child, 'host', 4)
> address = re.split(':',options.host)
> append_attribute(dom, host, 'name', address[0])
> append_attribute(dom, host, 'port', address[1])
> #
> # <target....
> #
> child = append_element(dom, element, 'target', 3)
> append_attribute(dom, child, 'dev', newdisk.target_dev)
> append_attribute(dom, child, 'bus', newdisk.target_bus)
>
>
> #
> # <address.....
> # libvirt will do auto-fill in typical case.
> #
> if newdisk.address_type != None :
> child = append_element(dom, element, 'address', 3)
> append_attribute(dom, child, 'type', newdisk.address_type)
>
> if newdisk.address_type == 'pci' :
> append_attribute(dom, child, 'bus', newdisk.address_bus)
> append_attribute(dom, child, 'slot', newdisk.address_slot)
> append_attribute(dom, child, 'function', newdisk.address_function)
> append_attribute(dom, child, 'domain', '0x0000')
>
> elif newdisk.address_type == 'drive' :
> append_attribute(dom, child, 'controller', newdisk.address_controller)
> append_attribute(dom, child, 'unit', newdisk.address_unit)
> append_attribute(dom, child, 'bus', newdisk.address_bus)
> append_attribute(dom, child, 'domain', '0x0000')
> #
> # <readonly
> #
> if newdisk.readonly == True:
> append_element(dom, element, 'readonly', 3)
>
> #
> # <shareable
> #
> if newdisk.shareable == True:
> append_element(dom, element, 'readonly', 3)
> #
> # <boot
> #
> if newdisk.boot_order != None:
> child = append_element(dom, element, 'boot', 3)
> append_attribute(dom, child, 'order', newdisk.boot_order)
>
> #
> # <serial
> #
> if newdisk.serial != None:
> child = append_element(dom, element, 'serial', 3)
> append_text(dom, child, newdisk.serial)
>
> #
> # <encryption
> #
>
> if newdisk.encrypt_format != None :
> child = append_element(dom, element, 'encryption', 3)
> append_attribute(dom, child, 'format', newdisk.encrypt_format)
> secret = append_element(dom, child, 'secret', 4)
> append_attribute(dom, secret, 'type', newdisk.encrypt_type)
> append_attribute(dom, secret, 'uuid', newdisk.encrypt_uuid)
> append_text(dom, child, '\n')
>
> # indent for </disk>
> append_text(dom, element, '\n')
> append_text(dom, element, ' ')
>
> if options.nosave == True :
> print ''
> print element.toxml('utf-8')
> exit(0)
>
> #
> # Insert newdisk to the tail of disks.
> #
> for devices in names :
> if not devices.hasChildNodes() :
> continue
> for name in devices.childNodes :
> if name.nodeName == 'controller' :
> break
> if name == None :
> append_text(dom, devices, ' ')
> x = dom.createTextNode('\n')
> devices.appendChild(element)
> devices.insertBefore(x, name)
> else:
> devices.insertBefore(element, name)
> x = dom.createTextNode('\n ')
> devices.insertBefore(x, name)
>
> if not options.yes :
> x = raw_input('Add this device Y/N ? ')
> if x != 'y' and x != 'Y' :
> exit(1)
>
> str = dom.toxml('utf-8')
>
> check_domain_unchanged(origin, domain)
>
> try:
> conn.defineXML(str)
> except:
> print 'Failed'
>
> _______________________________________________
> virt-tools-list mailing list
> virt-tools-list at redhat.com
> https://www.redhat.com/mailman/listinfo/virt-tools-list
More information about the virt-tools-list
mailing list