ia64/xen-unstable

changeset 19657: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>
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 -                            dev_uuid = sxp.child_value(o_dev_info, 'uuid')
    1.94 -                            if dev_uuid != None and \
    1.95 -                               self.device_update(dev_uuid, cfg_sxp = merge_sxp):
    1.96 -                                return dev_uuid
    1.97 +         # Enumerate devices for current domain
    1.98 +         sxpr = self.all_devices_sxpr(target = defined_config)
    1.99  
   1.100 -                        raise XendConfigError('The device "%s" is already defined' %
   1.101 -                                              blkdev_name)
   1.102 -                    
   1.103 -        elif dev_type == 'vif':
   1.104 -            dev_mac = dev_info.get('mac')
   1.105 -            
   1.106 -            for o_dev_type, o_dev_info in defined_devices_sxpr:
   1.107 -                if dev_type == o_dev_type:
   1.108 -                    if dev_mac.lower() == sxp.child_value(o_dev_info, 'mac').lower():
   1.109 -                        raise XendConfigError('The mac "%s" is already defined' %
   1.110 -                                              dev_mac)
   1.111 -        return None
   1.112 +         # For vif interface we won't check cross-domain
   1.113 +         if sxpr == None and dev_type == 'vif':
   1.114 +            return
   1.115 +
   1.116 +         # Preset None values to all variables we'll be checking
   1.117 +         new_uname = None
   1.118 +         uname = None
   1.119 +         dev = None
   1.120 +         mac = None
   1.121 +         mode = None
   1.122 +
   1.123 +         # Disk device
   1.124 +         if dev_type in ['vbd', 'tap']:
   1.125 +             for x in config:
   1.126 +                 if type(x) != str and (x[0] in ['uname', 'dev', 'mode']):
   1.127 +                     if x[0] == 'uname':
   1.128 +                         new_uname = x[1]
   1.129 +                     if x[0] == 'dev':
   1.130 +                         dev = x[1]
   1.131 +                     if x[0] == 'mode':
   1.132 +                         mode = x[1]
   1.133 +
   1.134 +             # If we don't have uname entry (happens in virt-manager) return
   1.135 +             if new_uname == None:
   1.136 +                 return
   1.137 +
   1.138 +             new_uname = new_uname.split(":")[len(new_uname.split(":"))-1]
   1.139 +             # We need to allow when uname is zero length, eg. hdc:cdrom device
   1.140 +             if len(new_uname) == 0:
   1.141 +                 log.debug("Null uname when attaching disk device, allowing %s..."
   1.142 +                           % dev)
   1.143 +                 return
   1.144 +
   1.145 +             log.debug("Checking for duplicate for uname: %s, dev: %s, mode: %s"
   1.146 +                       % (new_uname, dev, mode))
   1.147 +             # No device in dev found
   1.148 +             if dev == None:
   1.149 +                 return
   1.150 +
   1.151 +             devid = self._blkdev_name_to_number(dev)
   1.152 +             if devid == None:
   1.153 +                 return
   1.154 +
   1.155 +             for o_dev_info in sxpr:
   1.156 +                 # Get information only for tap/vbd block devices
   1.157 +                 if o_dev_info[0] in ['tap', 'vbd']:
   1.158 +                     uname = self.device_tuple_value_from_dev_info(o_dev_info, "uname")
   1.159 +                     dev = self.device_tuple_value_from_dev_info(o_dev_info, "dev")
   1.160 +                     dev_uname = None
   1.161 +                     if uname != None:
   1.162 +                         dev_uname = uname.split(":")[len(uname.split(":"))-1]
   1.163 +                     if new_uname == dev_uname:
   1.164 +                         raise XendConfigError('The uname "%s" is already defined' %
   1.165 +                                               dev_uname)
   1.166 +
   1.167 +                     blkdev = dev.split(":")[0]
   1.168 +                     blkdevid = self._blkdev_name_to_number(blkdev)
   1.169 +                     if blkdevid != None and devid == blkdevid:
   1.170 +                         raise XendConfigError('The device "%s" is already defined' %
   1.171 +                                               blkdev)
   1.172 +
   1.173 +             tMode = self.block_device_mode_translate(mode)
   1.174 +
   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):