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$"
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
48
62
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
85 """
86 I represent the state of a planet in an admin client.
87 See L{ManagerPlanetState}.
88 """
89
97
98 pb.setUnjellyableForClass(ManagerPlanetState, AdminPlanetState)
99
100
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
120
122 """
123 Clear out all component entries.
124
125 @returns: a DeferredList that will fire when all notifications
126 are done.
127 """
128
129 components = self.get('components')[:]
130
131 dList = [self.remove('components', c) for c in components]
132 return defer.DeferredList(dList)
133
134
136 """
137 I represent the state of an atmosphere in an admin client.
138 See L{ManagerAtmosphereState}.
139 """
140
146
147 pb.setUnjellyableForClass(ManagerAtmosphereState, AdminAtmosphereState)
148
149
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
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
175 """
176 Clear out all component entries
177 """
178
179 components = self.get('components')[:]
180
181 dList = [self.remove('components', c) for c in components]
182 return defer.DeferredList(dList)
183
184
186 """
187 I represent the state of a flow in an admin client.
188 See L{ManagerFlowState}.
189 """
190
196
197 pb.setUnjellyableForClass(ManagerFlowState, AdminFlowState)
198
199
200
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
214
215
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
258
260 return "<%s.%s name=%r>" % (self.__module__,
261 self.__class__.__name__,
262 self._dict['name'])
263
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
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
284 self.set('mood', jobState.get('mood'))
285
286
287
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
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
308 self.set('moodPending', None)
309
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
325 """
326 Remove the job state.
327 """
328
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
336
337
338
339
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
351 """
352 I represent the state of a component in the admin client.
353 See L{ManagerComponentState}.
354 """
355
357 return "<%s.%s name=%r>" % (self.__module__,
358 self.__class__.__name__,
359 self._dict['name'])
360
361 pb.setUnjellyableForClass(ManagerComponentState, AdminComponentState)
362
363
364
365
366
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
394
395
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