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 #----------------------------------------------------------------------------
|