Package flumotion :: Package manager :: Module base
[hide private]

Source Code for Module flumotion.manager.base

  1  # -*- Mode: Python; test-case-name: flumotion.test.test_manager_common -*- 
  2  # vi:si:et:sw=4:sts=4:ts=4 
  3  # 
  4  # Flumotion - a streaming media server 
  5  # Copyright (C) 2004,2005,2006,2007 Fluendo, S.L. (www.fluendo.com). 
  6  # All rights reserved. 
  7   
  8  # This file may be distributed and/or modified under the terms of 
  9  # the GNU General Public License version 2 as published by 
 10  # the Free Software Foundation. 
 11  # This file is distributed without any warranty; without even the implied 
 12  # warranty of merchantability or fitness for a particular purpose. 
 13  # See "LICENSE.GPL" in the source distribution for more information. 
 14   
 15  # Licensees having purchased or holding a valid Flumotion Advanced 
 16  # Streaming Server license may use this file in accordance with the 
 17  # Flumotion Advanced Streaming Server Commercial License Agreement. 
 18  # See "LICENSE.Flumotion" in the source distribution for more information. 
 19   
 20  # Headers in this file shall remain intact. 
 21   
 22  """ 
 23  common classes and code to support manager-side objects 
 24  """ 
 25   
 26  from twisted.internet import reactor, defer 
 27  from twisted.spread import pb, flavors 
 28  from twisted.python import failure, reflect 
 29   
 30  from flumotion.common import errors, interfaces, log, common 
 31  from flumotion.common.planet import moods 
 32  from flumotion.twisted import pb as fpb 
 33   
 34  __version__ = "$Rev: 6981 $" 
 35   
 36   
