xen-vtx-unstable

view tools/python/xen/xend/server/controller.py @ 6568:dd668f7527cb

merge?
author cl349@firebug.cl.cam.ac.uk
date Thu Sep 01 10:16:14 2005 +0000 (2005-09-01)
parents 38312fe7ec38 f0dc15fd3c1b
children 0e2b1e04d4cb 7d0fb56b4a91
line source
1 #============================================================================
2 # This library is free software; you can redistribute it and/or
3 # modify it under the terms of version 2.1 of the GNU Lesser General Public
4 # License as published by the Free Software Foundation.
5 #
6 # This library is distributed in the hope that it will be useful,
7 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 # Lesser General Public License for more details.
10 #
11 # You should have received a copy of the GNU Lesser General Public
12 # License along with this library; if not, write to the Free Software
13 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14 #============================================================================
15 # Copyright (C) 2004, 2005 Mike Wray <mike.wray@hp.com>
16 #============================================================================
18 """General support for controllers, which handle devices
19 for a domain.
20 """
22 from xen.xend.XendError import XendError
23 from xen.xend.xenstore import DBVar
24 from xen.xend.server.messages import msgTypeName, printMsg, getMessageType
26 DEBUG = 0
28 class CtrlMsgRcvr:
29 """Utility class to dispatch messages on a control channel.
30 Once I{registerChannel} has been called, our message types are registered
31 with the channel. The channel will call I{requestReceived}
32 when a request arrives if it has one of our message types.
34 @ivar channel: channel to a domain
35 @type channel: Channel
36 @ivar majorTypes: major message types we are interested in
37 @type majorTypes: {int:{int:method}}
39 """
41 def __init__(self, channel):
42 self.majorTypes = {}
43 self.channel = channel
45 def getHandler(self, type, subtype):
46 """Get the method for a type and subtype.
48 @param type: major message type
49 @param subtype: minor message type
50 @return: method or None
51 """
52 method = None
53 subtypes = self.majorTypes.get(type)
54 if subtypes:
55 method = subtypes.get(subtype)
56 return method
58 def addHandler(self, type, subtype, method):
59 """Add a method to handle a message type and subtype.
61 @param type: major message type
62 @param subtype: minor message type
63 @param method: method
64 """
65 subtypes = self.majorTypes.get(type)
66 if not subtypes:
67 subtypes = {}
68 self.majorTypes[type] = subtypes
69 subtypes[subtype] = method
71 def getMajorTypes(self):
72 """Get the list of major message types handled.
73 """
74 return self.majorTypes.keys()
76 def requestReceived(self, msg, type, subtype):
77 """Dispatch a request message to handlers.
78 Called by the channel for requests with one of our types.
80 @param msg: message
81 @type msg: xu message
82 @param type: major message type
83 @type type: int
84 @param subtype: minor message type
85 @type subtype: int
86 """
87 if DEBUG:
88 print 'requestReceived>',
89 printMsg(msg, all=True)
90 responded = 0
91 method = self.getHandler(type, subtype)
92 if method:
93 responded = method(msg)
94 elif DEBUG:
95 print ('requestReceived> No handler: Message type %s %d:%d'
96 % (msgTypeName(type, subtype), type, subtype)), self
97 return responded
100 def lostChannel(self):
101 """Called when the channel to the domain is lost.
102 """
103 if DEBUG:
104 print 'CtrlMsgRcvr>lostChannel>',
105 self.channel = None
107 def registerChannel(self):
108 """Register interest in our major message types with the
109 channel to our domain. Once we have registered, the channel
110 will call requestReceived for our messages.
111 """
112 if DEBUG:
113 print 'CtrlMsgRcvr>registerChannel>', self.channel, self.getMajorTypes()
114 if self.channel:
115 self.channel.registerDevice(self.getMajorTypes(), self)
117 def deregisterChannel(self):
118 """Deregister interest in our major message types with the
119 channel to our domain. After this the channel won't call
120 us any more.
121 """
122 if self.channel:
123 self.channel.deregisterDevice(self)
125 class DevControllerTable:
126 """Table of device controller classes, indexed by type name.
127 """
129 def __init__(self):
130 self.controllerClasses = {}
132 def getDevControllerClass(self, type):
133 return self.controllerClasses.get(type)
135 def addDevControllerClass(self, cls):
136 self.controllerClasses[cls.getType()] = cls
138 def delDevControllerClass(self, type):
139 if type in self.controllerClasses:
140 del self.controllerClasses[type]
142 def createDevController(self, type, vm, recreate=False):
143 cls = self.getDevControllerClass(type)
144 if not cls:
145 raise XendError("unknown device type: " + str(type))
146 return cls.createDevController(vm, recreate=recreate)
148 def getDevControllerTable():
149 """Singleton constructor for the controller table.
150 """
151 global devControllerTable
152 try:
153 devControllerTable
154 except:
155 devControllerTable = DevControllerTable()
156 return devControllerTable
158 def addDevControllerClass(name, cls):
159 """Add a device controller class to the controller table.
160 """
161 cls.type = name
162 getDevControllerTable().addDevControllerClass(cls)
164 def createDevController(name, vm, recreate=False):
165 return getDevControllerTable().createDevController(name, vm, recreate=recreate)
167 class DevController:
168 """Abstract class for a device controller attached to a domain.
169 A device controller manages all the devices of a given type for a domain.
170 There is exactly one device controller for each device type for
171 a domain.
173 """
175 # State:
176 # controller/<type> : for controller
177 # device/<type>/<id> : for each device
179 def createDevController(cls, vm, recreate=False):
180 """Class method to create a dev controller.
181 """
182 ctrl = cls(vm, recreate=recreate)
183 ctrl.initController(recreate=recreate)
184 ctrl.exportToDB()
185 return ctrl
187 createDevController = classmethod(createDevController)
189 def getType(cls):
190 return cls.type
192 getType = classmethod(getType)
194 __exports__ = [
195 DBVar('type', 'str'),
196 DBVar('destroyed', 'bool'),
197 ]
199 # Set when registered.
200 type = None
202 def __init__(self, vm, recreate=False):
203 self.destroyed = False
204 self.vm = vm
205 self.db = self.getDB()
206 self.deviceId = 0
207 self.devices = {}
208 self.device_order = []
210 def getDB(self):
211 """Get the db node to use for a controller.
212 """
213 return self.vm.db.addChild("/controller/%s" % self.getType())
215 def getDevDB(self, id):
216 """Get the db node to use for a device.
217 """
218 return self.vm.db.addChild("/device/%s/%s" % (self.getType(), id))
220 def exportToDB(self, save=False):
221 self.db.exportToDB(self, fields=self.__exports__, save=save)
223 def importFromDB(self):
224 self.db.importFromDB(self, fields=self.__exports__)
226 def getDevControllerType(self):
227 return self.dctype
229 def getDomain(self):
230 return self.vm.getDomain()
232 def getDomainName(self):
233 return self.vm.getName()
235 def getChannel(self):
236 chan = self.vm.getChannel()
237 return chan
239 def getDomainInfo(self):
240 return self.vm
242 #----------------------------------------------------------------------------
243 # Subclass interface.
244 # Subclasses should define the unimplemented methods..
245 # Redefinitions must have the same arguments.
247 def initController(self, recreate=False, reboot=False):
248 """Initialise the controller. Called when the controller is
249 first created, and again after the domain is rebooted (with reboot True).
250 If called with recreate True (and reboot False) the controller is being
251 recreated after a xend restart.
253 As this can be a re-init (after reboot) any controller state should
254 be reset. For example the destroyed flag.
255 """
256 self.destroyed = False
257 if reboot:
258 self.rebootDevices()
260 def newDevice(self, id, config, recreate=False):
261 """Create a device with the given config.
262 Must be defined in subclass.
263 Called with recreate True when the device is being recreated after a
264 xend restart.
266 @return device
267 """
268 raise NotImplementedError()
270 def createDevice(self, config, recreate=False, change=False):
271 """Create a device and attach to its front- and back-ends.
272 If recreate is true the device is being recreated after a xend restart.
273 If change is true the device is a change to an existing domain,
274 i.e. it is being added at runtime rather than when the domain is created.
275 """
276 dev = self.newDevice(self.nextDeviceId(), config, recreate=recreate)
277 if self.vm.recreate:
278 dev.importFromDB()
279 dev.init(recreate=recreate)
280 self.addDevice(dev)
281 if not recreate:
282 dev.exportToDB()
283 dev.attach(recreate=recreate, change=change)
284 dev.exportToDB()
286 return dev
288 def configureDevice(self, id, config, change=False):
289 """Reconfigure an existing device.
290 May be defined in subclass."""
291 dev = self.getDevice(id, error=True)
292 dev.configure(config, change=change)
294 def destroyDevice(self, id, change=False, reboot=False):
295 """Destroy a device.
296 May be defined in subclass.
298 If reboot is true the device is being destroyed for a domain reboot.
300 The device is not deleted, since it may be recreated later.
301 """
302 dev = self.getDevice(id, error=True)
303 dev.destroy(change=change, reboot=reboot)
304 return dev
306 def deleteDevice(self, id, change=True):
307 """Destroy a device and delete it.
308 Normally called to remove a device from a domain at runtime.
309 """
310 dev = self.destroyDevice(id, change=change)
311 self.removeDevice(dev)
313 def destroyController(self, reboot=False):
314 """Destroy all devices and clean up.
315 May be defined in subclass.
316 If reboot is true the controller is being destroyed for a domain reboot.
317 Called at domain shutdown.
318 """
319 self.destroyed = True
320 self.destroyDevices(reboot=reboot)
322 #----------------------------------------------------------------------------
324 def isDestroyed(self):
325 return self.destroyed
327 def getDevice(self, id, error=False):
328 dev = self.devices.get(int(id))
329 if error and not dev:
330 raise XendError("invalid device id: " + str(id))
331 return dev
333 def getDeviceIds(self):
334 return [ dev.getId() for dev in self.device_order ]
336 def getDevices(self):
337 return self.device_order
339 def getDeviceConfig(self, id):
340 return self.getDevice(id).getConfig()
342 def getDeviceConfigs(self):
343 return [ dev.getConfig() for dev in self.device_order ]
345 def getDeviceSxprs(self):
346 return [ dev.sxpr() for dev in self.device_order ]
348 def addDevice(self, dev):
349 self.devices[dev.getId()] = dev
350 self.device_order.append(dev)
351 return dev
353 def removeDevice(self, dev):
354 if dev.getId() in self.devices:
355 del self.devices[dev.getId()]
356 if dev in self.device_order:
357 self.device_order.remove(dev)
359 def rebootDevices(self):
360 for dev in self.getDevices():
361 dev.reboot()
363 def destroyDevices(self, reboot=False):
364 """Destroy all devices.
365 """
366 for dev in self.getDevices():
367 dev.destroy(reboot=reboot)
369 def getMaxDeviceId(self):
370 maxid = 0
371 for id in self.devices:
372 if id > maxid:
373 maxid = id
374 return maxid
376 def nextDeviceId(self):
377 id = self.deviceId
378 self.deviceId += 1
379 return id
381 def getDeviceCount(self):
382 return len(self.devices)
384 class Dev:
385 """Abstract class for a device attached to a device controller.
387 @ivar id: identifier
388 @type id: int
389 @ivar controller: device controller
390 @type controller: DevController
391 """
393 # ./status : need 2: actual and requested?
394 # down-down: initial.
395 # up-up: fully up.
396 # down-up: down requested, still up. Watch front and back, when both
397 # down go to down-down. But what if one (or both) is not connected?
398 # Still have front/back trees with status? Watch front/status, back/status?
399 # up-down: up requested, still down.
400 # Back-end watches ./status, front/status
401 # Front-end watches ./status, back/status
402 # i.e. each watches the other 2.
403 # Each is status/request status/actual?
404 #
405 # backend?
406 # frontend?
408 __exports__ = [
409 DBVar('id', ty='int'),
410 DBVar('type', ty='str'),
411 DBVar('config', ty='sxpr'),
412 DBVar('destroyed', ty='bool'),
413 ]
415 def __init__(self, controller, id, config, recreate=False):
416 self.controller = controller
417 self.id = id
418 self.config = config
419 self.destroyed = False
420 self.type = self.getType()
422 self.db = controller.getDevDB(id)
424 def exportToDB(self, save=False):
425 self.db.exportToDB(self, fields=self.__exports__, save=save)
427 def importFromDB(self):
428 self.db.importFromDB(self, fields=self.__exports__)
430 def getDomain(self):
431 return self.controller.getDomain()
433 def getDomainName(self):
434 return self.controller.getDomainName()
436 def getChannel(self):
437 return self.controller.getChannel()
439 def getDomainInfo(self):
440 return self.controller.getDomainInfo()
442 def getController(self):
443 return self.controller
445 def getType(self):
446 return self.controller.getType()
448 def getId(self):
449 return self.id
451 def getConfig(self):
452 return self.config
454 def isDestroyed(self):
455 return self.destroyed
457 #----------------------------------------------------------------------------
458 # Subclass interface.
459 # Define methods in subclass as needed.
460 # Redefinitions must have the same arguments.
462 def init(self, recreate=False, reboot=False):
463 """Initialization. Called on initial create (when reboot is False)
464 and on reboot (when reboot is True). When xend is restarting is
465 called with recreate True. Define in subclass if needed.
467 Device instance variables must be defined in the class constructor,
468 but given null or default values. The real values should be initialised
469 in this method. This allows devices to be re-initialised.
471 Since this can be called to re-initialise a device any state flags
472 should be reset.
473 """
474 self.destroyed = False
476 def attach(self, recreate=False, change=False):
477 """Attach the device to its front and back ends.
478 Define in subclass if needed.
479 """
480 pass
482 def reboot(self):
483 """Reconnect the device when the domain is rebooted.
484 """
485 self.init(reboot=True)
486 self.attach()
488 def sxpr(self):
489 """Get the s-expression for the deivice.
490 Implement in a subclass if needed.
492 @return: sxpr
493 """
494 return self.getConfig()
496 def configure(self, config, change=False):
497 """Reconfigure the device.
499 Implement in subclass.
500 """
501 raise NotImplementedError()
503 def refresh(self):
504 """Refresh the device..
505 Default no-op. Define in subclass if needed.
506 """
507 pass
509 def destroy(self, change=False, reboot=False):
510 """Destroy the device.
511 If change is True notify destruction (runtime change).
512 If reboot is True the device is being destroyed for a reboot.
513 Redefine in subclass if needed.
515 Called at domain shutdown and when a device is deleted from
516 a running domain (with change True).
517 """
518 self.destroyed = True
519 pass
521 #----------------------------------------------------------------------------