1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 """
24 flumotion.launch.parse: A parsing library for flumotion-launch syntax.
25 """
26
27
28 import copy
29 import sys
30
31 from flumotion.common import log, common, dag, registry
32
33 __all__ = ['parse_args']
34
35
37 sys.stderr.write(x + '\n')
38 raise SystemExit(1)
39
40
42 __slots__ = ['type', 'name', 'properties', 'plugs', 'feed',
43 'source', 'clock_master', '_reg']
44
59
61 eaters = self._reg.getEaters()
62 if eaters:
63 required = [x for x in eaters if x.getRequired()]
64 multiple = [x for x in eaters if x.getMultiple()]
65 if required and not self.source:
66 err('Component %s wants to eat but you didn\'t give it '
67 'food' % self.name)
68 if not multiple and len(self.source) > 1:
69 err('Component %s can only eat from one feeder' % self.name)
70 else:
71 if self.source:
72 err('Component %s can\'t eat from anything' % self.name)
73
75 def parse_fraction(v):
76 split = v.split('/')
77 assert len(split) == 2, \
78 "Fraction values should be in the form N/D"
79 return (int(split[0]), int(split[1]))
80
81 ret = {}
82 compprops = dict([(x.getName(), x) for x in specs])
83
84 for k, v in props.items():
85 if k not in compprops:
86 err('Component %s has no such property `%s\'' % (dname, k))
87
88 t = compprops[k].getType()
89 parsers = {'int': int,
90 'long': long,
91 'float': float,
92 'bool': common.strToBool,
93 'string': str,
94 'fraction': parse_fraction}
95
96 try:
97 parser = parsers[t]
98 except KeyError:
99 err('Unknown type `%s\' of property %s in component %s'
100 % (t, k, dname))
101
102 val = parser(v)
103
104 if compprops[k].isMultiple():
105 if not k in ret:
106 ret[k] = []
107 ret[k].append(val)
108 else:
109 ret[k] = val
110
111 for k, v in compprops.items():
112 if v.isRequired() and not k in ret:
113 err('Component %s missing required property `%s\' of type %s'
114 % (dname, k, v.getType()))
115
116 return ret
117
121
123 r = registry.getRegistry()
124 ret = {}
125 for socket in self._reg.getSockets():
126 ret[socket] = []
127 for plugtype, plugprops in self.plugs:
128 if not r.hasPlug(plugtype):
129 err('Unknown plug type: %s' % plugtype)
130 spec = r.getPlug(plugtype)
131 socket = spec.getSocket()
132 if not socket in ret:
133 err('Cannot add plug %s to component %s: '
134 'sockets of type %s not supported'
135 % (plugtype, self.name, socket))
136 props = self._complete_props(plugtype, plugprops,
137 spec.getProperties())
138 plug = {'type': plugtype, 'socket': socket,
139 'properties': props}
140 ret[socket].append(plug)
141 return ret
142
161
163 ret = {'name': self.name,
164 'type': self.type,
165 'properties': copy.deepcopy(self.properties),
166 'plugs': copy.deepcopy(self.properties),
167 'feed': copy.deepcopy(self.feed),
168 'clock-master': copy.deepcopy(self.clock_master),
169 'plugs': copy.deepcopy(self.plugs)}
170 if self.source:
171 ret['source'] = copy.deepcopy(self.source)
172 return ret
173
176 self._names = {}
177 self._last_component = None
178 self.components = {}
179 assert not self
180
182 i = self._names.get(type, 0)
183 self._names[type] = i + 1
184 return '%s%d' % (type, i)
185
186 - def add(self, type):
189
192
195
197 assert self._last_component
198 return self._last_component
199
201 return self.components.keys()
202
206
211
213 return self.components[key]
214
216 self.components[key] = val
217
219 return key in self.components
220
222 return len(self.components)
223
226
228 - def __init__(self, get_last_component):
229
230 self.links = []
231 self._tmp = None
232 self.get_last_component = get_last_component
233
235 return bool(self._tmp)
236
237 - def link(self, feedercompname=None, feeder=None, eatercompname=None,
238 eater=None):
239 if feedercompname:
240 assert not self._tmp
241 self._tmp = [feedercompname, feeder, eatercompname, eater]
242 elif feeder:
243 err('how did i get here?')
244 elif eatercompname:
245 if not self._tmp:
246 err('Invalid grammar: trying to link, but no feeder component')
247 self._tmp[2] = eatercompname
248 if eater:
249 self._tmp[3] = eater
250 elif eater:
251 if not self._tmp:
252 err('Invalid grammar: trying to link, but no feeder component')
253 self._tmp[3] = eater
254 else:
255
256 if not self._tmp:
257 self._tmp = [self.get_last_component(), None, None, None]
258 else:
259 if not self._tmp[0]:
260 self._tmp[0] = self.get_last_component()
261
262 if self._tmp and self._tmp[0] and self._tmp[2]:
263 self.links.append(self._tmp)
264 self._tmp = None
265
267 if self._tmp:
268 err('Invalid grammar: uncompleted link from %s.%s' % self._tmp[:2])
269 else:
270 return self.links
271
273 for link in self.get_links():
274 assert link[0] and link[2]
275 if not link[0] in component_types:
276 err('Invalid grammar: no feeder component %s to link from' % link[0])
277 if not link[2] in component_types:
278 err('Invalid grammar: no eater component %s to link to' % link[2])
279
280 reg = registry.getRegistry()
281 for link in self.get_links():
282 compname = link[0]
283 comptype = component_types[compname]
284 compreg = reg.getComponent(comptype)
285 if link[1]:
286 if not link[1] in compreg.getFeeders():
287 err('Component %s has no feeder named %s' % (compname, link[1]))
288
289 else:
290 if not compreg.getFeeders():
291 err('Component %s has no feeders' % compname)
292 link[1] = compreg.getFeeders()[0]
293
294 for link in self.get_links():
295 compname = link[2]
296 comptype = component_types[compname]
297 compreg = reg.getComponent(comptype)
298 eaters = compreg.getEaters()
299 if link[3]:
300 if not link[3] in [x.getName() for x in eaters]:
301 err('Component %s has no eater named %s' % (compname, link[3]))
302
303 else:
304 if not eaters:
305 err('Component %s has no eaters' % compname)
306 link[3] = eaters[0].getName()
307
308 feeders = dict([(name, []) for name in component_types])
309 for link in self.get_links():
310 feeders[link[2]].append('%s:%s' % (link[0], link[1]))
311 return feeders
312
315
317 for link in self.links:
318 print '%s:%s => %s:%s' % tuple(link)
319
321 plugargs = arg.split(',')
322 plug = plugargs.pop(0)[1:]
323 props = {}
324 for arg in plugargs:
325 prop = arg[:arg.index('=')]
326 val = arg[arg.index('=')+1:]
327 if not prop or not val:
328 err('Invalid plug property setting: %s' % arg)
329 props[prop] = val
330 return plug, props
331
333 prop = arg[:arg.index('=')]
334 val = arg[arg.index('=')+1:]
335 if not prop or not val:
336 err('Invalid property setting: %s' % arg)
337 return prop, val
338
340 def assert_in_component(msg):
341 if linker.pending() or not components:
342 err('Invalid grammar: %s' % msg)
343
344 if arg == '!':
345 if not components:
346 err('Invalid grammar: `!\' without feeder component')
347 linker.link()
348
349 elif arg[0] == '/':
350 assert_in_component('Plug %s does not follow a component' % arg)
351 plug, props = parse_plug(arg)
352 components.add_plug_to_current(plug, props)
353
354 elif arg.find('=') != -1:
355 assert_in_component('Property %s does not follow a component' % arg)
356 prop, val = parse_prop(arg)
357 components.add_prop_to_current(prop, val)
358
359 elif arg.find('.') != -1:
360 t = arg.split('.')
361 if len(t) != 2:
362 err('Invalid grammar: bad eater/feeder specification: %s' % arg)
363 t = [z or None for z in t]
364 if linker.pending():
365 linker.link(eatercompname=t[0], eater=t[1])
366 elif components:
367 linker.link(feedercompname=t[0] or components.last(), feeder=t[1])
368 else:
369 err('Invalid grammar: trying to link from feeder %s but '
370 'no feeder component' % arg)
371
372 else:
373 components.add(arg)
374 if linker.pending():
375 linker.link(eatercompname=components.last())
376
378 """Parse flumotion-launch arguments.
379
380 Parse flumotion-launch arguments, returning a list of component
381 configs.
382
383 A component config is what we will pass to a component when we
384 create it. It is a dict:
385
386 - 'name': component name
387 - 'type': component type
388 - 'properties': dict of property name => property value
389 - 'feed': list of [feeder name,...]
390 - 'source': list of [feeder name,...], (optional)
391 - 'clock-master': clock master or None
392 - 'plugs': dict of socket name => plug config
393 """
394
395 if not args:
396 err('Usage: flumotion-launch COMPONENT [! COMPONENT]...')
397
398 components = ComponentStore()
399
400 linker = Linker(components.last)
401
402 args.reverse()
403 while args:
404 parse_arg(args.pop().strip(), components, linker)
405
406 feeders = linker.resolve_links(dict([(name, components[name].type)
407 for name in components]))
408
409 for compname in feeders:
410 components[compname].source = feeders[compname]
411 components.complete_and_verify_configs()
412
413 return components.sorted_configs(linker.get_sort_order())
414