37 -class ManagerAvatar(fpb.PingableAvatar, log.Loggable):
38 """ 39 I am a base class for manager-side avatars to subclass from. 40 41 @ivar avatarId: the id for this avatar, unique inside the heaven 42 @type avatarId: str 43 @ivar heaven: the heaven this avatar is part of 44 @type heaven: L{flumotion.manager.base.ManagerHeaven} 45 @ivar mind: a remote reference to the client-side Medium 46 @type mind: L{twisted.spread.pb.RemoteReference} 47 @ivar vishnu: the vishnu that manages this avatar's heaven 48 @type vishnu: L{flumotion.manager.manager.Vishnu} 49 """ 50 remoteLogName = 'medium' 51 logCategory = 'manager-avatar' 52
53 - def __init__(self, heaven, avatarId, remoteIdentity, mind):
54 """ 55 @param heaven: the heaven this avatar is part of 56 @type heaven: L{flumotion.manager.base.ManagerHeaven} 57 @param avatarId: id of the avatar to create 58 @type avatarId: str 59 @param remoteIdentity: manager-assigned identity object for this 60 avatar 61 @type remoteIdentity: L{flumotion.common.identity.RemoteIdentity} 62 @param mind: a remote reference to the client-side Medium 63 @type mind: L{twisted.spread.pb.RemoteReference} 64 """ 65 fpb.PingableAvatar.__init__(self, avatarId) 66 self.heaven = heaven 67 self.logName = avatarId 68 self.setMind(mind) 69 self.vishnu = heaven.vishnu 70 self.remoteIdentity = remoteIdentity 71 72 self.debug("created new Avatar with id %s", avatarId)
73
74 - def perspective_writeFluDebugMarker(self, level, marker):
75 """ 76 Sets a marker that will be prefixed to the log strings. Setting this 77 marker to multiple elements at a time helps debugging. 78 @param marker: A string to prefix all the log strings. 79 @param level: The log level. It can be log.ERROR, log.DEBUG, 80 log.WARN, log.INFO or log.LOG 81 """ 82 83 self.writeMarker(marker, level) 84 workers = self.vishnu.workerHeaven.state.get('names') 85 componentStates = self.vishnu.getComponentStates() 86 for worker in workers: 87 self.perspective_workerCallRemote(worker, 'writeFluDebugMarker', 88 level, marker) 89 for componentState in componentStates: 90 m = self.vishnu.getComponentMapper(componentState) 91 if m.avatar: 92 self.perspective_componentCallRemote(componentState, 93 'writeFluDebugMarker', 94 level, marker)
95
96 - def makeAvatarInitArgs(klass, heaven, avatarId, remoteIdentity, mind):
97 return defer.succeed((heaven, avatarId, remoteIdentity, mind))
98 makeAvatarInitArgs = classmethod(makeAvatarInitArgs) 99
100 - def makeAvatar(klass, heaven, avatarId, remoteIdentity, mind):
101 log.debug('manager-avatar', 'making avatar with avatarId %s', 102 avatarId) 103 104 def have_args(args): 105 log.debug('manager-avatar', 'instantiating with args=%r', args) 106 return klass(*args)
107 d = klass.makeAvatarInitArgs(heaven, avatarId, remoteIdentity, mind) 108 d.addCallback(have_args) 109 return d
110 makeAvatar = classmethod(makeAvatar) 111
112 - def onShutdown(self):
113 self.stopPingChecking()
114
115 - def mindCallRemote(self, name, *args, **kwargs):
116 """ 117 Call the given remote method, and log calling and returning nicely. 118 119 @param name: name of the remote method 120 @type name: str 121 """ 122 level = log.DEBUG 123 if name == 'ping': 124 level = log.LOG 125 126 return self.mindCallRemoteLogging(level, -1, name, *args, **kwargs)
127
128 - def getClientAddress(self):
129 """ 130 Get the IPv4 address of the machine the PB client is connecting from, 131 as seen from the avatar. 132 133 @returns: the IPv4 address the client is coming from, or None. 134 @rtype: str or None 135 """ 136 if self.mind: 137 peer = self.mind.broker.transport.getPeer() 138 return peer.host 139 140 return None
141
142 - def perspective_getBundleSums(self, bundleName=None, fileName=None, 143 moduleName=None):
144 """ 145 Get a list of (bundleName, md5sum) of all dependency bundles, 146 starting with this bundle, in the correct order. 147 Any of bundleName, fileName, moduleName may be given. 148 149 @type bundleName: str or list of str 150 @param bundleName: the name of the bundle for fetching 151 @type fileName: str or list of str 152 @param fileName: the name of the file requested for fetching 153 @type moduleName: str or list of str 154 @param moduleName: the name of the module requested for import 155 156 @rtype: list of (str, str) tuples of (bundleName, md5sum) 157 """ 158 bundleNames = [] 159 fileNames = [] 160 moduleNames = [] 161 if bundleName: 162 if isinstance(bundleName, str): 163 bundleNames.append(bundleName) 164 else: 165 bundleNames.extend(bundleName) 166 self.debug('asked to get bundle sums for bundles %r' % bundleName) 167 if fileName: 168 if isinstance(fileName, str): 169 fileNames.append(fileName) 170 else: 171 fileNames.extend(fileName) 172 self.debug('asked to get bundle sums for files %r' % fileNames) 173 if moduleName: 174 if isinstance(moduleName, str): 175 moduleNames.append(moduleName) 176 else: 177 moduleNames.extend(moduleName) 178 self.debug('asked to get bundle sums for modules %r' % moduleNames) 179 180 basket = self.vishnu.getBundlerBasket() 181 182 # will raise an error if bundleName not known 183 for fileName in fileNames: 184 bundleName = basket.getBundlerNameByFile(fileName) 185 if not bundleName: 186 msg = 'containing ' + fileName 187 self.warning('No bundle %s' % msg) 188 raise errors.NoBundleError(msg) 189 else: 190 bundleNames.append(bundleName) 191 192 for moduleName in moduleNames: 193 bundleName = basket.getBundlerNameByImport(moduleName) 194 if not bundleName: 195 msg = 'for module ' + moduleName 196 self.warning('No bundle %s' % msg) 197 raise errors.NoBundleError(msg) 198 else: 199 bundleNames.append(bundleName) 200 201 deps = [] 202 for bundleName in bundleNames: 203 thisdeps = basket.getDependencies(bundleName) 204 self.debug('dependencies of %s: %r' % (bundleName, thisdeps[1:])) 205 deps.extend(thisdeps) 206 207 sums = [] 208 for dep in deps: 209 bundler = basket.getBundlerByName(dep) 210 if not bundler: 211 self.warning('Did not find bundle with name %s' % dep) 212 else: 213 sums.append((dep, bundler.bundle().md5sum)) 214 215 self.debug('requested bundles: %r' % [x[0] for x in sums]) 216 return sums
217
218 - def perspective_getBundleSumsByFile(self, filename):
219 """ 220 Get a list of (bundleName, md5sum) of all dependency bundles, 221 starting with this bundle, in the correct order. 222 223 @param filename: the name of the file in a bundle 224 @type filename: str 225 226 @returns: list of (bundleName, md5sum) tuples 227 @rtype: list of (str, str) tuples 228 """ 229 self.debug('asked to get bundle sums for file %s' % filename) 230 basket = self.vishnu.getBundlerBasket() 231 bundleName = basket.getBundlerNameByFile(filename) 232 if not bundleName: 233 self.warning('Did not find a bundle for file %s' % filename) 234 raise errors.NoBundleError("for file %s" % filename) 235 236 return self.perspective_getBundleSums(bundleName)
237
238 - def perspective_getBundleZips(self, bundles):
239 """ 240 Get the zip files for the given list of bundles. 241 242 @param bundles: the names of the bundles to get 243 @type bundles: list of str 244 245 @returns: dictionary of bundleName -> zipdata 246 @rtype: dict of str -> str 247 """ 248 basket = self.vishnu.getBundlerBasket() 249 zips = {} 250 for name in bundles: 251 bundler = basket.getBundlerByName(name) 252 if not bundler: 253 raise errors.NoBundleError('The bundle named "%s" was not found' 254 % (name,)) 255 zips[name] = bundler.bundle().getZip() 256 return zips
257
258 - def perspective_authenticate(self, bouncerName, keycard):
259 """ 260 Authenticate the given keycard. 261 If no bouncerName given, authenticate against the manager's bouncer. 262 If a bouncerName is given, authenticate against the given bouncer 263 in the atmosphere. 264 265 @since: 0.3.1 266 267 @param bouncerName: the name of the atmosphere bouncer, or None 268 @type bouncerName: str or None 269 @param keycard: the keycard to authenticate 270 @type keycard: L{flumotion.common.keycards.Keycard} 271 272 @returns: a deferred, returning the keycard or None. 273 """ 274 if not bouncerName: 275 self.debug( 276 'asked to authenticate keycard %r using manager bouncer' % 277 keycard) 278 return self.vishnu.bouncer.authenticate(keycard) 279 280 self.debug('asked to authenticate keycard %r using bouncer %s' % ( 281 keycard, bouncerName)) 282 avatarId = common.componentId('atmosphere', bouncerName) 283 if not self.heaven.hasAvatar(avatarId): 284 self.warning('No bouncer with id %s registered' % avatarId) 285 raise errors.UnknownComponentError(avatarId) 286 287 bouncerAvatar = self.heaven.getAvatar(avatarId) 288 return bouncerAvatar.authenticate(keycard)
289
290 - def perspective_keepAlive(self, bouncerName, issuerName, ttl):
291 """ 292 Resets the expiry timeout for keycards issued by issuerName. See 293 L{flumotion.component.bouncers.bouncer} for more information. 294 295 @since: 0.4.3 296 297 @param bouncerName: the name of the atmosphere bouncer, or None 298 @type bouncerName: str or None 299 @param issuerName: the issuer for which keycards should be kept 300 alive; that is to say, keycards with the 301 attribute 'issuerName' set to this value will 302 have their ttl values reset. 303 @type issuerName: str 304 @param ttl: the new expiry timeout 305 @type ttl: number 306 307 @returns: a deferred which will fire success or failure. 308 """ 309 self.debug('keycards keepAlive on behalf of %s, ttl=%d', 310 issuerName, ttl) 311 312 if not bouncerName: 313 return self.vishnu.bouncer.keepAlive(issuerName, ttl) 314 315 self.debug('looking for bouncer %s in atmosphere', bouncerName) 316 avatarId = common.componentId('atmosphere', bouncerName) 317 if not self.heaven.hasAvatar(avatarId): 318 self.warning('No bouncer with id %s registered', avatarId) 319 raise errors.UnknownComponentError(avatarId) 320 321 bouncerAvatar = self.heaven.getAvatar(avatarId) 322 return bouncerAvatar.keepAlive(issuerName, ttl)
323
324 - def perspective_getKeycardClasses(self):
325 """ 326 Get the keycard classes the manager's bouncer can authenticate. 327 328 @since: 0.3.1 329 330 @returns: a deferred, returning a list of keycard class names 331 @rtype: L{twisted.internet.defer.Deferred} firing list of str 332 """ 333 classes = self.vishnu.bouncer.keycardClasses 334 return [reflect.qual(c) for c in classes]
335
336 -class ManagerHeaven(pb.Root, log.Loggable):
337 """ 338 I am a base class for heavens in the manager. 339 340 @cvar avatarClass: the class object this heaven instantiates avatars from. 341 To be set in subclass. 342 @ivar avatars: a dict of avatarId -> Avatar 343 @type avatars: dict of str -> L{ManagerAvatar} 344 @ivar vishnu: the Vishnu in control of all the heavens 345 @type vishnu: L{flumotion.manager.manager.Vishnu} 346 """ 347 avatarClass = None 348
349 - def __init__(self, vishnu):
350 """ 351 @param vishnu: the Vishnu in control of all the heavens 352 @type vishnu: L{flumotion.manager.manager.Vishnu} 353 """ 354 self.vishnu = vishnu 355 self.avatars = {} # avatarId -> avatar; populated by
356 # manager.Dispatcher 357 358 ### ManagerHeaven methods
359 - def getAvatar(self, avatarId):
360 """ 361 Get the avatar with the given id. 362 363 @param avatarId: id of the avatar to get 364 @type avatarId: str 365 366 @returns: the avatar with the given id 367 @rtype: L{ManagerAvatar} 368 """ 369 return self.avatars[avatarId]
370
371 - def hasAvatar(self, avatarId):
372 """ 373 Check if a component with that name is registered. 374 375 @param avatarId: id of the avatar to check 376 @type avatarId: str 377 378 @returns: True if an avatar with that id is registered 379 @rtype: bool 380 """ 381 return self.avatars.has_key(avatarId)
382
383 - def getAvatars(self):
384 """ 385 Get all avatars in this heaven. 386 387 @returns: a list of all avatars in this heaven 388 @rtype: list of L{ManagerAvatar} 389 """ 390 return self.avatars.values()
391