debuggers.hg
changeset 19695:9ff5c79b0ceb
xend: Device duplicate check fix
I've checked the duplicate-check code here and I found that's checked
only in the context of one domain but not cross-domain. The thing is
that we should check tap/vbd device cross-domain not to allow another
guest to use the same disk image in some circumstances to prevent VM's
disk corruption.
The patch included denies disk image addition under those
circumstances:
1. We're adding read-only disk that's already used as write-exclusive
2. We're adding write-shared disk that's already used as
write-exclusive
3. We're adding write-exclusive disk that's already used
4. We're adding read-only disk that's already used as write-shared*
(because of I/O caching issues etc.)
The vif device duplicate check remains the same it was and it's
checked in the context of current domain only so that behaviour has
been preserved.
Signed-off-by: Michal Novotny <minovotn@redhat.com>
I've checked the duplicate-check code here and I found that's checked
only in the context of one domain but not cross-domain. The thing is
that we should check tap/vbd device cross-domain not to allow another
guest to use the same disk image in some circumstances to prevent VM's
disk corruption.
The patch included denies disk image addition under those
circumstances:
1. We're adding read-only disk that's already used as write-exclusive
2. We're adding write-shared disk that's already used as
write-exclusive
3. We're adding write-exclusive disk that's already used
4. We're adding read-only disk that's already used as write-shared*
(because of I/O caching issues etc.)
The vif device duplicate check remains the same it was and it's
checked in the context of current domain only so that behaviour has
been preserved.
Signed-off-by: Michal Novotny <minovotn@redhat.com>
author | Keir Fraser <keir.fraser@citrix.com> |
---|---|
date | Wed May 27 11:28:45 2009 +0100 (2009-05-27) |
parents | 87c411a7c1df |
children | 28a197617286 |
files | tools/python/xen/xend/XendConfig.py |
line diff
1.1 --- a/tools/python/xen/xend/XendConfig.py Wed May 27 11:27:13 2009 +0100 1.2 +++ b/tools/python/xen/xend/XendConfig.py Wed May 27 11:28:45 2009 +0100 1.3 @@ -19,6 +19,7 @@ import logging 1.4 import re 1.5 import time 1.6 import types 1.7 +import XendDomain 1.8 1.9 from xen.xend import sxp 1.10 from xen.xend import uuid 1.11 @@ -1162,65 +1163,142 @@ class XendConfig(dict): 1.12 return None 1.13 return devid 1.14 1.15 + def device_tuple_value_from_dev_info(self, dev_info, key): 1.16 + for x in dev_info: 1.17 + if (type(x) != str): 1.18 + for xx in x: 1.19 + if (xx[0] == key): 1.20 + return xx[1] 1.21 + return None 1.22 + 1.23 + # This function translates all block device modes (incl. aliases) to 1.24 + # one common label per each device mode. Those modes can be: 1.25 + # read-only (ro), write-exclusive (wx) and write-shared (ws) 1.26 + def block_device_mode_translate(self, mode): 1.27 + # Device modes can be read-only (ro), write-exclusive (wx) or 1.28 + # write-shared (ws), otherwise an error is raised 1.29 + if mode == "w" or mode == "wr": 1.30 + return "wx" 1.31 + elif mode == "r" or mode == "ro": 1.32 + return "ro" 1.33 + elif mode == "!" or mode == "w!": 1.34 + return "ws" 1.35 + 1.36 + # If no mode defined we consider this as write-exclusive 1.37 + return "wx" 1.38 + 1.39 + # Detect device duplicates for vbd, tap and vif devices for domain and 1.40 + # duplicate unames in global context not to destroy virtual block devices 1.41 def device_duplicate_check(self, dev_type, dev_info, defined_config, config): 1.42 - defined_devices_sxpr = self.all_devices_sxpr(target = defined_config) 1.43 - 1.44 - if dev_type == 'vbd' or dev_type == 'tap': 1.45 - dev_uname = dev_info.get('uname') 1.46 - blkdev_name = dev_info.get('dev') 1.47 - devid = self._blkdev_name_to_number(blkdev_name) 1.48 - if devid == None or dev_uname == None: 1.49 - return 1.50 - 1.51 - for o_dev_type, o_dev_info in defined_devices_sxpr: 1.52 - if o_dev_type == 'vbd' or o_dev_type == 'tap': 1.53 - blkdev_file = blkdev_uname_to_file(dev_uname) 1.54 - o_dev_uname = sxp.child_value(o_dev_info, 'uname') 1.55 - if o_dev_uname != None: 1.56 - o_blkdev_file = blkdev_uname_to_file(o_dev_uname) 1.57 - if blkdev_file == o_blkdev_file: 1.58 - raise XendConfigError('The file "%s" is already used' % 1.59 - blkdev_file) 1.60 - if dev_uname == o_dev_uname: 1.61 - raise XendConfigError('The uname "%s" is already defined' % 1.62 - dev_uname) 1.63 - o_blkdev_name = sxp.child_value(o_dev_info, 'dev') 1.64 - o_devid = self._blkdev_name_to_number(o_blkdev_name) 1.65 - if o_devid != None and devid == o_devid: 1.66 - name_array = blkdev_name.split(':', 2) 1.67 - if len(name_array) == 2 and name_array[1] == 'cdrom': 1.68 - # 1.69 - # Since the device is a cdrom, we are most likely 1.70 - # inserting, changing, or removing a cd. We can 1.71 - # update the old device instead of creating a new 1.72 - # one. 1.73 - # 1.74 - if o_dev_uname != None and dev_uname == None: 1.75 - # 1.76 - # We are removing a cd. We can simply update 1.77 - # the uname on the existing device. 1.78 - # 1.79 - merge_sxp = sxp.from_string("('vbd' ('uname' ''))") 1.80 - else: 1.81 - merge_sxp = config 1.82 + # Enumerate all devices for all domains 1.83 + allSxprs = [] 1.84 + val = XendDomain.instance().domains.values() 1.85 + for v in val: 1.86 + sxpr = v.getDeviceSxprs(dev_type) 1.87 + for key in sxpr: 1.88 + try: 1.89 + index = allSxprs.index(key) 1.90 + except: 1.91 + allSxprs.append(key) 1.92 + 1.93 + # Enumerate devices for current domain 1.94 + sxpr = self.all_devices_sxpr(target = defined_config) 1.95 + 1.96 + # For vif interface we won't check cross-domain 1.97 + if sxpr == None and dev_type == 'vif': 1.98 + return 1.99 + 1.100 + # Preset None values to all variables we'll be checking 1.101 + new_uname = None 1.102 + uname = None 1.103 + dev = None 1.104 + mac = None 1.105 + mode = None 1.106 + 1.107 + # Disk device 1.108 + if dev_type in ['vbd', 'tap']: 1.109 + for x in config: 1.110 + if type(x) != str and (x[0] in ['uname', 'dev', 'mode']): 1.111 + if x[0] == 'uname': 1.112 + new_uname = x[1] 1.113 + if x[0] == 'dev': 1.114 + dev = x[1] 1.115 + if x[0] == 'mode': 1.116 + mode = x[1] 1.117 + 1.118 + # If we don't have uname entry (happens in virt-manager) return 1.119 + if new_uname == None: 1.120 + return 1.121 + 1.122 + new_uname = new_uname.split(":")[len(new_uname.split(":"))-1] 1.123 + # We need to allow when uname is zero length, eg. hdc:cdrom device 1.124 + if len(new_uname) == 0: 1.125 + log.debug("Null uname when attaching disk device, allowing %s..." 1.126 + % dev) 1.127 + return 1.128 + 1.129 + log.debug("Checking for duplicate for uname: %s, dev: %s, mode: %s" 1.130 + % (new_uname, dev, mode)) 1.131 + # No device in dev found 1.132 + if dev == None: 1.133 + return 1.134 1.135 - dev_uuid = sxp.child_value(o_dev_info, 'uuid') 1.136 - if dev_uuid != None and \ 1.137 - self.device_update(dev_uuid, cfg_sxp = merge_sxp): 1.138 - return dev_uuid 1.139 + devid = self._blkdev_name_to_number(dev) 1.140 + if devid == None: 1.141 + return 1.142 + 1.143 + for o_dev_info in sxpr: 1.144 + # Get information only for tap/vbd block devices 1.145 + if o_dev_info[0] in ['tap', 'vbd']: 1.146 + uname = self.device_tuple_value_from_dev_info(o_dev_info, "uname") 1.147 + dev = self.device_tuple_value_from_dev_info(o_dev_info, "dev") 1.148 + dev_uname = None 1.149 + if uname != None: 1.150 + dev_uname = uname.split(":")[len(uname.split(":"))-1] 1.151 + if new_uname == dev_uname: 1.152 + raise XendConfigError('The uname "%s" is already defined' % 1.153 + dev_uname) 1.154 + 1.155 + blkdev = dev.split(":")[0] 1.156 + blkdevid = self._blkdev_name_to_number(blkdev) 1.157 + if blkdevid != None and devid == blkdevid: 1.158 + raise XendConfigError('The device "%s" is already defined' % 1.159 + blkdev) 1.160 + 1.161 + tMode = self.block_device_mode_translate(mode) 1.162 1.163 - raise XendConfigError('The device "%s" is already defined' % 1.164 - blkdev_name) 1.165 - 1.166 - elif dev_type == 'vif': 1.167 - dev_mac = dev_info.get('mac') 1.168 - 1.169 - for o_dev_type, o_dev_info in defined_devices_sxpr: 1.170 - if dev_type == o_dev_type: 1.171 - if dev_mac.lower() == sxp.child_value(o_dev_info, 'mac').lower(): 1.172 - raise XendConfigError('The mac "%s" is already defined' % 1.173 - dev_mac) 1.174 - return None 1.175 + # Device/uname not found in the context of current domain but we 1.176 + # need to have a look to global context. We deny addition of device 1.177 + # in those cases: 1.178 + # 1. We're adding read-only disk that's already used as write-exclusive 1.179 + # 2. We're adding write-shared disk that's already used as write-exclusive 1.180 + # 3. We're adding write-exclusive disk that's already used 1.181 + # 4. We're adding read-only disk that's already used as write-shared 1.182 + for o_dev_info in allSxprs: 1.183 + backend = self.device_tuple_value_from_dev_info(o_dev_info, "backend") 1.184 + params = xstransact.Read(backend, "params") 1.185 + aMode = self.block_device_mode_translate( 1.186 + xstransact.Read(backend, "mode") ) 1.187 + dev_uname = params.split(":")[len(params.split(":"))-1] 1.188 + if new_uname == dev_uname: 1.189 + if ((tMode == "ro" and aMode == "wx") 1.190 + or (tMode == "ws" and aMode == "wx") 1.191 + or (tMode == "ro" and aMode == "ws") 1.192 + or (tMode == "wx")): 1.193 + raise XendConfigError('The uname "%s" is already used by another domain' % 1.194 + dev_uname) 1.195 + 1.196 + # Virtual network adapter 1.197 + elif dev_type == 'vif': 1.198 + dev_mac = dev_info.get('mac') 1.199 + 1.200 + for o_dev_type, o_dev_info in sxpr: 1.201 + if dev_type == o_dev_type: 1.202 + if dev_mac.lower() == sxp.child_value(o_dev_info, 'mac').lower(): 1.203 + raise XendConfigError('The mac "%s" is already defined' % 1.204 + dev_mac) 1.205 + 1.206 + return None 1.207 1.208 def device_add(self, dev_type, cfg_sxp = None, cfg_xenapi = None, 1.209 target = None):