1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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: 6990 $"
34
35
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
60
62 """
63 Return a list of all component states in this planet
64 (from atmosphere and all flows).
65
66 @rtype: list of L{ManagerComponentState}
67 """
68 ret = []
69
70 a = self.get('atmosphere')
71 if a:
72 ret.extend(a.get('components'))
73
74 flows = self.get('flows')
75 if flows:
76 for flow in flows:
77 ret.extend(flow.get('components'))
78
79 return ret
80
81
83 """
84 I represent the state of a planet in an admin client.
85 See L{ManagerPlanetState}.
86 """
94
95 pb.setUnjellyableForClass(ManagerPlanetState, AdminPlanetState)
96
98 """
99 I represent the state of an atmosphere in the manager.
100 The atmosphere contains components that do not participate in a flow,
101 but provide services to flow components.
102
103 I have the following keys:
104
105 - name: string, "atmosphere"
106 - parent: L{ManagerPlanetState}
107 - components (list): list of L{ManagerComponentState}
108 """
109
116
118 """
119 Clear out all component entries.
120
121 @returns: a DeferredList that will fire when all notifications
122 are done.
123 """
124
125 components = self.get('components')[:]
126
127 dList = [self.remove('components', c) for c in components]
128 return defer.DeferredList(dList)
129
131 """
132 I represent the state of an atmosphere in an admin client.
133 See L{ManagerAtmosphereState}.
134 """
140
141 pb.setUnjellyableForClass(ManagerAtmosphereState, AdminAtmosphereState)
142
144 """
145 I represent the state of a flow in the manager.
146
147 I have the following keys:
148
149 - name: string, name of the flow
150 - parent: L{ManagerPlanetState}
151 - components (list): list of L{ManagerComponentState}
152 """
154 """
155 ManagerFlowState constructor. Any keyword arguments are
156 intepreted as initial key-value pairs to set on the new
157 ManagerFlowState.
158 """
159 flavors.StateCacheable.__init__(self)
160 self.addKey('name')
161 self.addKey('parent')
162 self.addListKey('components')
163 for k, v in kwargs.items():
164 self.set(k, v)
165
167 """
168 Clear out all component entries
169 """
170
171 components = self.get('components')[:]
172
173 dList = [self.remove('components', c) for c in components]
174 return defer.DeferredList(dList)
175
177 """
178 I represent the state of a flow in an admin client.
179 See L{ManagerFlowState}.
180 """
186
187 pb.setUnjellyableForClass(ManagerFlowState, AdminFlowState)
188
189
190
191 """
192 @cvar moods: an enum representing the mood a component can be in.
193 """
194 moods = enum.EnumClass(
195 'Moods',
196 ('happy', 'hungry', 'waking', 'sleeping', 'lost', 'sad')
197 )
198 moods.can_stop = staticmethod(lambda m: m != moods.sleeping)
199 moods.can_start = staticmethod(lambda m: m == moods.sleeping)
200
201 _jobStateKeys = ['mood', 'manager-ip', 'pid', 'workerName']
202 _jobStateListKeys = ['messages', ]
203
204
206 """
207 I represent the state of a component in the manager.
208 I have my own state, and also proxy state from the L{ManagerJobState}
209 when the component is actually created in a worker.
210
211 I have the following keys of my own:
212
213 - name: str, name of the component, unique in the parent
214 - parent: L{ManagerFlowState} or L{ManagerAtmosphereState}
215 - type: str, type of the component
216 - moodPending: int, the mood value the component is being set to
217 - workerRequested: str, name of the worker this component is
218 requested to be started on.
219 - config: dict, the configuration dict for this component
220
221 It also has a special key, 'mood'. This acts as a proxy for the mood
222 in the L{WorkerJobState}, when there is a job attached (the job's copy
223 is authoritative when it connects), and is controlled independently at
224 other times.
225
226 I proxy the following keys from the serialized L{WorkerJobState}:
227 - mood, manager-ip, pid, workerName
228 - messages (list)
229 """
230
247
249 return "<%s.%s name=%r>" % (self.__module__,
250 self.__class__.__name__,
251 self._dict['name'])
252
254 """
255 Set the job state I proxy from.
256
257 @type jobState: L{ManagerJobState}
258 """
259 self._jobState = jobState
260 for key in _jobStateKeys:
261
262 if key == 'mood':
263 continue
264 v = jobState.get(key)
265 if v != None:
266 self.set(key, v)
267 for key in _jobStateListKeys:
268 valueList = jobState.get(key)
269 if valueList != None:
270 for v in valueList:
271 self.append(key, v)
272
273 self.set('mood', jobState.get('mood'))
274
275
276
277 proxiedKeys = _jobStateKeys + _jobStateListKeys
278 def proxy(attr):
279 def event(state, key, value):
280 if key in proxiedKeys:
281 getattr(self, attr)(key, value)
282 return event
283
284 jobState.addListener(self, set_=proxy('set'), append=proxy('append'),
285 remove=proxy('remove'))
286
287 - def set(self, key, value):
288
289 if key == 'mood':
290 log.info('componentstate', 'mood of %s changed to %s',
291 self.get('name'), moods.get(value).name)
292 flavors.StateCacheable.set(self, key, value)
293 if key == 'mood' and value == self.get('moodPending'):
294
295 self.set('moodPending', None)
296
298 if self._jobState and moodValue != moods.sad.value:
299 log.warning('componentstate', 'cannot set component mood to '
300 'something other than sad when we have a '
301 'jobState -- fix your code!')
302 elif moodValue == self.get('mood'):
303 log.log('componentstate', '%s already in mood %d',
304 self.get('name'), moodValue)
305 else:
306 log.debug('componentstate', 'manager sets mood of %s from %s to %d',
307 self.get('name'), self.get('mood'), moodValue)
308 self.set('mood', moodValue)
309
311 """
312 Remove the job state.
313 """
314
315 for m in self._jobState.get('messages'):
316 self.remove('messages', m)
317
318 self._jobState.removeListener(self)
319 self._jobState = None
320
321
322
323
324
325
326 if shutdownRequested:
327 log.debug('componentstate', "Shutdown was requested, %s"
328 " now sleeping", self.get('name'))
329 self.setMood(moods.sleeping.value)
330 elif self.get('mood') != moods.sad.value:
331 log.debug('componentstate', "Shutdown was NOT requested,"
332 " %s now lost", self.get('name'))
333 self.setMood(moods.lost.value)
334
336 """
337 I represent the state of a component in the admin client.
338 See L{ManagerComponentState}.
339 """
341 return "<%s.%s name=%r>" % (self.__module__,
342 self.__class__.__name__,
343 self._dict['name'])
344
345 pb.setUnjellyableForClass(ManagerComponentState, AdminComponentState)
346
347
348
350 """
351 I represent the state of a job in the worker, running a component.
352
353 I have the following keys:
354
355 - mood: int, value of the mood this component is in
356 - ip: string, IP address of the worker
357 - pid: int, PID of the job process
358 - workerName: string, name of the worker I'm running on
359 - messages: list of L{flumotion.common.messages.Message}
360
361 In addition, if I am the state of a FeedComponent, then I also
362 have the following keys:
363
364 - eaterNames: list of feedId being eaten by the eaters
365 - feederNames: list of feedId being fed by the feeders
366
367 @todo: change eaterNames and feederNames to eaterFeedIds and ...
368 """
375
377 """
378 I represent the state of a job in the manager.
379 See L{WorkerJobState}.
380 """
381 pass
382
383 pb.setUnjellyableForClass(WorkerJobState, ManagerJobState)
384