Package flumotion :: Package common :: Module bundleclient
[hide private]

Source Code for Module flumotion.common.bundleclient

  1  # -*- Mode: Python; test-case-name: flumotion.test.test_bundleclient -*- 
  2  # vi:si:et:sw=4:sts=4:ts=4 
  3   
  4  # Flumotion - a streaming media server 
  5  # Copyright (C) 2004,2005,2006,2007,2008,2009 Fluendo, S.L. 
  6  # Copyright (C) 2010,2011 Flumotion Services, S.A. 
  7  # All rights reserved. 
  8  # 
  9  # This file may be distributed and/or modified under the terms of 
 10  # the GNU Lesser General Public License version 2.1 as published by 
 11  # the Free Software Foundation. 
 12  # This file is distributed without any warranty; without even the implied 
 13  # warranty of merchantability or fitness for a particular purpose. 
 14  # See "LICENSE.LGPL" in the source distribution for more information. 
 15  # 
 16  # Headers in this file shall remain intact. 
 17   
 18  """bundle interface for fetching, caching and importing 
 19  """ 
 20   
 21  import os 
 22  import sys 
 23   
 24  from flumotion.common import bundle, errors, log, package 
 25  from flumotion.configure import configure 
 26   
 27  __all__ = ['BundleLoader'] 
 28  __version__ = "$Rev$" 
 29   
 30   
31 -class BundleLoader(log.Loggable):
32 """ 33 I am an object that can get and set up bundles from a PB server. 34 35 @cvar remote: a remote reference to an avatar on the PB server. 36 """ 37 remote = None 38 _unbundler = None 39
40 - def __init__(self, callRemote):
41 """ 42 @type callRemote: callable 43 """ 44 self.callRemote = callRemote 45 self._unbundler = bundle.Unbundler(configure.cachedir)
46
47 - def getBundles(self, **kwargs):
48 # FIXME: later on, split out this method into getBundles which does 49 # not call registerPackagePath, and setupBundles which calls getBundles 50 # and register. Then change getBundles calls to setupBundles. 51 """ 52 Get, extract and register all bundles needed. 53 Either one of bundleName, fileName or moduleName should be specified 54 in **kwargs, which should be strings or lists of strings. 55 56 @returns: a deferred firing a a list of (bundleName, bundlePath) 57 tuples, with lowest dependency first. 58 bundlePath is the directory to register 59 for this package. 60 """ 61 62 def annotated(d, *extraVals): 63 64 def annotatedReturn(ret): 65 return (ret, ) + extraVals
66 d.addCallback(annotatedReturn) 67 return d
68 69 def getZips(sums): 70 # sums is a list of name, sum tuples, highest to lowest 71 # figure out which bundles we're missing 72 toFetch = [] 73 for name, md5 in sums: 74 path = os.path.join(configure.cachedir, name, md5) 75 if os.path.exists(path): 76 self.log('%s is up to date', name) 77 else: 78 self.log('%s needs fetching', name) 79 # FIXME: We cannot be completelly sure the bundle has the 80 # correct content only by checking that the directory exists. 81 # The worker/manager could have died during a download leaving 82 # the package incomplete. 83 toFetch.append(name) 84 if toFetch: 85 return annotated(self.callRemote('getBundleZips', toFetch), 86 toFetch, sums) 87 else: 88 return {}, [], sums 89 90 def unpackAndRegister((zips, toFetch, sums)): 91 for name in toFetch: 92 if name not in zips: 93 msg = "Missing bundle %s was not received" 94 self.warning(msg, name) 95 raise errors.NoBundleError(msg % name) 96 97 b = bundle.Bundle(name) 98 b.setZip(zips[name]) 99 path = self._unbundler.unbundle(b) 100 101 # register all package paths; to do so we need to reverse sums 102 sums.reverse() 103 ret = [] 104 for name, md5 in sums: 105 self.log('registerPackagePath for %s' % name) 106 path = os.path.join(configure.cachedir, name, md5) 107 if not os.path.exists(path): 108 self.warning("path %s for bundle %s does not exist", 109 path, name) 110 else: 111 package.getPackager().registerPackagePath(path, name) 112 ret.append((name, path)) 113 114 return ret 115 116 # get sums for all bundles we need 117 d = self.callRemote('getBundleSums', **kwargs) 118 d.addCallback(getZips) 119 d.addCallback(unpackAndRegister) 120 return d 121
122 - def loadModule(self, moduleName):
123 """ 124 Load the module given by name. 125 Sets up all necessary bundles to be able to load the module. 126 127 @rtype: L{twisted.internet.defer.Deferred} 128 @returns: a deferred that will fire when the given module is loaded, 129 giving the loaded module. 130 """ 131 132 def gotBundles(bundles): 133 self.debug('Got bundles %r', bundles) 134 135 # load up the module and return it 136 __import__(moduleName, globals(), locals(), []) 137 self.log('loaded module %s', moduleName) 138 return sys.modules[moduleName]
139 140 self.debug('Loading module %s', moduleName) 141 142 # get sums for all bundles we need 143 d = self.getBundles(moduleName=moduleName) 144 d.addCallback(gotBundles) 145 return d 146
147 - def getBundleByName(self, bundleName):
148 """ 149 Get the given bundle locally. 150 151 @rtype: L{twisted.internet.defer.Deferred} 152 @returns: a deferred returning the absolute path under which the 153 bundle is extracted. 154 """ 155 156 def gotBundles(bundles): 157 name, path = bundles[-1] 158 assert name == bundleName 159 self.debug('Got bundle %s in %s', bundleName, path) 160 return path
161 162 163 self.debug('Getting bundle %s', bundleName) 164 d = self.getBundles(bundleName=bundleName) 165 d.addCallback(gotBundles) 166 return d 167
168 - def getFile(self, fileName):
169 """ 170 Do everything needed to get the given bundled file. 171 172 @returns: a deferred returning the absolute path to a local copy 173 of the given file. 174 """ 175 176 def gotBundles(bundles): 177 name, bundlePath = bundles[-1] 178 path = os.path.join(bundlePath, fileName) 179 if not os.path.exists(path): 180 self.warning("path %s for file %s does not exist", 181 path, fileName) 182 return path
183 184 self.debug('Getting file %s', fileName) 185 d = self.getBundles(fileName=fileName) 186 d.addCallback(gotBundles) 187 return d 188