xen-vtx-unstable

annotate tools/python/xen/xend/server/controller.py @ 5388:cb9679a15acd

bitkeeper revision 1.1662.1.21 (42a80585xAt7ZrRcqmCRm3HvTrL7Mg)

Many files:
Switch to xenstore for storing persistent information.
Signed-off-by: Mike Wray <mike.wray@hp.com>
Signed-off-by: Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
author cl349@firebug.cl.cam.ac.uk
date Thu Jun 09 09:01:57 2005 +0000 (2005-06-09)
parents cf712d7c809c
children 6d3e8f90c2df
rev   line source
mjw@1654 1 # Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
mjw@1719 2 """General support for controllers, which handle devices
mjw@1719 3 for a domain.
mjw@1719 4 """
mjw@1654 5
mjw@4580 6 from xen.xend.XendError import XendError
cl349@5388 7 from xen.xend.xenstore import DBVar
cl349@5336 8 from xen.xend.server.messages import msgTypeName, printMsg, getMessageType
mjw@1623 9
mjw@2397 10 DEBUG = 0
mjw@1677 11
mjw@4580 12 class CtrlMsgRcvr:
mjw@4668 13 """Utility class to dispatch messages on a control channel.
mjw@4580 14 Once I{registerChannel} has been called, our message types are registered
mjw@4580 15 with the channel. The channel will call I{requestReceived}
mjw@4580 16 when a request arrives if it has one of our message types.
mjw@4580 17
mjw@4580 18 @ivar channel: channel to a domain
mjw@4580 19 @type channel: Channel
mjw@4580 20 @ivar majorTypes: major message types we are interested in
mjw@4580 21 @type majorTypes: {int:{int:method}}
mjw@4580 22
mjw@1677 23 """
mjw@1677 24
mjw@4580 25 def __init__(self, channel):
mjw@4580 26 self.majorTypes = {}
mjw@4580 27 self.channel = channel
mjw@1677 28
mjw@4580 29 def getHandler(self, type, subtype):
mjw@2224 30 """Get the method for a type and subtype.
mjw@2224 31
mjw@2224 32 @param type: major message type
mjw@2224 33 @param subtype: minor message type
mjw@2224 34 @return: method or None
mjw@2224 35 """
mjw@2224 36 method = None
mjw@2224 37 subtypes = self.majorTypes.get(type)
mjw@2224 38 if subtypes:
mjw@2224 39 method = subtypes.get(subtype)
mjw@2224 40 return method
mjw@2224 41
mjw@4580 42 def addHandler(self, type, subtype, method):
mjw@2224 43 """Add a method to handle a message type and subtype.
mjw@2224 44
mjw@2224 45 @param type: major message type
mjw@2224 46 @param subtype: minor message type
mjw@2224 47 @param method: method
mjw@2224 48 """
mjw@2224 49 subtypes = self.majorTypes.get(type)
mjw@2224 50 if not subtypes:
mjw@2224 51 subtypes = {}
mjw@2224 52 self.majorTypes[type] = subtypes
mjw@2224 53 subtypes[subtype] = method
mjw@2224 54
mjw@2224 55 def getMajorTypes(self):
mjw@2224 56 """Get the list of major message types handled.
mjw@2224 57 """
mjw@2224 58 return self.majorTypes.keys()
mjw@2224 59
mjw@1623 60 def requestReceived(self, msg, type, subtype):
mjw@1719 61 """Dispatch a request message to handlers.
mjw@1719 62 Called by the channel for requests with one of our types.
mjw@1654 63
mjw@1719 64 @param msg: message
mjw@1719 65 @type msg: xu message
mjw@1719 66 @param type: major message type
mjw@1719 67 @type type: int
mjw@1719 68 @param subtype: minor message type
mjw@1719 69 @type subtype: int
mjw@1654 70 """
mjw@1677 71 if DEBUG:
mjw@1719 72 print 'requestReceived>',
mjw@4580 73 printMsg(msg, all=True)
mjw@2265 74 responded = 0
mjw@4580 75 method = self.getHandler(type, subtype)
mjw@1623 76 if method:
mjw@4580 77 responded = method(msg)
mjw@1677 78 elif DEBUG:
mjw@1623 79 print ('requestReceived> No handler: Message type %s %d:%d'
mjw@1623 80 % (msgTypeName(type, subtype), type, subtype)), self
mjw@2265 81 return responded
mjw@1623 82
mjw@1677 83
mjw@1623 84 def lostChannel(self):
mjw@1654 85 """Called when the channel to the domain is lost.
mjw@1654 86 """
mjw@4617 87 if DEBUG:
mjw@4617 88 print 'CtrlMsgRcvr>lostChannel>',
mjw@4580 89 self.channel = None
mjw@1623 90
mjw@1623 91 def registerChannel(self):
mjw@1654 92 """Register interest in our major message types with the
mjw@1719 93 channel to our domain. Once we have registered, the channel
mjw@4580 94 will call requestReceived for our messages.
mjw@1654 95 """
mjw@4580 96 if DEBUG:
mjw@4580 97 print 'CtrlMsgRcvr>registerChannel>', self.channel, self.getMajorTypes()
mjw@4580 98 if self.channel:
mjw@2224 99 self.channel.registerDevice(self.getMajorTypes(), self)
mjw@1623 100
mjw@1623 101 def deregisterChannel(self):
mjw@1654 102 """Deregister interest in our major message types with the
mjw@1719 103 channel to our domain. After this the channel won't call
mjw@1719 104 us any more.
mjw@1654 105 """
mjw@1623 106 if self.channel:
mjw@1623 107 self.channel.deregisterDevice(self)
mjw@1623 108
mjw@4580 109 class DevControllerTable:
mjw@4668 110 """Table of device controller classes, indexed by type name.
mjw@2332 111 """
mjw@2332 112
mjw@2332 113 def __init__(self):
mjw@4668 114 self.controllerClasses = {}
mjw@4580 115
mjw@4668 116 def getDevControllerClass(self, type):
mjw@4668 117 return self.controllerClasses.get(type)
mjw@4580 118
cl349@5346 119 def addDevControllerClass(self, cls):
cl349@5346 120 self.controllerClasses[cls.getType()] = cls
mjw@2332 121
mjw@4668 122 def delDevControllerClass(self, type):
mjw@4668 123 if type in self.controllerClasses:
mjw@4668 124 del self.controllerClasses[type]
mjw@2332 125
mjw@4580 126 def createDevController(self, type, vm, recreate=False):
cl349@5346 127 cls = self.getDevControllerClass(type)
cl349@5346 128 if not cls:
mjw@4580 129 raise XendError("unknown device type: " + type)
cl349@5346 130 return cls.createDevController(vm, recreate=recreate)
mjw@2332 131
mjw@4580 132 def getDevControllerTable():
mjw@4668 133 """Singleton constructor for the controller table.
mjw@4668 134 """
mjw@4580 135 global devControllerTable
mjw@4580 136 try:
mjw@4580 137 devControllerTable
mjw@4580 138 except:
mjw@4580 139 devControllerTable = DevControllerTable()
mjw@4580 140 return devControllerTable
mjw@1623 141
cl349@5346 142 def addDevControllerClass(name, cls):
mjw@4668 143 """Add a device controller class to the controller table.
mjw@4668 144 """
cl349@5346 145 cls.type = name
cl349@5346 146 getDevControllerTable().addDevControllerClass(cls)
mjw@4580 147
mjw@4580 148 def createDevController(name, vm, recreate=False):
mjw@4580 149 return getDevControllerTable().createDevController(name, vm, recreate=recreate)
mjw@4580 150
mjw@4580 151 class DevController:
mjw@4580 152 """Abstract class for a device controller attached to a domain.
mjw@4580 153 A device controller manages all the devices of a given type for a domain.
mjw@4580 154 There is exactly one device controller for each device type for
mjw@4580 155 a domain.
mjw@2332 156
mjw@4580 157 """
mjw@4580 158
cl349@5388 159 # State:
cl349@5388 160 # controller/<type> : for controller
cl349@5388 161 # device/<type>/<id> : for each device
cl349@5388 162
cl349@5346 163 def createDevController(cls, vm, recreate=False):
mjw@4668 164 """Class method to create a dev controller.
mjw@4668 165 """
cl349@5346 166 ctrl = cls(vm, recreate=recreate)
mjw@4668 167 ctrl.initController(recreate=recreate)
cl349@5388 168 ctrl.exportToDB()
mjw@4668 169 return ctrl
mjw@4668 170
mjw@4668 171 createDevController = classmethod(createDevController)
mjw@4668 172
cl349@5346 173 def getType(cls):
cl349@5346 174 return cls.type
mjw@4668 175
mjw@4668 176 getType = classmethod(getType)
mjw@4668 177
cl349@5388 178 __exports__ = [
cl349@5388 179 DBVar('type', 'str'),
cl349@5388 180 DBVar('destroyed', 'bool'),
cl349@5388 181 ]
cl349@5388 182
cl349@5346 183 # Set when registered.
cl349@5346 184 type = None
cl349@5346 185
mjw@4668 186 def __init__(self, vm, recreate=False):
mjw@4580 187 self.destroyed = False
mjw@4580 188 self.vm = vm
cl349@5388 189 self.db = self.getDB()
mjw@4580 190 self.deviceId = 0
mjw@4580 191 self.devices = {}
mjw@4580 192 self.device_order = []
mjw@2332 193
cl349@5388 194 def getDB(self):
cl349@5388 195 """Get the db node to use for a controller.
cl349@5388 196 """
cl349@5388 197 return self.vm.db.addChild("/controller/%s" % self.getType())
cl349@5388 198
cl349@5388 199 def getDevDB(self, id):
cl349@5388 200 """Get the db node to use for a device.
cl349@5388 201 """
cl349@5388 202 return self.vm.db.addChild("/device/%s/%s" % (self.getType(), id))
cl349@5388 203
cl349@5388 204 def exportToDB(self, save=False):
cl349@5388 205 self.db.exportToDB(self, fields=self.__exports__, save=save)
cl349@5388 206
cl349@5388 207 def importFromDB(self):
cl349@5388 208 self.db.importFromDB(self, fields=self.__exports__)
cl349@5388 209
mjw@4580 210 def getDevControllerType(self):
mjw@4580 211 return self.dctype
mjw@2332 212
mjw@4580 213 def getDomain(self):
mjw@4580 214 return self.vm.getDomain()
mjw@4580 215
mjw@4580 216 def getDomainName(self):
mjw@4580 217 return self.vm.getName()
mjw@2332 218
mjw@4580 219 def getChannel(self):
mjw@4580 220 chan = self.vm.getChannel()
mjw@4580 221 return chan
mjw@4580 222
mjw@4580 223 def getDomainInfo(self):
mjw@4580 224 return self.vm
mjw@2332 225
mjw@4580 226 #----------------------------------------------------------------------------
mjw@4580 227 # Subclass interface.
mjw@4580 228 # Subclasses should define the unimplemented methods..
mjw@4580 229 # Redefinitions must have the same arguments.
mjw@2332 230
mjw@4580 231 def initController(self, recreate=False, reboot=False):
mjw@4668 232 """Initialise the controller. Called when the controller is
mjw@4668 233 first created, and again after the domain is rebooted (with reboot True).
mjw@4668 234 If called with recreate True (and reboot False) the controller is being
mjw@4668 235 recreated after a xend restart.
mjw@4668 236
mjw@4668 237 As this can be a re-init (after reboot) any controller state should
mjw@4668 238 be reset. For example the destroyed flag.
mjw@4668 239 """
mjw@4580 240 self.destroyed = False
mjw@4580 241 if reboot:
mjw@4580 242 self.rebootDevices()
mjw@4580 243
mjw@4580 244 def newDevice(self, id, config, recreate=False):
mjw@4580 245 """Create a device with the given config.
mjw@4580 246 Must be defined in subclass.
mjw@4668 247 Called with recreate True when the device is being recreated after a
mjw@4668 248 xend restart.
mjw@4580 249
mjw@4580 250 @return device
mjw@2332 251 """
mjw@2332 252 raise NotImplementedError()
mjw@2332 253
mjw@4580 254 def createDevice(self, config, recreate=False, change=False):
mjw@4668 255 """Create a device and attach to its front- and back-ends.
mjw@4668 256 If recreate is true the device is being recreated after a xend restart.
mjw@4668 257 If change is true the device is a change to an existing domain,
mjw@4668 258 i.e. it is being added at runtime rather than when the domain is created.
mjw@4668 259 """
cl349@5388 260 dev = self.newDevice(self.nextDeviceId(), config, recreate=recreate)
cl349@5388 261 if self.vm.recreate:
cl349@5388 262 dev.importFromDB()
mjw@4580 263 dev.init(recreate=recreate)
mjw@4580 264 self.addDevice(dev)
cl349@5388 265 if not recreate:
cl349@5388 266 dev.exportToDB()
mjw@4580 267 dev.attach(recreate=recreate, change=change)
cl349@5388 268 dev.exportToDB()
mjw@4580 269
mjw@4580 270 def configureDevice(self, id, config, change=False):
mjw@4580 271 """Reconfigure an existing device.
mjw@4580 272 May be defined in subclass."""
cl349@5338 273 dev = self.getDevice(id, error=True)
mjw@4580 274 dev.configure(config, change=change)
mjw@2267 275
mjw@4580 276 def destroyDevice(self, id, change=False, reboot=False):
mjw@4580 277 """Destroy a device.
mjw@4668 278 May be defined in subclass.
mjw@4668 279
mjw@4668 280 If reboot is true the device is being destroyed for a domain reboot.
mjw@4668 281
mjw@4668 282 The device is not deleted, since it may be recreated later.
mjw@4668 283 """
cl349@5338 284 dev = self.getDevice(id, error=True)
mjw@4580 285 dev.destroy(change=change, reboot=reboot)
mjw@4580 286 return dev
mjw@4580 287
mjw@4580 288 def deleteDevice(self, id, change=True):
mjw@4668 289 """Destroy a device and delete it.
mjw@4668 290 Normally called to remove a device from a domain at runtime.
mjw@4668 291 """
mjw@4580 292 dev = self.destroyDevice(id, change=change)
mjw@4580 293 self.removeDevice(dev)
mjw@4580 294
mjw@4580 295 def destroyController(self, reboot=False):
mjw@4580 296 """Destroy all devices and clean up.
mjw@4668 297 May be defined in subclass.
mjw@4668 298 If reboot is true the controller is being destroyed for a domain reboot.
mjw@4668 299 Called at domain shutdown.
mjw@4668 300 """
mjw@4580 301 self.destroyed = True
mjw@4580 302 self.destroyDevices(reboot=reboot)
mjw@4580 303
mjw@4580 304 #----------------------------------------------------------------------------
mjw@4580 305
mjw@4580 306 def isDestroyed(self):
mjw@4580 307 return self.destroyed
mjw@4580 308
cl349@5338 309 def getDevice(self, id, error=False):
cl349@5338 310 dev = self.devices.get(id)
cl349@5338 311 if error and not dev:
cl349@5338 312 raise XendError("invalid device id: " + id)
cl349@5338 313 return dev
mjw@4580 314
mjw@4580 315 def getDeviceIds(self):
mjw@4580 316 return [ dev.getId() for dev in self.device_order ]
mjw@4580 317
mjw@4580 318 def getDevices(self):
mjw@4580 319 return self.device_order
mjw@4580 320
mjw@4580 321 def getDeviceConfig(self, id):
mjw@4580 322 return self.getDevice(id).getConfig()
mjw@4580 323
mjw@4580 324 def getDeviceConfigs(self):
mjw@4580 325 return [ dev.getConfig() for dev in self.device_order ]
mjw@4580 326
mjw@4580 327 def getDeviceSxprs(self):
mjw@4580 328 return [ dev.sxpr() for dev in self.device_order ]
mjw@4580 329
mjw@4580 330 def addDevice(self, dev):
mjw@4580 331 self.devices[dev.getId()] = dev
mjw@4580 332 self.device_order.append(dev)
mjw@4580 333 return dev
mjw@4580 334
mjw@4580 335 def removeDevice(self, dev):
mjw@4580 336 if dev.getId() in self.devices:
mjw@4580 337 del self.devices[dev.getId()]
mjw@4580 338 if dev in self.device_order:
mjw@4580 339 self.device_order.remove(dev)
mjw@4580 340
mjw@4580 341 def rebootDevices(self):
mjw@4580 342 for dev in self.getDevices():
mjw@4580 343 dev.reboot()
mjw@4580 344
mjw@4580 345 def destroyDevices(self, reboot=False):
mjw@4580 346 """Destroy all devices.
mjw@4580 347 """
mjw@4580 348 for dev in self.getDevices():
mjw@4580 349 dev.destroy(reboot=reboot)
mjw@2225 350
mjw@4580 351 def getMaxDeviceId(self):
mjw@4580 352 maxid = 0
mjw@4580 353 for id in self.devices:
mjw@4580 354 if id > maxid:
mjw@4580 355 maxid = id
mjw@4580 356 return maxid
mjw@4580 357
mjw@4580 358 def nextDeviceId(self):
mjw@4580 359 id = self.deviceId
mjw@4580 360 self.deviceId += 1
mjw@4580 361 return id
mjw@4580 362
mjw@4580 363 def getDeviceCount(self):
mjw@4580 364 return len(self.devices)
mjw@4580 365
mjw@4580 366 class Dev:
mjw@4580 367 """Abstract class for a device attached to a device controller.
mjw@2332 368
mjw@4580 369 @ivar id: identifier
mjw@4580 370 @type id: int
mjw@4580 371 @ivar controller: device controller
mjw@4580 372 @type controller: DevController
mjw@4580 373 """
mjw@4580 374
cl349@5388 375 # ./status : need 2: actual and requested?
cl349@5388 376 # down-down: initial.
cl349@5388 377 # up-up: fully up.
cl349@5388 378 # down-up: down requested, still up. Watch front and back, when both
cl349@5388 379 # down go to down-down. But what if one (or both) is not connected?
cl349@5388 380 # Still have front/back trees with status? Watch front/status, back/status?
cl349@5388 381 # up-down: up requested, still down.
cl349@5388 382 # Back-end watches ./status, front/status
cl349@5388 383 # Front-end watches ./status, back/status
cl349@5388 384 # i.e. each watches the other 2.
cl349@5388 385 # Each is status/request status/actual?
cl349@5388 386 #
cl349@5388 387 # backend?
cl349@5388 388 # frontend?
cl349@5388 389
cl349@5388 390 __exports__ = [
cl349@5388 391 DBVar('id', ty='int'),
cl349@5388 392 DBVar('type', ty='str'),
cl349@5388 393 DBVar('config', ty='sxpr'),
cl349@5388 394 DBVar('destroyed', ty='bool'),
cl349@5388 395 ]
cl349@5388 396
mjw@4580 397 def __init__(self, controller, id, config, recreate=False):
mjw@4580 398 self.controller = controller
mjw@4580 399 self.id = id
mjw@4580 400 self.config = config
mjw@4580 401 self.destroyed = False
cl349@5388 402 self.type = self.getType()
cl349@5388 403
cl349@5388 404 self.db = controller.getDevDB(id)
cl349@5388 405
cl349@5388 406 def exportToDB(self, save=False):
cl349@5388 407 self.db.exportToDB(self, fields=self.__exports__, save=save)
cl349@5388 408
cl349@5388 409 def importFromDB(self):
cl349@5388 410 self.db.importFromDB(self, fields=self.__exports__)
mjw@4580 411
mjw@4580 412 def getDomain(self):
mjw@4580 413 return self.controller.getDomain()
mjw@4580 414
mjw@4580 415 def getDomainName(self):
mjw@4580 416 return self.controller.getDomainName()
mjw@4580 417
mjw@4580 418 def getChannel(self):
mjw@4580 419 return self.controller.getChannel()
mjw@4580 420
mjw@4580 421 def getDomainInfo(self):
mjw@4580 422 return self.controller.getDomainInfo()
mjw@4580 423
mjw@4580 424 def getController(self):
mjw@4580 425 return self.controller
mjw@4580 426
mjw@4580 427 def getType(self):
mjw@4580 428 return self.controller.getType()
mjw@2332 429
mjw@4580 430 def getId(self):
mjw@4580 431 return self.id
mjw@4580 432
mjw@4580 433 def getConfig(self):
mjw@4580 434 return self.config
mjw@4580 435
mjw@4580 436 def isDestroyed(self):
mjw@4580 437 return self.destroyed
mjw@4580 438
mjw@4580 439 #----------------------------------------------------------------------------
mjw@4580 440 # Subclass interface.
mjw@4580 441 # Define methods in subclass as needed.
mjw@4580 442 # Redefinitions must have the same arguments.
mjw@4580 443
mjw@4580 444 def init(self, recreate=False, reboot=False):
mjw@4580 445 """Initialization. Called on initial create (when reboot is False)
mjw@4580 446 and on reboot (when reboot is True). When xend is restarting is
mjw@4580 447 called with recreate True. Define in subclass if needed.
mjw@4668 448
mjw@4668 449 Device instance variables must be defined in the class constructor,
mjw@4668 450 but given null or default values. The real values should be initialised
mjw@4668 451 in this method. This allows devices to be re-initialised.
mjw@4668 452
mjw@4668 453 Since this can be called to re-initialise a device any state flags
mjw@4668 454 should be reset.
mjw@4580 455 """
mjw@4580 456 self.destroyed = False
mjw@2332 457
mjw@4580 458 def attach(self, recreate=False, change=False):
mjw@4580 459 """Attach the device to its front and back ends.
mjw@4580 460 Define in subclass if needed.
mjw@4580 461 """
mjw@4580 462 pass
mjw@4580 463
mjw@4580 464 def reboot(self):
mjw@4668 465 """Reconnect the device when the domain is rebooted.
mjw@4580 466 """
mjw@4580 467 self.init(reboot=True)
mjw@4580 468 self.attach()
mjw@2332 469
mjw@4580 470 def sxpr(self):
mjw@4580 471 """Get the s-expression for the deivice.
mjw@4580 472 Implement in a subclass if needed.
mjw@2332 473
mjw@4580 474 @return: sxpr
mjw@4580 475 """
mjw@4580 476 return self.getConfig()
mjw@4580 477
mjw@4580 478 def configure(self, config, change=False):
mjw@4580 479 """Reconfigure the device.
mjw@4580 480
mjw@4580 481 Implement in subclass.
mjw@2332 482 """
mjw@2332 483 raise NotImplementedError()
mjw@2332 484
mjw@4580 485 def refresh(self):
mjw@4580 486 """Refresh the device..
mjw@4580 487 Default no-op. Define in subclass if needed.
mjw@2520 488 """
mjw@4580 489 pass
mjw@2185 490
mjw@4580 491 def destroy(self, change=False, reboot=False):
mjw@4580 492 """Destroy the device.
mjw@4580 493 If change is True notify destruction (runtime change).
mjw@4580 494 If reboot is True the device is being destroyed for a reboot.
mjw@4580 495 Redefine in subclass if needed.
mjw@4668 496
mjw@4668 497 Called at domain shutdown and when a device is deleted from
mjw@4668 498 a running domain (with change True).
mjw@4580 499 """
mjw@4580 500 self.destroyed = True
mjw@4580 501 pass
mjw@1654 502
mjw@4580 503 #----------------------------------------------------------------------------