Files
xen/tools/python/xen/xend/server/blkif.py
T
kfraser@localhost.localdomain e080ffba8d Fix/cleanup destroyDevice code path in xend.
When calling destroyDevice code path (e.g. xm block-detach dom devid),
allow specifying an integer device id or a device name such as xvdN or
/dev/xvdN.  Allowing the /dev/xvdN form is useful when detaching
devices from dom0.  Bootloaders may do this to unmount a disk
previously mounted in dom0.

Move examination of device ID format into the DevController,
permitting device controllers to determine a valid device ID instead
of higher level code.

Signed-off-by: Jim Fehlig <jfehlig@novell.com>
2007-08-06 10:12:34 +01:00

176 lines
6.4 KiB
Python

#============================================================================
# This library is free software; you can redistribute it and/or
# modify it under the terms of version 2.1 of the GNU Lesser General Public
# License as published by the Free Software Foundation.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#============================================================================
# Copyright (C) 2004, 2005 Mike Wray <mike.wray@hp.com>
# Copyright (C) 2005, 2006 XenSource Inc.
#============================================================================
import re
import string
from xen.util import blkif
from xen.util import security
from xen.xend.XendError import VmError
from xen.xend.server.DevController import DevController
class BlkifController(DevController):
"""Block device interface controller. Handles all block devices
for a domain.
"""
def __init__(self, vm):
"""Create a block device controller.
"""
DevController.__init__(self, vm)
def getDeviceDetails(self, config):
"""@see DevController.getDeviceDetails"""
uname = config.get('uname', '')
dev = config.get('dev', '')
if 'ioemu:' in dev:
(_, dev) = string.split(dev, ':', 1)
try:
(dev, dev_type) = string.split(dev, ':', 1)
except ValueError:
dev_type = "disk"
if uname is None:
if dev_type == 'cdrom':
(typ, params) = ("", "")
else:
raise VmError(
'Block device must have physical details specified')
else:
try:
(typ, params) = string.split(uname, ':', 1)
except ValueError:
(typ, params) = ("", "")
mode = config.get('mode', 'r')
if mode not in ('r', 'w', 'w!'):
raise VmError('Invalid mode')
back = {'dev' : dev,
'type' : typ,
'params' : params,
'mode' : mode,
}
uuid = config.get('uuid')
if uuid:
back['uuid'] = uuid
if security.on():
self.do_access_control(config, uname)
devid = blkif.blkdev_name_to_number(dev)
if devid is None:
raise VmError('Unable to find number for device (%s)' % (dev))
front = { 'virtual-device' : "%i" % devid,
'device-type' : dev_type
}
return (devid, back, front)
def do_access_control(self, config, uname):
(label, ssidref, policy) = \
security.get_res_security_details(uname)
domain_label = self.vm.get_security_label()
if domain_label:
rc = security.res_security_check_xapi(label, ssidref, policy,
domain_label)
if rc == 0:
raise VmError("VM's access to block device '%s' denied" %
uname)
else:
from xen.util.acmpolicy import ACM_LABEL_UNLABELED
if label != ACM_LABEL_UNLABELED:
raise VmError("VM must have a security label to access "
"block device '%s'" % uname)
def reconfigureDevice(self, _, config):
"""@see DevController.reconfigureDevice"""
(devid, new_back, new_front) = self.getDeviceDetails(config)
(dev, mode) = self.readBackend(devid, 'dev', 'mode')
dev_type = self.readFrontend(devid, 'device-type')
if (dev_type == 'cdrom' and new_front['device-type'] == 'cdrom' and
dev == new_back['dev'] and mode == 'r'):
# dummy device
self.writeBackend(devid,
'type', new_back['type'],
'params', '')
# new backend-device
self.writeBackend(devid,
'type', new_back['type'],
'params', new_back['params'])
return new_back.get('uuid')
else:
raise VmError('Refusing to reconfigure device %s:%d to %s' %
(self.deviceClass, devid, config))
def getDeviceConfiguration(self, devid):
"""Returns the configuration of a device.
@note: Similar to L{configuration} except it returns a dict.
@return: dict
"""
config = DevController.getDeviceConfiguration(self, devid)
devinfo = self.readBackend(devid, 'dev', 'type', 'params', 'mode',
'uuid')
dev, typ, params, mode, uuid = devinfo
if dev:
dev_type = self.readFrontend(devid, 'device-type')
if dev_type:
dev += ':' + dev_type
config['dev'] = dev
if typ and params:
config['uname'] = typ +':' + params
else:
config['uname'] = None
if mode:
config['mode'] = mode
if uuid:
config['uuid'] = uuid
return config
def destroyDevice(self, devid, force):
"""@see DevController.destroyDevice"""
# vbd device IDs can be either string or integer. Further, the
# following string values are possible:
# - devicetype/deviceid (vbd/51728)
# - devicetype/devicename (/dev/xvdb)
# - devicename (xvdb)
# Let our superclass handle integer or devicetype/deviceid forms.
# If we are given a device name form, then look up the device ID
# from it, and destroy that ID instead.
try:
DevController.destroyDevice(self, devid, force)
except ValueError:
devid_end = type(devid) is str and devid.split('/')[-1] or None
for i in self.deviceIDs():
d = self.readBackend(i, 'dev')
if d == devid or (devid_end and d == devid_end):
DevController.destroyDevice(self, i, force)
return
raise VmError("Device %s not connected" % devid)