[virt-tools-list] [RFC] virt-disk : a command line tool for modify domain XML's disk of inactive domains.
KAMEZAWA Hiroyuki
kamezawa.hiroyu at jp.fujitsu.com
Mon Feb 21 08:23:27 UTC 2011
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 ?
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'
More information about the virt-tools-list
mailing list