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