Package flumotion :: Package component :: Package bouncers :: Module multibouncer
[hide private]

Source Code for Module flumotion.component.bouncers.multibouncer

  1  # -*- Mode: Python -*- 
  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  A bouncer that combines other bouncers. 
 24  """ 
 25   
 26  from twisted.internet import defer 
 27  from twisted.python import util 
 28   
 29  from flumotion.common import keycards, watched 
 30  from flumotion.common import python, messages, errors, documentation 
 31  from flumotion.common.i18n import N_, gettexter 
 32  from flumotion.component.bouncers import base, component, combinator 
 33   
 34  T_ = gettexter() 
 35   
 36   
37 -class MultiBouncer(component.Bouncer):
38 39 logCategory = 'multibouncer' 40 # FIXME random classes for now, they're going away anyway 41 keycardClasses = (keycards.KeycardHTTPGetArguments, 42 keycards.KeycardGeneric, keycards.KeycardUACPCC, 43 keycards.KeycardUACPP, keycards.KeycardToken) 44
45 - def init(self):
46 self.watchable_keycards = watched.WatchedDict() # keycard id -> Keycard 47 self.contexts = {} # keycard id -> {algorithm name -> bool result} 48 self.algorithms = util.OrderedDict() # name -> algorithm instance 49 self.combinator = None
50
51 - def do_setup(self):
52 53 def add_entry(entry, algorithm): 54 name = entry['type'] 55 if name in self.algorithms: 56 suffix = 1 57 while ('%s-%d' % (name, suffix)) in self.algorithms: 58 suffix += 1 59 name = '%s-%d' % (name, suffix) 60 61 assert name not in self.algorithms 62 self.algorithms[name] = algorithm 63 return name
64 65 # get all algorithm plugs this component has, put them into 66 # self.algorithms with unique names 67 entries = self.config['plugs'].get(base.BOUNCER_ALGORITHM_SOCKET, []) 68 algorithms = self.plugs.get(base.BOUNCER_ALGORITHM_SOCKET, []) 69 70 # check if there's at least one algorithm plug in a separate method, so 71 # subclasses can override it 72 self.check_algorithms(algorithms) 73 74 for entry, algorithm in zip(entries, algorithms): 75 # add the algorithm to the algorithms dictionary 76 name = add_entry(entry, algorithm) 77 # provide the algorithm with the keycard store 78 algorithm.set_keycard_store(self.watchable_keycards) 79 # provide the algorithm with an expiry function crafted especially 80 # for it (containing its unique name) 81 expire = lambda ids: self.algorithm_expire_keycard_ids(ids, name) 82 algorithm.set_expire_function(expire) 83 84 # we don't have any algorithms, stop here (see StaticMultiBouncer to 85 # see why we need this) 86 if not self.algorithms: 87 return 88 89 self.debug("configured with algorithms %r", self.algorithms.keys()) 90 91 # create the algorithm combinator 92 props = self.config['properties'] 93 self.combinator = combinator.AlgorithmCombinator(self.algorithms) 94 95 if 'combination' in props and combinator.pyparsing is None: 96 m = messages.Error(T_(N_( 97 "To use the 'combination' property you need to " 98 "have the 'pyparsing' module installed.\n")), 99 mid='missing-pyparsing') 100 documentation.messageAddPythonInstall(m, 'pyparsing') 101 self.addMessage(m) 102 raise errors.ComponentSetupHandledError() 103 104 # get the combination specification, defaulting to implicit AND 105 spec = props.get('combination', ' and '.join(self.algorithms.keys())) 106 self.debug("using combination %s", spec) 107 try: 108 self.combinator.create_combination(spec) 109 except combinator.ParseException, e: 110 m = messages.Error(T_(N_( 111 "Invalid algorithms combination: %s"), str(e)), 112 mid='wrong-combination') 113 self.addMessage(m) 114 raise errors.ComponentSetupHandledError()
115
116 - def check_algorithms(self, algorithms):
117 if not algorithms: 118 m = messages.Error(T_(N_( 119 "The multibouncer requires at least one bouncer " 120 "algorithm plug to be present")), mid='no-algorithm') 121 self.addMessage(m) 122 raise errors.ComponentSetupHandledError()
123
124 - def do_authenticate(self, keycard):
125 # create a context for this request 126 context = {} 127 # ask the combinator for an answer 128 d = self.combinator.evaluate(keycard, context) 129 130 def authenticated(res, keycard): 131 # the answer is True/False 132 if not res: 133 # False, return None as per the bouncer protocol 134 return None 135 if hasattr(keycard, 'ttl') and keycard.ttl <= 0: 136 # keycard was invalid on input 137 self.log('immediately expiring keycard %r', keycard) 138 return None 139 if self.addKeycard(keycard): 140 # keycard added, set state to AUTHENTICATED, keep the context, 141 # return to caller 142 keycard.state = keycards.AUTHENTICATED 143 self.contexts[keycard.id] = context 144 self.watchable_keycards[keycard.id] = keycard 145 return keycard
146 147 return d.addCallback(authenticated, keycard) 148
149 - def on_keycardRemoved(self, keycard):
150 # clear our references to the keycard 151 del self.contexts[keycard.id] 152 del self.watchable_keycards[keycard.id]
153
154 - def algorithm_expire_keycard_ids(self, keycard_ids, name):
155 # this gets called by a particular algorithm when it wants to expire a 156 # keycard 157 to_expire = [] 158 159 self.debug("algorithm %r requested expiration of keycards %r", 160 name, keycard_ids) 161 162 for keycard_id in keycard_ids: 163 # change the result in the context 164 context = self.contexts[keycard_id] 165 context[name] = False 166 # Reevaluate in the combinator. Because we already got an answer 167 # for that context, it should contain all necesary info, so we 168 # never should call any algorithm method: just do synchronous 169 # evaluation. 170 if not self.combinator.synchronous_evaluate(context): 171 self.log("keycard with id %r will be expired", keycard_id) 172 to_expire.append(keycard_id) 173 174 return self.expireKeycardIds(to_expire)
175 176
177 -class StaticMultiBouncer(MultiBouncer):
178 """A multibouncer that has a static list of bouncer algorithm plugs""" 179 180 algorithmClasses = None 181
182 - def get_main_algorithm(self):
183 for algorithm in self.algorithms.itervalues(): 184 return algorithm
185
186 - def setMedium(self, medium):
187 MultiBouncer.setMedium(self, medium) 188 for algorithm in self.algorithms.itervalues(): 189 self._export_plug_interface(algorithm, medium)
190
191 - def do_setup(self):
192 if self.algorithmClasses is None: 193 raise NotImplementedError("Subclass did not choose algorithm") 194 195 def start_algorithm(d, algorithm, name): 196 self.algorithms[name] = algorithm 197 d.addCallback(lambda _: defer.maybeDeferred(algorithm.start, self)) 198 d.addCallback(algorithm_started, algorithm, name)
199 200 def algorithm_started(_, algorithm, name): 201 algorithm.set_keycard_store(self.watchable_keycards) 202 203 expire = lambda ids: self.algorithm_expire_keycard_ids(ids, name) 204 algorithm.set_expire_function(expire)
205 206 try: 207 klasses = iter(self.algorithmClasses) 208 except TypeError: 209 klasses = iter((self.algorithmClasses, )) 210 211 d = defer.Deferred() 212 for klass in klasses: 213 name = klass.__name__ 214 algorithm = klass({'properties': self.config['properties']}) 215 start_algorithm(d, algorithm, name) 216 217 def create_combinator(_): 218 self.combinator = combinator.AlgorithmCombinator(self.algorithms) 219 spec = ' and '.join(self.algorithms.keys()) 220 self.combinator.create_combination(spec) 221 222 d.addCallback(create_combinator) 223 224 d.callback(None) 225 return d 226
227 - def check_algorithms(self, algorithms):
228 pass
229
230 - def do_stop(self):
231 d = defer.Deferred() 232 for algorithm in self.algorithms.values(): 233 d.addCallback(lambda _: defer.maybeDeferred(algorithm.stop, self)) 234 235 d.callback(None) 236 return d
237