xen-vtx-unstable

annotate tools/python/xen/xend/server/controller.py @ 6131:f056b0cc171e

xm block-create doesn't work. It seems like this command hasn't even
been tested (perhaps since the un-Twisting?). This particular problem
was that one function was being called with self instead of the right
argument and another function's return value was being used when it
didn't actually return anything.

This patch also improves the error handling a bit by making sure we
don't thrown an exception on a log statement with a None value. In
general, one should always use the % formatter instead of concatination
for strings in Python (even though this is not what this patch does).

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