xen-vtx-unstable
annotate tools/python/xen/xend/server/controller.py @ 2185:6c272033a2ed
bitkeeper revision 1.1159.1.36 (4119f27eonzahatd09ja80xf3ifOFw)
Documentation and some debug changes.
Documentation and some debug changes.
author | mjw@wray-m-3.hpl.hp.com |
---|---|
date | Wed Aug 11 10:18:38 2004 +0000 (2004-08-11) |
parents | dae98734f12e |
children | 6c85a89e99e3 |
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@1623 | 6 from twisted.internet import defer |
mjw@1721 | 7 #defer.Deferred.debug = 1 |
mjw@1623 | 8 |
mjw@1623 | 9 import channel |
mjw@1719 | 10 from messages import msgTypeName, printMsg |
mjw@1623 | 11 |
mjw@1719 | 12 DEBUG = 0 |
mjw@1677 | 13 |
mjw@1677 | 14 class Responder: |
mjw@1719 | 15 """Handler for a response to a message with a specified id. |
mjw@1677 | 16 """ |
mjw@1677 | 17 |
mjw@1677 | 18 def __init__(self, mid, deferred): |
mjw@1677 | 19 """Create a responder. |
mjw@1677 | 20 |
mjw@1719 | 21 @param mid: message id of response to handle |
mjw@1719 | 22 @type mid: int |
mjw@1719 | 23 @param deferred: deferred object holding the callbacks |
mjw@1719 | 24 @type deferred: Deferred |
mjw@1677 | 25 """ |
mjw@1677 | 26 self.mid = mid |
mjw@1677 | 27 self.deferred = deferred |
mjw@1677 | 28 |
mjw@1677 | 29 def responseReceived(self, msg): |
mjw@1719 | 30 """Entry point called when a response message with the right id arrives. |
mjw@1719 | 31 Calls callback on I{self.deferred} with the message. |
mjw@1719 | 32 |
mjw@1719 | 33 @param msg: response message |
mjw@1719 | 34 @type msg: xu message |
mjw@1719 | 35 """ |
mjw@1677 | 36 if self.deferred.called: return |
mjw@1677 | 37 self.deferred.callback(msg) |
mjw@1677 | 38 |
mjw@1677 | 39 def error(self, err): |
mjw@1719 | 40 """Entry point called when there has been an error. |
mjw@1719 | 41 Calls errback on I{self.deferred} with the error. |
mjw@1719 | 42 |
mjw@1719 | 43 @param err: error |
mjw@1719 | 44 @type err: Exception |
mjw@1719 | 45 """ |
mjw@1677 | 46 if self.deferred.called: return |
mjw@1677 | 47 self.deferred.errback(err) |
mjw@1677 | 48 |
mjw@1623 | 49 class CtrlMsgRcvr: |
mjw@1623 | 50 """Abstract class for things that deal with a control interface to a domain. |
mjw@1719 | 51 Once I{registerChannel} has been called, our message types are registered |
mjw@1719 | 52 with the channel to the domain. The channel will call I{requestReceived} |
mjw@1719 | 53 when a request arrives, or I{responseReceived} when a response arrives, |
mjw@1719 | 54 if they have one of our message types. |
mjw@1654 | 55 |
mjw@1719 | 56 @ivar dom: the domain we are a control interface for |
mjw@1719 | 57 @type dom: int |
mjw@1719 | 58 @ivar majorTypes: major message types we are interested in |
mjw@1719 | 59 @type majorTypes: [int] |
mjw@1719 | 60 @ivar subTypes: mapping of message subtypes to methods |
mjw@1719 | 61 @ivar subTypes: {int:method} |
mjw@1719 | 62 @ivar timeout: timeout (in seconds) for message handlers |
mjw@1719 | 63 @type timeout: int |
mjw@1654 | 64 |
mjw@1719 | 65 @ivar channel: channel to the domain |
mjw@1719 | 66 @type channel: Channel |
mjw@1719 | 67 @ivar idx: channel index |
mjw@1719 | 68 @ivar idx: string |
mjw@1719 | 69 @ivar responders: table of message response handlers |
mjw@1719 | 70 @type responders: {int:Responder} |
mjw@1623 | 71 """ |
mjw@1623 | 72 |
mjw@1623 | 73 def __init__(self): |
mjw@1623 | 74 self.channelFactory = channel.channelFactory() |
mjw@1623 | 75 self.majorTypes = [ ] |
mjw@1623 | 76 self.subTypes = {} |
mjw@1623 | 77 self.dom = None |
mjw@1623 | 78 self.channel = None |
mjw@1623 | 79 self.idx = None |
mjw@1728 | 80 self.responders = {} |
mjw@1677 | 81 self.timeout = 10 |
mjw@1677 | 82 |
mjw@1677 | 83 def setTimeout(self, timeout): |
mjw@1677 | 84 self.timeout = timeout |
mjw@1623 | 85 |
mjw@1623 | 86 def requestReceived(self, msg, type, subtype): |
mjw@1719 | 87 """Dispatch a request message to handlers. |
mjw@1719 | 88 Called by the channel for requests with one of our types. |
mjw@1654 | 89 |
mjw@1719 | 90 @param msg: message |
mjw@1719 | 91 @type msg: xu message |
mjw@1719 | 92 @param type: major message type |
mjw@1719 | 93 @type type: int |
mjw@1719 | 94 @param subtype: minor message type |
mjw@1719 | 95 @type subtype: int |
mjw@1654 | 96 """ |
mjw@1677 | 97 if DEBUG: |
mjw@1719 | 98 print 'requestReceived>', |
mjw@1719 | 99 printMsg(msg, all=1) |
mjw@1623 | 100 method = self.subTypes.get(subtype) |
mjw@1623 | 101 if method: |
mjw@1623 | 102 method(msg, 1) |
mjw@1677 | 103 elif DEBUG: |
mjw@1623 | 104 print ('requestReceived> No handler: Message type %s %d:%d' |
mjw@1623 | 105 % (msgTypeName(type, subtype), type, subtype)), self |
mjw@1623 | 106 |
mjw@1623 | 107 def responseReceived(self, msg, type, subtype): |
mjw@1654 | 108 """Dispatch a response to handlers. |
mjw@1719 | 109 Called by the channel for responses with one of our types. |
mjw@1719 | 110 |
mjw@1719 | 111 First looks for a message responder for the message's id. |
mjw@1719 | 112 See L{callResponders}, L{addResponder}. |
mjw@1719 | 113 If there is no responder, looks for a message handler for |
mjw@1719 | 114 the message type/subtype. |
mjw@1654 | 115 |
mjw@1719 | 116 @param msg: message |
mjw@1719 | 117 @type msg: xu message |
mjw@1719 | 118 @param type: major message type |
mjw@1719 | 119 @type type: int |
mjw@1719 | 120 @param subtype: minor message type |
mjw@1719 | 121 @type subtype: int |
mjw@1654 | 122 """ |
mjw@1677 | 123 if DEBUG: |
mjw@1719 | 124 print 'responseReceived>', |
mjw@1719 | 125 printMsg(msg, all=1) |
mjw@1677 | 126 if self.callResponders(msg): |
mjw@1677 | 127 return |
mjw@1623 | 128 method = self.subTypes.get(subtype) |
mjw@1623 | 129 if method: |
mjw@1623 | 130 method(msg, 0) |
mjw@1677 | 131 elif DEBUG: |
mjw@1623 | 132 print ('responseReceived> No handler: Message type %s %d:%d' |
mjw@1623 | 133 % (msgTypeName(type, subtype), type, subtype)), self |
mjw@1623 | 134 |
mjw@1677 | 135 def addResponder(self, mid, deferred): |
mjw@1677 | 136 """Add a responder for a message id. |
mjw@1719 | 137 The I{deferred} is called with callback(msg) when a response |
mjw@1728 | 138 with message id I{mid} arrives. |
mjw@1677 | 139 |
mjw@1719 | 140 Responders have a timeout set and I{deferred} will error |
mjw@1719 | 141 on expiry. |
mjw@1677 | 142 |
mjw@1719 | 143 @param mid: message id of response expected |
mjw@1719 | 144 @type mid: int |
mjw@1719 | 145 @param deferred: handler for the response |
mjw@1719 | 146 @type deferred: Deferred |
mjw@1719 | 147 @return: responder |
mjw@1719 | 148 @rtype: Responder |
mjw@1677 | 149 """ |
mjw@1728 | 150 resp = Responder(mid, deferred) |
mjw@1728 | 151 self.responders[resp.mid] = resp |
mjw@1677 | 152 if self.timeout > 0: |
mjw@1677 | 153 deferred.setTimeout(self.timeout) |
mjw@1677 | 154 return resp |
mjw@1677 | 155 |
mjw@1677 | 156 def callResponders(self, msg): |
mjw@1677 | 157 """Call any waiting responders for a response message. |
mjw@1719 | 158 Looks for a responder registered for the message's id. |
mjw@1719 | 159 See L{addResponder}. |
mjw@1677 | 160 |
mjw@1719 | 161 @param msg: response message |
mjw@1719 | 162 @type msg: xu message |
mjw@1719 | 163 @return: 1 if there was a responder for the message, 0 otherwise |
mjw@1719 | 164 @rtype : bool |
mjw@1677 | 165 """ |
mjw@1677 | 166 hdr = msg.get_header() |
mjw@1677 | 167 mid = hdr['id'] |
mjw@1677 | 168 handled = 0 |
mjw@1728 | 169 resp = self.responders.get(mid) |
mjw@1728 | 170 if resp: |
mjw@1728 | 171 handled = 1 |
mjw@1728 | 172 resp.responseReceived(msg) |
mjw@1728 | 173 del self.responders[mid] |
mjw@1728 | 174 # Clean up called responders. |
mjw@1728 | 175 for resp in self.responders.values(): |
mjw@1728 | 176 if resp.deferred.called: |
mjw@1728 | 177 del self.responders[resp.mid] |
mjw@1677 | 178 return handled |
mjw@1677 | 179 |
mjw@1623 | 180 def lostChannel(self): |
mjw@1654 | 181 """Called when the channel to the domain is lost. |
mjw@1654 | 182 """ |
mjw@1623 | 183 pass |
mjw@1623 | 184 |
mjw@1623 | 185 def registerChannel(self): |
mjw@1654 | 186 """Register interest in our major message types with the |
mjw@1719 | 187 channel to our domain. Once we have registered, the channel |
mjw@1719 | 188 will call requestReceived or responseReceived for our messages. |
mjw@1654 | 189 """ |
mjw@1623 | 190 self.channel = self.channelFactory.domChannel(self.dom) |
mjw@1623 | 191 self.idx = self.channel.getIndex() |
mjw@1623 | 192 if self.majorTypes: |
mjw@1623 | 193 self.channel.registerDevice(self.majorTypes, self) |
mjw@1623 | 194 |
mjw@1623 | 195 def deregisterChannel(self): |
mjw@1654 | 196 """Deregister interest in our major message types with the |
mjw@1719 | 197 channel to our domain. After this the channel won't call |
mjw@1719 | 198 us any more. |
mjw@1654 | 199 """ |
mjw@1623 | 200 if self.channel: |
mjw@1623 | 201 self.channel.deregisterDevice(self) |
mjw@1893 | 202 self.channel = None |
mjw@1623 | 203 |
mjw@1623 | 204 def produceRequests(self): |
mjw@1654 | 205 """Produce any queued requests. |
mjw@1654 | 206 |
mjw@1719 | 207 @return: number produced |
mjw@1719 | 208 @rtype: int |
mjw@1654 | 209 """ |
mjw@1623 | 210 return 0 |
mjw@1623 | 211 |
mjw@1677 | 212 def writeRequest(self, msg, response=None): |
mjw@1654 | 213 """Write a request to the channel. |
mjw@1677 | 214 |
mjw@1719 | 215 @param msg: request message |
mjw@1719 | 216 @type msg: xu message |
mjw@1719 | 217 @param response: response handler |
mjw@1719 | 218 @type response: Deferred |
mjw@1654 | 219 """ |
mjw@1623 | 220 if self.channel: |
mjw@1719 | 221 if DEBUG: |
mjw@1719 | 222 print 'CtrlMsgRcvr>writeRequest>', |
mjw@1719 | 223 printMsg(msg, all=1) |
mjw@1677 | 224 if response: |
mjw@1677 | 225 self.addResponder(msg.get_header()['id'], response) |
mjw@1623 | 226 self.channel.writeRequest(msg) |
mjw@1623 | 227 else: |
mjw@1623 | 228 print 'CtrlMsgRcvr>writeRequest>', 'no channel!', self |
mjw@1623 | 229 |
mjw@1623 | 230 def writeResponse(self, msg): |
mjw@1719 | 231 """Write a response to the channel. This acknowledges |
mjw@1719 | 232 a request message. |
mjw@1719 | 233 |
mjw@1719 | 234 @param msg: message |
mjw@1719 | 235 @type msg: xu message |
mjw@1654 | 236 """ |
mjw@1623 | 237 if self.channel: |
mjw@1719 | 238 if DEBUG: |
mjw@1719 | 239 print 'CtrlMsgRcvr>writeResponse>', |
mjw@1719 | 240 printMsg(msg, all=0) |
mjw@1623 | 241 self.channel.writeResponse(msg) |
mjw@1623 | 242 else: |
mjw@1623 | 243 print 'CtrlMsgRcvr>writeResponse>', 'no channel!', self |
mjw@1623 | 244 |
mjw@1623 | 245 class ControllerFactory(CtrlMsgRcvr): |
mjw@1719 | 246 """Abstract class for factories creating controllers for a domain. |
mjw@1623 | 247 Maintains a table of instances. |
mjw@1654 | 248 |
mjw@1719 | 249 @ivar instances: mapping of index to controller instance |
mjw@2185 | 250 @type instances: {String: Controller} |
mjw@1719 | 251 @ivar dom: domain |
mjw@1719 | 252 @type dom: int |
mjw@1623 | 253 """ |
mjw@1623 | 254 |
mjw@1623 | 255 def __init__(self): |
mjw@1623 | 256 CtrlMsgRcvr.__init__(self) |
mjw@1623 | 257 self.instances = {} |
mjw@1623 | 258 self.dom = 0 |
mjw@1623 | 259 |
mjw@1623 | 260 def addInstance(self, instance): |
mjw@1654 | 261 """Add a controller instance (under its index). |
mjw@1654 | 262 """ |
mjw@1623 | 263 self.instances[instance.idx] = instance |
mjw@1623 | 264 |
mjw@1623 | 265 def getInstance(self, idx): |
mjw@1654 | 266 """Get a controller instance from its index. |
mjw@1654 | 267 """ |
mjw@1623 | 268 return self.instances.get(idx) |
mjw@1623 | 269 |
mjw@1623 | 270 def getInstances(self): |
mjw@1654 | 271 """Get a list of all controller instances. |
mjw@1654 | 272 """ |
mjw@1623 | 273 return self.instances.values() |
mjw@1623 | 274 |
mjw@1623 | 275 def getInstanceByDom(self, dom): |
mjw@1654 | 276 """Get the controller instance for the given domain. |
mjw@2185 | 277 |
mjw@2185 | 278 @param dom: domain id |
mjw@2185 | 279 @type dom: int |
mjw@2185 | 280 @return: controller or None |
mjw@1654 | 281 """ |
mjw@1623 | 282 for inst in self.instances.values(): |
mjw@1623 | 283 if inst.dom == dom: |
mjw@1623 | 284 return inst |
mjw@1623 | 285 return None |
mjw@1623 | 286 |
mjw@1623 | 287 def delInstance(self, instance): |
mjw@2185 | 288 """Delete a controller instance from the table. |
mjw@2185 | 289 |
mjw@2185 | 290 @param instance: controller instance |
mjw@1654 | 291 """ |
mjw@1623 | 292 if instance.idx in self.instances: |
mjw@1623 | 293 del self.instances[instance.idx] |
mjw@1623 | 294 |
mjw@1623 | 295 def createInstance(self, dom, recreate=0): |
mjw@1654 | 296 """Create an instance. Define in a subclass. |
mjw@1719 | 297 |
mjw@1719 | 298 @param dom: domain |
mjw@1719 | 299 @type dom: int |
mjw@1719 | 300 @param recreate: true if the instance is being recreated (after xend restart) |
mjw@1719 | 301 @type recreate: int |
mjw@2185 | 302 @return: controller instance |
mjw@2185 | 303 @rtype: Controller (or subclass) |
mjw@1654 | 304 """ |
mjw@1623 | 305 raise NotImplementedError() |
mjw@1623 | 306 |
mjw@1623 | 307 def instanceClosed(self, instance): |
mjw@1654 | 308 """Callback called when an instance is closed (usually by the instance). |
mjw@2185 | 309 |
mjw@2185 | 310 @param instance: controller instance |
mjw@1654 | 311 """ |
mjw@1623 | 312 self.delInstance(instance) |
mjw@1623 | 313 |
mjw@1623 | 314 class Controller(CtrlMsgRcvr): |
mjw@1623 | 315 """Abstract class for a device controller attached to a domain. |
mjw@2185 | 316 |
mjw@2185 | 317 @ivar factory: controller factory |
mjw@2185 | 318 @type factory: ControllerFactory |
mjw@2185 | 319 @ivar dom: domain |
mjw@2185 | 320 @type dom: int |
mjw@2185 | 321 @ivar channel: channel to the domain |
mjw@2185 | 322 @type channel: Channel |
mjw@2185 | 323 @ivar idx: channel index |
mjw@2185 | 324 @type idx: String |
mjw@1623 | 325 """ |
mjw@1623 | 326 |
mjw@1623 | 327 def __init__(self, factory, dom): |
mjw@1623 | 328 CtrlMsgRcvr.__init__(self) |
mjw@1623 | 329 self.factory = factory |
mjw@1623 | 330 self.dom = int(dom) |
mjw@1623 | 331 self.channel = None |
mjw@1623 | 332 self.idx = None |
mjw@1623 | 333 |
mjw@1623 | 334 def close(self): |
mjw@1654 | 335 """Close the controller. |
mjw@1654 | 336 """ |
mjw@1623 | 337 self.lostChannel() |
mjw@1623 | 338 |
mjw@1623 | 339 def lostChannel(self): |
mjw@1654 | 340 """The controller channel has been lost. |
mjw@1654 | 341 """ |
mjw@1664 | 342 self.deregisterChannel() |
mjw@1623 | 343 self.factory.instanceClosed(self) |
mjw@1623 | 344 |
mjw@1623 | 345 class Dev: |
mjw@1654 | 346 """Abstract class for a device attached to a device controller. |
mjw@2185 | 347 |
mjw@2185 | 348 @ivar idx: identifier |
mjw@2185 | 349 @type idx: String |
mjw@2185 | 350 @ivar controller: device controller |
mjw@2185 | 351 @type controller: DeviceController |
mjw@2185 | 352 @ivar props: property table |
mjw@2185 | 353 @type props: { String: value } |
mjw@1654 | 354 """ |
mjw@1654 | 355 |
mjw@1975 | 356 def __init__(self, idx, controller): |
mjw@1975 | 357 self.idx = str(idx) |
mjw@1623 | 358 self.controller = controller |
mjw@1623 | 359 self.props = {} |
mjw@1623 | 360 |
mjw@1975 | 361 def getidx(self): |
mjw@1975 | 362 return self.idx |
mjw@1975 | 363 |
mjw@1623 | 364 def setprop(self, k, v): |
mjw@1623 | 365 self.props[k] = v |
mjw@1623 | 366 |
mjw@1623 | 367 def getprop(self, k, v=None): |
mjw@1623 | 368 return self.props.get(k, v) |
mjw@1623 | 369 |
mjw@1623 | 370 def hasprop(self, k): |
mjw@1623 | 371 return k in self.props |
mjw@1623 | 372 |
mjw@1623 | 373 def delprop(self, k): |
mjw@1623 | 374 if k in self.props: |
mjw@1623 | 375 del self.props[k] |
mjw@1623 | 376 |
mjw@1623 | 377 def sxpr(self): |
mjw@2185 | 378 """Get the s-expression for the deivice. |
mjw@2185 | 379 Implement in a subclass. |
mjw@2185 | 380 |
mjw@2185 | 381 @return: sxpr |
mjw@2185 | 382 """ |
mjw@1623 | 383 raise NotImplementedError() |
mjw@1623 | 384 |
mjw@1623 | 385 |