xen-vtx-unstable
annotate tools/python/xen/xend/server/controller.py @ 6796:0d8c0db04258
Don't return failure when trying to delete a non-existent node.
Signed-off-by: Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
Signed-off-by: Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
author | cl349@firebug.cl.cam.ac.uk |
---|---|
date | Tue Sep 13 21:52:24 2005 +0000 (2005-09-13) |
parents | 72e4e2aab342 |
children |
rev | line source |
---|---|
kaf24@6064 | 1 #============================================================================ |
kaf24@6064 | 2 # This library is free software; you can redistribute it and/or |
kaf24@6064 | 3 # modify it under the terms of version 2.1 of the GNU Lesser General Public |
kaf24@6064 | 4 # License as published by the Free Software Foundation. |
kaf24@6064 | 5 # |
kaf24@6064 | 6 # This library is distributed in the hope that it will be useful, |
kaf24@6064 | 7 # but WITHOUT ANY WARRANTY; without even the implied warranty of |
kaf24@6064 | 8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
kaf24@6064 | 9 # Lesser General Public License for more details. |
kaf24@6064 | 10 # |
kaf24@6064 | 11 # You should have received a copy of the GNU Lesser General Public |
kaf24@6064 | 12 # License along with this library; if not, write to the Free Software |
kaf24@6064 | 13 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
kaf24@6064 | 14 #============================================================================ |
kaf24@6064 | 15 # Copyright (C) 2004, 2005 Mike Wray <mike.wray@hp.com> |
kaf24@6064 | 16 #============================================================================ |
kaf24@6064 | 17 |
mjw@1719 | 18 """General support for controllers, which handle devices |
mjw@1719 | 19 for a domain. |
mjw@1719 | 20 """ |
mjw@1654 | 21 |
mjw@4580 | 22 from xen.xend.XendError import XendError |
cl349@5388 | 23 from xen.xend.xenstore import DBVar |
mjw@1623 | 24 |
mjw@2397 | 25 DEBUG = 0 |
mjw@1677 | 26 |
mjw@4580 | 27 class DevControllerTable: |
mjw@4668 | 28 """Table of device controller classes, indexed by type name. |
mjw@2332 | 29 """ |
mjw@2332 | 30 |
mjw@2332 | 31 def __init__(self): |
mjw@4668 | 32 self.controllerClasses = {} |
mjw@4580 | 33 |
mjw@4668 | 34 def getDevControllerClass(self, type): |
mjw@4668 | 35 return self.controllerClasses.get(type) |
mjw@4580 | 36 |
cl349@5346 | 37 def addDevControllerClass(self, cls): |
cl349@5346 | 38 self.controllerClasses[cls.getType()] = cls |
mjw@2332 | 39 |
mjw@4668 | 40 def delDevControllerClass(self, type): |
mjw@4668 | 41 if type in self.controllerClasses: |
mjw@4668 | 42 del self.controllerClasses[type] |
mjw@2332 | 43 |
mjw@4580 | 44 def createDevController(self, type, vm, recreate=False): |
cl349@5346 | 45 cls = self.getDevControllerClass(type) |
cl349@5346 | 46 if not cls: |
kaf24@6131 | 47 raise XendError("unknown device type: " + str(type)) |
cl349@5346 | 48 return cls.createDevController(vm, recreate=recreate) |
mjw@2332 | 49 |
mjw@4580 | 50 def getDevControllerTable(): |
mjw@4668 | 51 """Singleton constructor for the controller table. |
mjw@4668 | 52 """ |
mjw@4580 | 53 global devControllerTable |
mjw@4580 | 54 try: |
mjw@4580 | 55 devControllerTable |
mjw@4580 | 56 except: |
mjw@4580 | 57 devControllerTable = DevControllerTable() |
mjw@4580 | 58 return devControllerTable |
mjw@1623 | 59 |
cl349@5346 | 60 def addDevControllerClass(name, cls): |
mjw@4668 | 61 """Add a device controller class to the controller table. |
mjw@4668 | 62 """ |
cl349@5346 | 63 cls.type = name |
cl349@5346 | 64 getDevControllerTable().addDevControllerClass(cls) |
mjw@4580 | 65 |
ewan@6771 | 66 |
ewan@6771 | 67 def isDevControllerClass(name): |
ewan@6771 | 68 """@return True if a device controller class has been registered with |
ewan@6771 | 69 the controller table under the given name.""" |
ewan@6771 | 70 return name in getDevControllerTable().controllerClasses |
ewan@6771 | 71 |
ewan@6771 | 72 |
mjw@4580 | 73 def createDevController(name, vm, recreate=False): |
mjw@4580 | 74 return getDevControllerTable().createDevController(name, vm, recreate=recreate) |
mjw@4580 | 75 |
mjw@4580 | 76 class DevController: |
mjw@4580 | 77 """Abstract class for a device controller attached to a domain. |
mjw@4580 | 78 A device controller manages all the devices of a given type for a domain. |
mjw@4580 | 79 There is exactly one device controller for each device type for |
mjw@4580 | 80 a domain. |
mjw@2332 | 81 |
mjw@4580 | 82 """ |
mjw@4580 | 83 |
cl349@5388 | 84 # State: |
cl349@5388 | 85 # controller/<type> : for controller |
cl349@5388 | 86 # device/<type>/<id> : for each device |
cl349@5388 | 87 |
cl349@5346 | 88 def createDevController(cls, vm, recreate=False): |
mjw@4668 | 89 """Class method to create a dev controller. |
mjw@4668 | 90 """ |
cl349@5346 | 91 ctrl = cls(vm, recreate=recreate) |
mjw@4668 | 92 ctrl.initController(recreate=recreate) |
cl349@5388 | 93 ctrl.exportToDB() |
mjw@4668 | 94 return ctrl |
mjw@4668 | 95 |
mjw@4668 | 96 createDevController = classmethod(createDevController) |
mjw@4668 | 97 |
cl349@5346 | 98 def getType(cls): |
cl349@5346 | 99 return cls.type |
mjw@4668 | 100 |
mjw@4668 | 101 getType = classmethod(getType) |
mjw@4668 | 102 |
cl349@5388 | 103 __exports__ = [ |
cl349@5388 | 104 DBVar('type', 'str'), |
cl349@5388 | 105 DBVar('destroyed', 'bool'), |
cl349@5388 | 106 ] |
cl349@5388 | 107 |
cl349@5346 | 108 # Set when registered. |
cl349@5346 | 109 type = None |
cl349@5346 | 110 |
mjw@4668 | 111 def __init__(self, vm, recreate=False): |
mjw@4580 | 112 self.destroyed = False |
mjw@4580 | 113 self.vm = vm |
cl349@5388 | 114 self.db = self.getDB() |
mjw@4580 | 115 self.deviceId = 0 |
mjw@4580 | 116 self.devices = {} |
mjw@4580 | 117 self.device_order = [] |
mjw@2332 | 118 |
cl349@5388 | 119 def getDB(self): |
cl349@5388 | 120 """Get the db node to use for a controller. |
cl349@5388 | 121 """ |
cl349@5388 | 122 return self.vm.db.addChild("/controller/%s" % self.getType()) |
cl349@5388 | 123 |
cl349@5388 | 124 def getDevDB(self, id): |
cl349@5388 | 125 """Get the db node to use for a device. |
cl349@5388 | 126 """ |
cl349@5388 | 127 return self.vm.db.addChild("/device/%s/%s" % (self.getType(), id)) |
cl349@5388 | 128 |
cl349@5388 | 129 def exportToDB(self, save=False): |
cl349@5388 | 130 self.db.exportToDB(self, fields=self.__exports__, save=save) |
cl349@5388 | 131 |
cl349@5388 | 132 def importFromDB(self): |
cl349@5388 | 133 self.db.importFromDB(self, fields=self.__exports__) |
cl349@5388 | 134 |
mjw@4580 | 135 def getDevControllerType(self): |
mjw@4580 | 136 return self.dctype |
mjw@2332 | 137 |
mjw@4580 | 138 def getDomain(self): |
mjw@4580 | 139 return self.vm.getDomain() |
mjw@4580 | 140 |
mjw@4580 | 141 def getDomainName(self): |
mjw@4580 | 142 return self.vm.getName() |
mjw@2332 | 143 |
mjw@4580 | 144 def getDomainInfo(self): |
mjw@4580 | 145 return self.vm |
mjw@2332 | 146 |
mjw@4580 | 147 #---------------------------------------------------------------------------- |
mjw@4580 | 148 # Subclass interface. |
mjw@4580 | 149 # Subclasses should define the unimplemented methods.. |
mjw@4580 | 150 # Redefinitions must have the same arguments. |
mjw@2332 | 151 |
mjw@4580 | 152 def initController(self, recreate=False, reboot=False): |
mjw@4668 | 153 """Initialise the controller. Called when the controller is |
mjw@4668 | 154 first created, and again after the domain is rebooted (with reboot True). |
mjw@4668 | 155 If called with recreate True (and reboot False) the controller is being |
mjw@4668 | 156 recreated after a xend restart. |
mjw@4668 | 157 |
mjw@4668 | 158 As this can be a re-init (after reboot) any controller state should |
mjw@4668 | 159 be reset. For example the destroyed flag. |
mjw@4668 | 160 """ |
mjw@4580 | 161 self.destroyed = False |
mjw@4580 | 162 if reboot: |
mjw@4580 | 163 self.rebootDevices() |
mjw@4580 | 164 |
mjw@4580 | 165 def newDevice(self, id, config, recreate=False): |
mjw@4580 | 166 """Create a device with the given config. |
mjw@4580 | 167 Must be defined in subclass. |
mjw@4668 | 168 Called with recreate True when the device is being recreated after a |
mjw@4668 | 169 xend restart. |
mjw@4580 | 170 |
mjw@4580 | 171 @return device |
mjw@2332 | 172 """ |
mjw@2332 | 173 raise NotImplementedError() |
mjw@2332 | 174 |
mjw@4580 | 175 def createDevice(self, config, recreate=False, change=False): |
mjw@4668 | 176 """Create a device and attach to its front- and back-ends. |
mjw@4668 | 177 If recreate is true the device is being recreated after a xend restart. |
mjw@4668 | 178 If change is true the device is a change to an existing domain, |
mjw@4668 | 179 i.e. it is being added at runtime rather than when the domain is created. |
mjw@4668 | 180 """ |
cl349@5388 | 181 dev = self.newDevice(self.nextDeviceId(), config, recreate=recreate) |
cl349@5388 | 182 if self.vm.recreate: |
cl349@5388 | 183 dev.importFromDB() |
mjw@4580 | 184 dev.init(recreate=recreate) |
mjw@4580 | 185 self.addDevice(dev) |
cl349@5388 | 186 if not recreate: |
cl349@5388 | 187 dev.exportToDB() |
mjw@4580 | 188 dev.attach(recreate=recreate, change=change) |
cl349@5388 | 189 dev.exportToDB() |
mjw@4580 | 190 |
kaf24@6131 | 191 return dev |
kaf24@6131 | 192 |
mjw@4580 | 193 def configureDevice(self, id, config, change=False): |
mjw@4580 | 194 """Reconfigure an existing device. |
mjw@4580 | 195 May be defined in subclass.""" |
cl349@5338 | 196 dev = self.getDevice(id, error=True) |
mjw@4580 | 197 dev.configure(config, change=change) |
mjw@2267 | 198 |
mjw@4580 | 199 def destroyDevice(self, id, change=False, reboot=False): |
mjw@4580 | 200 """Destroy a device. |
mjw@4668 | 201 May be defined in subclass. |
mjw@4668 | 202 |
mjw@4668 | 203 If reboot is true the device is being destroyed for a domain reboot. |
mjw@4668 | 204 |
mjw@4668 | 205 The device is not deleted, since it may be recreated later. |
mjw@4668 | 206 """ |
cl349@5338 | 207 dev = self.getDevice(id, error=True) |
mjw@4580 | 208 dev.destroy(change=change, reboot=reboot) |
mjw@4580 | 209 return dev |
mjw@4580 | 210 |
mjw@4580 | 211 def deleteDevice(self, id, change=True): |
mjw@4668 | 212 """Destroy a device and delete it. |
mjw@4668 | 213 Normally called to remove a device from a domain at runtime. |
mjw@4668 | 214 """ |
mjw@4580 | 215 dev = self.destroyDevice(id, change=change) |
mjw@4580 | 216 self.removeDevice(dev) |
mjw@4580 | 217 |
mjw@4580 | 218 def destroyController(self, reboot=False): |
mjw@4580 | 219 """Destroy all devices and clean up. |
mjw@4668 | 220 May be defined in subclass. |
mjw@4668 | 221 If reboot is true the controller is being destroyed for a domain reboot. |
mjw@4668 | 222 Called at domain shutdown. |
mjw@4668 | 223 """ |
mjw@4580 | 224 self.destroyed = True |
mjw@4580 | 225 self.destroyDevices(reboot=reboot) |
mjw@4580 | 226 |
mjw@4580 | 227 #---------------------------------------------------------------------------- |
mjw@4580 | 228 |
mjw@4580 | 229 def isDestroyed(self): |
mjw@4580 | 230 return self.destroyed |
mjw@4580 | 231 |
cl349@5338 | 232 def getDevice(self, id, error=False): |
kaf24@6132 | 233 dev = self.devices.get(int(id)) |
cl349@5338 | 234 if error and not dev: |
kaf24@6131 | 235 raise XendError("invalid device id: " + str(id)) |
cl349@5338 | 236 return dev |
mjw@4580 | 237 |
mjw@4580 | 238 def getDeviceIds(self): |
mjw@4580 | 239 return [ dev.getId() for dev in self.device_order ] |
mjw@4580 | 240 |
mjw@4580 | 241 def getDevices(self): |
mjw@4580 | 242 return self.device_order |
mjw@4580 | 243 |
mjw@4580 | 244 def getDeviceConfig(self, id): |
mjw@4580 | 245 return self.getDevice(id).getConfig() |
mjw@4580 | 246 |
mjw@4580 | 247 def getDeviceConfigs(self): |
mjw@4580 | 248 return [ dev.getConfig() for dev in self.device_order ] |
mjw@4580 | 249 |
mjw@4580 | 250 def getDeviceSxprs(self): |
mjw@4580 | 251 return [ dev.sxpr() for dev in self.device_order ] |
mjw@4580 | 252 |
mjw@4580 | 253 def addDevice(self, dev): |
mjw@4580 | 254 self.devices[dev.getId()] = dev |
mjw@4580 | 255 self.device_order.append(dev) |
mjw@4580 | 256 return dev |
mjw@4580 | 257 |
mjw@4580 | 258 def removeDevice(self, dev): |
mjw@4580 | 259 if dev.getId() in self.devices: |
mjw@4580 | 260 del self.devices[dev.getId()] |
mjw@4580 | 261 if dev in self.device_order: |
mjw@4580 | 262 self.device_order.remove(dev) |
mjw@4580 | 263 |
mjw@4580 | 264 def rebootDevices(self): |
mjw@4580 | 265 for dev in self.getDevices(): |
mjw@4580 | 266 dev.reboot() |
mjw@4580 | 267 |
mjw@4580 | 268 def destroyDevices(self, reboot=False): |
mjw@4580 | 269 """Destroy all devices. |
mjw@4580 | 270 """ |
mjw@4580 | 271 for dev in self.getDevices(): |
mjw@4580 | 272 dev.destroy(reboot=reboot) |
mjw@2225 | 273 |
mjw@4580 | 274 def getMaxDeviceId(self): |
mjw@4580 | 275 maxid = 0 |
mjw@4580 | 276 for id in self.devices: |
mjw@4580 | 277 if id > maxid: |
mjw@4580 | 278 maxid = id |
mjw@4580 | 279 return maxid |
mjw@4580 | 280 |
mjw@4580 | 281 def nextDeviceId(self): |
mjw@4580 | 282 id = self.deviceId |
mjw@4580 | 283 self.deviceId += 1 |
mjw@4580 | 284 return id |
mjw@4580 | 285 |
mjw@4580 | 286 def getDeviceCount(self): |
mjw@4580 | 287 return len(self.devices) |
mjw@4580 | 288 |
mjw@4580 | 289 class Dev: |
mjw@4580 | 290 """Abstract class for a device attached to a device controller. |
mjw@2332 | 291 |
mjw@4580 | 292 @ivar id: identifier |
mjw@4580 | 293 @type id: int |
mjw@4580 | 294 @ivar controller: device controller |
mjw@4580 | 295 @type controller: DevController |
mjw@4580 | 296 """ |
mjw@4580 | 297 |
cl349@5388 | 298 # ./status : need 2: actual and requested? |
cl349@5388 | 299 # down-down: initial. |
cl349@5388 | 300 # up-up: fully up. |
cl349@5388 | 301 # down-up: down requested, still up. Watch front and back, when both |
cl349@5388 | 302 # down go to down-down. But what if one (or both) is not connected? |
cl349@5388 | 303 # Still have front/back trees with status? Watch front/status, back/status? |
cl349@5388 | 304 # up-down: up requested, still down. |
cl349@5388 | 305 # Back-end watches ./status, front/status |
cl349@5388 | 306 # Front-end watches ./status, back/status |
cl349@5388 | 307 # i.e. each watches the other 2. |
cl349@5388 | 308 # Each is status/request status/actual? |
cl349@5388 | 309 # |
cl349@5388 | 310 # backend? |
cl349@5388 | 311 # frontend? |
cl349@5388 | 312 |
cl349@5388 | 313 __exports__ = [ |
cl349@5388 | 314 DBVar('id', ty='int'), |
cl349@5388 | 315 DBVar('type', ty='str'), |
cl349@5388 | 316 DBVar('config', ty='sxpr'), |
cl349@5388 | 317 DBVar('destroyed', ty='bool'), |
cl349@5388 | 318 ] |
cl349@5388 | 319 |
mjw@4580 | 320 def __init__(self, controller, id, config, recreate=False): |
mjw@4580 | 321 self.controller = controller |
mjw@4580 | 322 self.id = id |
mjw@4580 | 323 self.config = config |
mjw@4580 | 324 self.destroyed = False |
cl349@5388 | 325 self.type = self.getType() |
cl349@5388 | 326 |
cl349@5388 | 327 self.db = controller.getDevDB(id) |
cl349@5388 | 328 |
cl349@5388 | 329 def exportToDB(self, save=False): |
cl349@5388 | 330 self.db.exportToDB(self, fields=self.__exports__, save=save) |
cl349@5388 | 331 |
cl349@5388 | 332 def importFromDB(self): |
cl349@5388 | 333 self.db.importFromDB(self, fields=self.__exports__) |
mjw@4580 | 334 |
mjw@4580 | 335 def getDomain(self): |
mjw@4580 | 336 return self.controller.getDomain() |
mjw@4580 | 337 |
mjw@4580 | 338 def getDomainName(self): |
mjw@4580 | 339 return self.controller.getDomainName() |
mjw@4580 | 340 |
mjw@4580 | 341 def getDomainInfo(self): |
mjw@4580 | 342 return self.controller.getDomainInfo() |
mjw@4580 | 343 |
mjw@4580 | 344 def getController(self): |
mjw@4580 | 345 return self.controller |
mjw@4580 | 346 |
mjw@4580 | 347 def getType(self): |
mjw@4580 | 348 return self.controller.getType() |
mjw@2332 | 349 |
mjw@4580 | 350 def getId(self): |
mjw@4580 | 351 return self.id |
mjw@4580 | 352 |
mjw@4580 | 353 def getConfig(self): |
mjw@4580 | 354 return self.config |
mjw@4580 | 355 |
mjw@4580 | 356 def isDestroyed(self): |
mjw@4580 | 357 return self.destroyed |
mjw@4580 | 358 |
mjw@4580 | 359 #---------------------------------------------------------------------------- |
mjw@4580 | 360 # Subclass interface. |
mjw@4580 | 361 # Define methods in subclass as needed. |
mjw@4580 | 362 # Redefinitions must have the same arguments. |
mjw@4580 | 363 |
mjw@4580 | 364 def init(self, recreate=False, reboot=False): |
mjw@4580 | 365 """Initialization. Called on initial create (when reboot is False) |
mjw@4580 | 366 and on reboot (when reboot is True). When xend is restarting is |
mjw@4580 | 367 called with recreate True. Define in subclass if needed. |
mjw@4668 | 368 |
mjw@4668 | 369 Device instance variables must be defined in the class constructor, |
mjw@4668 | 370 but given null or default values. The real values should be initialised |
mjw@4668 | 371 in this method. This allows devices to be re-initialised. |
mjw@4668 | 372 |
mjw@4668 | 373 Since this can be called to re-initialise a device any state flags |
mjw@4668 | 374 should be reset. |
mjw@4580 | 375 """ |
mjw@4580 | 376 self.destroyed = False |
mjw@2332 | 377 |
mjw@4580 | 378 def attach(self, recreate=False, change=False): |
mjw@4580 | 379 """Attach the device to its front and back ends. |
mjw@4580 | 380 Define in subclass if needed. |
mjw@4580 | 381 """ |
mjw@4580 | 382 pass |
mjw@4580 | 383 |
mjw@4580 | 384 def reboot(self): |
mjw@4668 | 385 """Reconnect the device when the domain is rebooted. |
mjw@4580 | 386 """ |
mjw@4580 | 387 self.init(reboot=True) |
mjw@4580 | 388 self.attach() |
mjw@2332 | 389 |
mjw@4580 | 390 def sxpr(self): |
mjw@4580 | 391 """Get the s-expression for the deivice. |
mjw@4580 | 392 Implement in a subclass if needed. |
mjw@2332 | 393 |
mjw@4580 | 394 @return: sxpr |
mjw@4580 | 395 """ |
mjw@4580 | 396 return self.getConfig() |
mjw@4580 | 397 |
mjw@4580 | 398 def configure(self, config, change=False): |
mjw@4580 | 399 """Reconfigure the device. |
mjw@4580 | 400 |
mjw@4580 | 401 Implement in subclass. |
mjw@2332 | 402 """ |
mjw@2332 | 403 raise NotImplementedError() |
mjw@2332 | 404 |
mjw@4580 | 405 def refresh(self): |
mjw@4580 | 406 """Refresh the device.. |
mjw@4580 | 407 Default no-op. Define in subclass if needed. |
mjw@2520 | 408 """ |
mjw@4580 | 409 pass |
mjw@2185 | 410 |
mjw@4580 | 411 def destroy(self, change=False, reboot=False): |
mjw@4580 | 412 """Destroy the device. |
mjw@4580 | 413 If change is True notify destruction (runtime change). |
mjw@4580 | 414 If reboot is True the device is being destroyed for a reboot. |
mjw@4580 | 415 Redefine in subclass if needed. |
mjw@4668 | 416 |
mjw@4668 | 417 Called at domain shutdown and when a device is deleted from |
mjw@4668 | 418 a running domain (with change True). |
mjw@4580 | 419 """ |
mjw@4580 | 420 self.destroyed = True |
mjw@4580 | 421 pass |
mjw@1654 | 422 |
mjw@4580 | 423 #---------------------------------------------------------------------------- |