xen-vtx-unstable

annotate tools/python/xen/xend/server/controller.py @ 6771:5aa6a2eff69f

Added isDevControllerClass so that XendDomainInfo does not need to store the same information internally. This may soon go, depending on how useful controller.py turns out to be.
author ewan@linford.intra
date Tue Sep 13 14:49:37 2005 +0100 (2005-09-13)
parents 7d0fb56b4a91
children 4d899a738d59 e7c7196fa329
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 #----------------------------------------------------------------------------