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.twisted import pb as fpb 
 32   
33 -class ManagerAvatar(fpb.PingableAvatar, log.Loggable):
34 """ 35 I am a base class for manager-side avatars to subclass from. 36 37 @ivar avatarId: the id for this avatar, unique inside the heaven 38 @type avatarId: str 39 @ivar heaven: the heaven this avatar is part of 40 @type heaven: L{flumotion.manager.base.ManagerHeaven} 41 @ivar mind: a remote reference to the client-side Medium 42 @type mind: L{twisted.spread.pb.RemoteReference} 43 @ivar vishnu: the vishnu that manages this avatar's heaven 44 @type vishnu: L{flumotion.manager.manager.Vishnu} 45 """ 46 remoteLogName = 'medium' 47 logCategory = 'manager-avatar' 48
49 - def __init__(self, heaven, avatarId, remoteIdentity, mind):
50 """ 51 @param heaven: the heaven this avatar is part of 52 @type heaven: L{flumotion.manager.base.ManagerHeaven} 53 @param avatarId: id of the avatar to create 54 @type avatarId: str 55 @param remoteIdentity: manager-assigned identity object for this 56 avatar 57 @type remoteIdentity: L{flumotion.common.identity.RemoteIdentity} 58 @param mind: a remote reference to the client-side Medium 59 @type mind: L{twisted.spread.pb.RemoteReference} 60 """ 61 fpb.PingableAvatar.__init__(self, avatarId) 62 self.heaven = heaven 63 self.logName = avatarId 64 self.setMind(mind) 65 self.vishnu = heaven.vishnu 66 self.remoteIdentity = remoteIdentity 67 68 self.debug("created new Avatar with id %s", avatarId)
69
70 - def makeAvatarInitArgs(klass, heaven, avatarId, remoteIdentity, mind):
71 return defer.succeed((heaven, avatarId, remoteIdentity, mind))
72 makeAvatarInitArgs = classmethod(makeAvatarInitArgs) 73
74 - def makeAvatar(klass, heaven, avatarId, remoteIdentity, mind):
75 log.debug('manager-avatar', 'making avatar with avatarId %s', 76 avatarId) 77 def have_args(args): 78 log.debug('manager-avatar', 'instantiating with args=%r', args) 79 return klass(*args)
80 d = klass.makeAvatarInitArgs(heaven, avatarId, remoteIdentity, mind) 81 d.addCallback(have_args) 82 return d
83 makeAvatar = classmethod(makeAvatar) 84
85 - def onShutdown(self):
86 self.stopPingChecking()
87
88 - def mindCallRemote(self, name, *args, **kwargs):
89 """ 90 Call the given remote method, and log calling and returning nicely. 91 92 @param name: name of the remote method 93 @type name: str 94 """ 95 level = log.DEBUG 96 if name == 'ping': 97 level = log.LOG 98 99 return self.mindCallRemoteLogging(level, -1, name, *args, **kwargs)
100
101 - def getClientAddress(self):
102 """ 103 Get the IPv4 address of the machine the PB client is connecting from, 104 as seen from the avatar. 105 106 @returns: the IPv4 address the client is coming from, or None. 107 @rtype: str or None 108 """ 109 if self.mind: 110 peer = self.mind.broker.transport.getPeer() 111 return peer.host 112 113 return None
114
115 - def perspective_getBundleSums(self, bundleName=None, fileName=None, 116 moduleName=None):
117 """ 118 Get a list of (bundleName, md5sum) of all dependency bundles, 119 starting with this bundle, in the correct order. 120 Any of bundleName, fileName, moduleName may be given. 121 122 @type bundleName: str or list of str 123 @param bundleName: the name of the bundle for fetching 124 @type fileName: str or list of str 125 @param fileName: the name of the file requested for fetching 126 @type moduleName: str or list of str 127 @param moduleName: the name of the module requested for import 128 129 @rtype: list of (str, str) tuples of (bundleName, md5sum) 130 """ 131 bundleNames = [] 132 fileNames = [] 133 moduleNames = [] 134 if bundleName: 135 if isinstance(bundleName, str): 136 bundleNames.append(bundleName) 137 else: 138 bundleNames.extend(bundleName) 139 self.debug('asked to get bundle sums for bundles %r' % bundleName) 140 if fileName: 141 if isinstance(fileName, str): 142 fileNames.append(fileName) 143 else: 144 fileNames.extend(fileName) 145 self.debug('asked to get bundle sums for files %r' % fileNames) 146 if moduleName: 147 if isinstance(moduleName, str): 148 moduleNames.append(moduleName) 149 else: 150 moduleNames.extend(moduleName) 151 self.debug('asked to get bundle sums for modules %r' % moduleNames) 152 153 basket = self.vishnu.getBundlerBasket() 154 155 # will raise an error if bundleName not known 156 for fileName in fileNames: 157 bundleName = basket.getBundlerNameByFile(fileName) 158 if not bundleName: 159 msg = 'containing ' + fileName 160 self.warning('No bundle %s' % msg) 161 raise errors.NoBundleError(msg) 162 else: 163 bundleNames.append(bundleName) 164 165 for moduleName in moduleNames: 166 bundleName = basket.getBundlerNameByImport(moduleName) 167 if not bundleName: 168 msg = 'for module ' + moduleName 169 self.warning('No bundle %s' % msg) 170 raise errors.NoBundleError(msg) 171 else: 172 bundleNames.append(bundleName) 173 174 deps = [] 175 for bundleName in bundleNames: 176 thisdeps = basket.getDependencies(bundleName) 177 self.debug('dependencies of %s: %r' % (bundleName, thisdeps[1:])) 178 deps.extend(thisdeps) 179 180 sums = [] 181 for dep in deps: 182 bundler = basket.getBundlerByName(dep) 183 if not bundler: 184 self.warning('Did not find bundle with name %s' % dep) 185 else: 186 sums.append((dep, bundler.bundle().md5sum)) 187 188 self.debug('requested bundles: %r' % [x[0] for x in sums]) 189 return sums
190
191 - def perspective_getBundleSumsByFile(self, filename):
192 """ 193 Get a list of (bundleName, md5sum) of all dependency bundles, 194 starting with this bundle, in the correct order. 195 196 @param filename: the name of the file in a bundle 197 @type filename: str 198 199 @returns: list of (bundleName, md5sum) tuples 200 @rtype: list of (str, str) tuples 201 """ 202 self.debug('asked to get bundle sums for file %s' % filename) 203 basket = self.vishnu.getBundlerBasket() 204 bundleName = basket.getBundlerNameByFile(filename) 205 if not bundleName: 206 self.warning('Did not find a bundle for file %s' % filename) 207 raise errors.NoBundleError("for file %s" % filename) 208 209 return self.perspective_getBundleSums(bundleName)
210
211 - def perspective_getBundleZips(self, bundles):
212 """ 213 Get the zip files for the given list of bundles. 214 215 @param bundles: the names of the bundles to get 216 @type bundles: list of str 217 218 @returns: dictionary of bundleName -> zipdata 219 @rtype: dict of str -> str 220 """ 221 basket = self.vishnu.getBundlerBasket() 222 zips = {} 223 for name in bundles: 224 bundler = basket.getBundlerByName(name) 225 if not bundler: 226 raise errors.NoBundleError('The bundle named "%s" was not found' 227 % (name,)) 228 zips[name] = bundler.bundle().getZip() 229 return zips
230
231 - def perspective_authenticate(self, bouncerName, keycard):
232 """ 233 Authenticate the given keycard. 234 If no bouncerName given, authenticate against the manager's bouncer. 235 If a bouncerName is given, authenticate against the given bouncer 236 in the atmosphere. 237 238 @since: 0.3.1 239 240 @param bouncerName: the name of the atmosphere bouncer, or None 241 @type bouncerName: str or None 242 @param keycard: the keycard to authenticate 243 @type keycard: L{flumotion.common.keycards.Keycard} 244 245 @returns: a deferred, returning the keycard or None. 246 """ 247 if not bouncerName: 248 self.debug( 249 'asked to authenticate keycard %r using manager bouncer' % 250 keycard) 251 return self.vishnu.bouncer.authenticate(keycard) 252 253 self.debug('asked to authenticate keycard %r using bouncer %s' % ( 254 keycard, bouncerName)) 255 avatarId = common.componentId('atmosphere', bouncerName) 256 if not self.heaven.hasAvatar(avatarId): 257 self.warning('No bouncer with id %s registered' % avatarId) 258 raise errors.UnknownComponentError(avatarId) 259 260 bouncerAvatar = self.heaven.getAvatar(avatarId) 261 return bouncerAvatar.authenticate(keycard)
262
263 - def perspective_keepAlive(self, bouncerName, issuerName, ttl):
264 """ 265 Resets the expiry timeout for keycards issued by issuerName. See 266 L{flumotion.component.bouncers.bouncer} for more information. 267 268 @since: 0.4.3 269 270 @param bouncerName: the name of the atmosphere bouncer, or None 271 @type bouncerName: str or None 272 @param issuerName: the issuer for which keycards should be kept 273 alive; that is to say, keycards with the 274 attribute 'issuerName' set to this value will 275 have their ttl values reset. 276 @type issuerName: str 277 @param ttl: the new expiry timeout 278 @type ttl: number 279 280 @returns: a deferred which will fire success or failure. 281 """ 282 self.debug('keycards keepAlive on behalf of %s, ttl=%d', 283 issuerName, ttl) 284 285 if not bouncerName: 286 return self.vishnu.bouncer.keepAlive(issuerName, ttl) 287 288 self.debug('looking for bouncer %s in atmosphere', bouncerName) 289 avatarId = common.componentId('atmosphere', bouncerName) 290 if not self.heaven.hasAvatar(avatarId): 291 self.warning('No bouncer with id %s registered', avatarId) 292 raise errors.UnknownComponentError(avatarId) 293 294 bouncerAvatar = self.heaven.getAvatar(avatarId) 295 return bouncerAvatar.keepAlive(issuerName, ttl)
296
297 - def perspective_getKeycardClasses(self):
298 """ 299 Get the keycard classes the manager's bouncer can authenticate. 300 301 @since: 0.3.1 302 303 @returns: a deferred, returning a list of keycard class names 304 @rtype: L{twisted.internet.defer.Deferred} firing list of str 305 """ 306 list = self.vishnu.bouncer.keycardClasses 307 return [reflect.qual(c) for c in list]
308
309 -class ManagerHeaven(pb.Root, log.Loggable):
310 """ 311 I am a base class for heavens in the manager. 312 313 @cvar avatarClass: the class object this heaven instantiates avatars from. 314 To be set in subclass. 315 @ivar avatars: a dict of avatarId -> Avatar 316 @type avatars: dict of str -> L{ManagerAvatar} 317 @ivar vishnu: the Vishnu in control of all the heavens 318 @type vishnu: L{flumotion.manager.manager.Vishnu} 319 """ 320 avatarClass = None 321
322 - def __init__(self, vishnu):
323 """ 324 @param vishnu: the Vishnu in control of all the heavens 325 @type vishnu: L{flumotion.manager.manager.Vishnu} 326 """ 327 self.vishnu = vishnu 328 self.avatars = {} # avatarId -> avatar; populated by
329 # manager.Dispatcher 330 331 ### ManagerHeaven methods
332 - def getAvatar(self, avatarId):
333 """ 334 Get the avatar with the given id. 335 336 @param avatarId: id of the avatar to get 337 @type avatarId: str 338 339 @returns: the avatar with the given id 340 @rtype: L{ManagerAvatar} 341 """ 342 return self.avatars[avatarId]
343
344 - def hasAvatar(self, avatarId):
345 """ 346 Check if a component with that name is registered. 347 348 @param avatarId: id of the avatar to check 349 @type avatarId: str 350 351 @returns: True if an avatar with that id is registered 352 @rtype: bool 353 """ 354 return self.avatars.has_key(avatarId)
355
356 - def getAvatars(self):
357 """ 358 Get all avatars in this heaven. 359 360 @returns: a list of all avatars in this heaven 361 @rtype: list of L{ManagerAvatar} 362 """ 363 return self.avatars.values()
364