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

Source Code for Module flumotion.common.planet

  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  """serializable objects from worker through manager to admin. 
 23  Used by planet, flow, job and component. 
 24  """ 
 25   
 26  from twisted.spread import pb 
 27  from twisted.internet import defer 
 28  from zope.interface import implements 
 29   
 30  from flumotion.twisted import flavors 
 31  from flumotion.common import enum, log 
 32   
 33  __version__ = "$Rev$" 
 34   
 35   
36 -class ManagerPlanetState(flavors.StateCacheable):
37 """ 38 I represent the state of a planet in the manager. 39 40 I have the following keys: 41 42 - name 43 - manager 44 - atmosphere: L{ManagerAtmosphereState} 45 - flows (list): list of L{ManagerFlowState} 46 """ 47 # FIXME: why is there a 'parent' key ? 48
49 - def __init__(self):
50 flavors.StateCacheable.__init__(self) 51 self.addKey('name') 52 self.addKey('version') 53 self.addKey('parent') 54 self.addKey('manager') 55 self.addKey('atmosphere') 56 self.addListKey('flows') 57 self.addDictKey('messages') 58 59 # we always have at least one atmosphere 60 self.set('atmosphere', ManagerAtmosphereState()) 61 self.get('atmosphere').set('parent', self)
62
63 - def getComponents(self):
64 """ 65 Return a list of all component states in this planet 66 (from atmosphere and all flows). 67 68 @rtype: list of L{ManagerComponentState} 69 """ 70 ret = [] 71 72 a = self.get('atmosphere') 73 if a: 74 ret.extend(a.get('components')) 75 76 flows = self.get('flows') 77 if flows: 78 for flow in flows: 79 ret.extend(flow.get('components')) 80 81 return ret
82 83
84 -class AdminPlanetState(flavors.StateRemoteCache):
85 """ 86 I represent the state of a planet in an admin client. 87 See L{ManagerPlanetState}. 88 """ 89
90 - def invalidate(self):
91 for flow in self.get('flows'): 92 flow.invalidate() 93 94 self.get('atmosphere').invalidate() 95 96 flavors.StateRemoteCache.invalidate(self)
97 98 pb.setUnjellyableForClass(ManagerPlanetState, AdminPlanetState) 99 100
101 -class ManagerAtmosphereState(flavors.StateCacheable):
102 """ 103 I represent the state of an atmosphere in the manager. 104 The atmosphere contains components that do not participate in a flow, 105 but provide services to flow components. 106 107 I have the following keys: 108 109 - name: string, "atmosphere" 110 - parent: L{ManagerPlanetState} 111 - components (list): list of L{ManagerComponentState} 112 """ 113
114 - def __init__(self):
115 flavors.StateCacheable.__init__(self) 116 self.addKey('parent') 117 self.addListKey('components') 118 self.addKey('name') 119 self.set('name', 'atmosphere')
120
121 - def empty(self):
122 """ 123 Clear out all component entries. 124 125 @returns: a DeferredList that will fire when all notifications 126 are done. 127 """ 128 # make a copy, so we can iterate safely while modifying 129 components = self.get('components')[:] 130 131 dList = [self.remove('components', c) for c in components] 132 return defer.DeferredList(dList)
133 134
135 -class AdminAtmosphereState(flavors.StateRemoteCache):
136 """ 137 I represent the state of an atmosphere in an admin client. 138 See L{ManagerAtmosphereState}. 139 """ 140
141 - def invalidate(self):
142 for component in self.get('components'): 143 component.invalidate() 144 145 flavors.StateRemoteCache.invalidate(self)
146 147 pb.setUnjellyableForClass(ManagerAtmosphereState, AdminAtmosphereState) 148 149
150 -class ManagerFlowState(flavors.StateCacheable):
151 """ 152 I represent the state of a flow in the manager. 153 154 I have the following keys: 155 156 - name: string, name of the flow 157 - parent: L{ManagerPlanetState} 158 - components (list): list of L{ManagerComponentState} 159 """ 160
161 - def __init__(self, **kwargs):
162 """ 163 ManagerFlowState constructor. Any keyword arguments are 164 intepreted as initial key-value pairs to set on the new 165 ManagerFlowState. 166 """ 167 flavors.StateCacheable.__init__(self) 168 self.addKey('name') 169 self.addKey('parent') 170 self.addListKey('components') 171 for k, v in kwargs.items(): 172 self.set(k, v)
173
174 - def empty(self):
175 """ 176 Clear out all component entries 177 """ 178 # take a copy of the list because we're modifying while running 179 components = self.get('components')[:] 180 181 dList = [self.remove('components', c) for c in components] 182 return defer.DeferredList(dList)
183 184
185 -class AdminFlowState(flavors.StateRemoteCache):
186 """ 187 I represent the state of a flow in an admin client. 188 See L{ManagerFlowState}. 189 """ 190
191 - def invalidate(self):
192 for component in self.get('components'): 193 component.invalidate() 194 195 flavors.StateRemoteCache.invalidate(self)
196 197 pb.setUnjellyableForClass(ManagerFlowState, AdminFlowState) 198 199 # moods 200 # FIXME. make epydoc like this 201 """ 202 @cvar moods: an enum representing the mood a component can be in. 203 """ 204 moods = enum.EnumClass( 205 'Moods', 206 ('happy', 'hungry', 'waking', 'sleeping', 'lost', 'sad')) 207 moods.can_stop = staticmethod(lambda m: m != moods.sleeping) 208 moods.can_start = staticmethod(lambda m: m == moods.sleeping) 209 210 _jobStateKeys = ['mood', 'manager-ip', 'pid', 'workerName'] 211 _jobStateListKeys = ['messages', ] 212 213 # FIXME: maybe make Atmosphere and Flow subclass from a ComponentGroup class ? 214 215
216 -class ManagerComponentState(flavors.StateCacheable):
217 """ 218 I represent the state of a component in the manager. 219 I have my own state, and also proxy state from the L{ManagerJobState} 220 when the component is actually created in a worker. 221 222 I have the following keys of my own: 223 224 - name: str, name of the component, unique in the parent 225 - parent: L{ManagerFlowState} or L{ManagerAtmosphereState} 226 - type: str, type of the component 227 - moodPending: int, the mood value the component is being set to 228 - workerRequested: str, name of the worker this component is 229 requested to be started on. 230 - config: dict, the configuration dict for this component 231 232 It also has a special key, 'mood'. This acts as a proxy for the mood 233 in the L{WorkerJobState}, when there is a job attached (the job's copy 234 is authoritative when it connects), and is controlled independently at 235 other times. 236 237 I proxy the following keys from the serialized L{WorkerJobState}: 238 - mood, manager-ip, pid, workerName 239 - messages (list) 240 """ 241
242 - def __init__(self):
243 flavors.StateCacheable.__init__(self) 244 # our additional keys 245 self.addKey('name') 246 self.addKey('type') 247 self.addKey('parent') 248 self.addKey('moodPending') 249 self.addKey('workerRequested') 250 self.addKey('config') # dictionary 251 252 # proxied from job state or combined with our state (mood) 253 for k in _jobStateKeys: 254 self.addKey(k) 255 for k in _jobStateListKeys: 256 self.addListKey(k) 257 self._jobState = None
258
259 - def __repr__(self):
260 return "<%s.%s name=%r>" % (self.__module__, 261 self.__class__.__name__, 262 self._dict['name'])
263
264 - def setJobState(self, jobState):
265 """ 266 Set the job state I proxy from. 267 268 @type jobState: L{ManagerJobState} 269 """ 270 self._jobState = jobState 271 for key in _jobStateKeys: 272 # only set non-None values 273 if key == 'mood': 274 continue 275 v = jobState.get(key) 276 if v != None: 277 self.set(key, v) 278 for key in _jobStateListKeys: 279 valueList = jobState.get(key) 280 if valueList != None: 281 for v in valueList: 282 self.append(key, v) 283 # set mood last; see #552 284 self.set('mood', jobState.get('mood')) 285 286 # only proxy keys we want proxied; eaterNames and feederNames 287 # are ignored for example 288 proxiedKeys = _jobStateKeys + _jobStateListKeys 289 290 def proxy(attr): 291 292 def event(state, key, value): 293 if key in proxiedKeys: 294 getattr(self, attr)(key, value)
295 return event
296 297 jobState.addListener(self, set_=proxy('set'), append=proxy('append'), 298 remove=proxy('remove')) 299
300 - def set(self, key, value):
301 # extend set so we can log mood changes 302 if key == 'mood': 303 log.info('componentstate', 'mood of %s changed to %s', 304 self.get('name'), moods.get(value).name) 305 flavors.StateCacheable.set(self, key, value) 306 if key == 'mood' and value == self.get('moodPending'): 307 # we have reached our pending mood 308 self.set('moodPending', None)
309
310 - def setMood(self, moodValue):
311 if self._jobState and moodValue != moods.sad.value: 312 log.warning('componentstate', 'cannot set component mood to ' 313 'something other than sad when we have a ' 314 'jobState -- fix your code!') 315 elif moodValue == self.get('mood'): 316 log.log('componentstate', '%s already in mood %d', 317 self.get('name'), moodValue) 318 else: 319 log.debug('componentstate', 320 'manager sets mood of %s from %s to %d', 321 self.get('name'), self.get('mood'), moodValue) 322 self.set('mood', moodValue)
323
324 - def clearJobState(self, shutdownRequested):
325 """ 326 Remove the job state. 327 """ 328 # Clear messages proxied from job 329 for m in self._jobState.get('messages'): 330 self.remove('messages', m) 331 332 self._jobState.removeListener(self) 333 self._jobState = None 334 335 # Clearing a job state means that a component logged out. If the 336 # component logs out due to an explicit manager request, go to 337 # sleeping. Otherwise if the component is sad, leave the mood as 338 # it is, or otherwise go to lost, because it got disconnected 339 # for an unknown reason (probably network related). 340 if shutdownRequested: 341 log.debug('componentstate', "Shutdown was requested, %s" 342 " now sleeping", self.get('name')) 343 self.setMood(moods.sleeping.value) 344 elif self.get('mood') != moods.sad.value: 345 log.debug('componentstate', "Shutdown was NOT requested," 346 " %s now lost", self.get('name')) 347 self.setMood(moods.lost.value)
348 349
350 -class AdminComponentState(flavors.StateRemoteCache):
351 """ 352 I represent the state of a component in the admin client. 353 See L{ManagerComponentState}. 354 """ 355
356 - def __repr__(self):
357 return "<%s.%s name=%r>" % (self.__module__, 358 self.__class__.__name__, 359 self._dict['name'])
360 361 pb.setUnjellyableForClass(ManagerComponentState, AdminComponentState) 362 363 # state of an existing component running in a job process 364 # exchanged between worker and manager 365 366
367 -class WorkerJobState(flavors.StateCacheable):
368 """ 369 I represent the state of a job in the worker, running a component. 370 371 I have the following keys: 372 373 - mood: int, value of the mood this component is in 374 - ip: string, IP address of the worker 375 - pid: int, PID of the job process 376 - workerName: string, name of the worker I'm running on 377 - messages: list of L{flumotion.common.messages.Message} 378 379 In addition, if I am the state of a FeedComponent, then I also 380 have the following keys: 381 382 - eaterNames: list of feedId being eaten by the eaters 383 - feederNames: list of feedId being fed by the feeders 384 385 @todo: change eaterNames and feederNames to eaterFeedIds and ... 386 """ 387
388 - def __init__(self):
389 flavors.StateCacheable.__init__(self) 390 for k in _jobStateKeys: 391 self.addKey(k) 392 for k in _jobStateListKeys: 393 self.addListKey(k)
394 395
396 -class ManagerJobState(flavors.StateRemoteCache):
397 """ 398 I represent the state of a job in the manager. 399 See L{WorkerJobState}. 400 """ 401 pass
402 403 pb.setUnjellyableForClass(WorkerJobState, ManagerJobState) 404