blob: 6dc68617b69822be52e4ea0e8b91b63213d632ea [file] [log] [blame]
Matthew Barthdb440d42017-04-17 15:49:37 -05001#!/usr/bin/env python
2
Brad Bishop34a7acd2017-04-27 23:47:23 -04003'''Phosphor DBus Monitor YAML parser and code generator.
4
5The parser workflow is broken down as follows:
6 1 - Import YAML files as native python type(s) instance(s).
7 2 - Create an instance of the Everything class from the
8 native python type instance(s) with the Everything.load
9 method.
10 3 - The Everything class constructor orchestrates conversion of the
11 native python type(s) instances(s) to render helper types.
12 Each render helper type constructor imports its attributes
13 from the native python type(s) instances(s).
14 4 - Present the converted YAML to the command processing method
15 requested by the script user.
16'''
17
Matthew Barthdb440d42017-04-17 15:49:37 -050018import os
19import sys
20import yaml
Brad Bishop34a7acd2017-04-27 23:47:23 -040021import mako.lookup
Matthew Barthdb440d42017-04-17 15:49:37 -050022from argparse import ArgumentParser
Brad Bishop34a7acd2017-04-27 23:47:23 -040023from sdbusplus.renderer import Renderer
Brad Bishop05b0c1e2017-05-23 00:24:01 -040024from sdbusplus.namedelement import NamedElement
Brad Bishope73b2c32017-05-23 18:01:54 -040025import sdbusplus.property
Brad Bishop05b0c1e2017-05-23 00:24:01 -040026
27
28class InvalidConfigError(BaseException):
29 '''General purpose config file parsing error.'''
30
31 def __init__(self, path, msg):
32 '''Display configuration file with the syntax
33 error and the error message.'''
34
35 self.config = path
36 self.msg = msg
37
38
39class NotUniqueError(InvalidConfigError):
40 '''Within a config file names must be unique.
41 Display the config file with the duplicate and
42 the duplicate itself.'''
43
44 def __init__(self, path, cls, *names):
45 fmt = 'Duplicate {0}: "{1}"'
46 super(NotUniqueError, self).__init__(
47 path, fmt.format(cls, ' '.join(names)))
48
49
50def get_index(objs, cls, name, config=None):
51 '''Items are usually rendered as C++ arrays and as
52 such are stored in python lists. Given an item name
53 its class, and an optional config file filter, find
54 the item index.'''
55
56 for i, x in enumerate(objs.get(cls, [])):
57 if config and x.configfile != config:
58 continue
59 if x.name != name:
60 continue
61
62 return i
63 raise InvalidConfigError(config, 'Could not find name: "{0}"'.format(name))
64
65
66def exists(objs, cls, name, config=None):
67 '''Check to see if an item already exists in a list given
68 the item name.'''
69
70 try:
71 get_index(objs, cls, name, config)
72 except:
73 return False
74
75 return True
76
77
78def add_unique(obj, *a, **kw):
79 '''Add an item to one or more lists unless already present,
80 with an option to constrain the search to a specific config file.'''
81
82 for container in a:
83 if not exists(container, obj.cls, obj.name, config=kw.get('config')):
84 container.setdefault(obj.cls, []).append(obj)
Matthew Barthdb440d42017-04-17 15:49:37 -050085
86
Brad Bishop01079892017-05-26 10:56:45 -040087class Cast(object):
88 '''Decorate an argument by casting it.'''
89
90 def __init__(self, cast, target):
91 '''cast is the cast type (static, const, etc...).
92 target is the cast target type.'''
93 self.cast = cast
94 self.target = target
95
96 def __call__(self, arg):
97 return '{0}_cast<{1}>({2})'.format(self.cast, self.target, arg)
98
99
100class Literal(object):
101 '''Decorate an argument with a literal operator.'''
102
103 integer_types = [
104 'int8',
105 'int16',
106 'int32',
107 'int64',
108 'uint8',
109 'uint16',
110 'uint32',
111 'uint64'
112 ]
113
114 def __init__(self, type):
115 self.type = type
116
117 def __call__(self, arg):
118 if 'uint' in self.type:
119 arg = '{0}ull'.format(arg)
120 elif 'int' in self.type:
121 arg = '{0}ll'.format(arg)
122
123 if self.type in self.integer_types:
124 return Cast('static', '{0}_t'.format(self.type))(arg)
125
126 if self.type == 'string':
127 return '{0}s'.format(arg)
128
129 return arg
130
131
132class FixBool(object):
133 '''Un-capitalize booleans.'''
134
135 def __call__(self, arg):
136 return '{0}'.format(arg.lower())
137
138
139class Quote(object):
140 '''Decorate an argument by quoting it.'''
141
142 def __call__(self, arg):
143 return '"{0}"'.format(arg)
144
145
146class Argument(NamedElement, Renderer):
147 '''Define argument type inteface.'''
148
149 def __init__(self, **kw):
150 self.type = kw.pop('type', None)
151 super(Argument, self).__init__(**kw)
152
153 def argument(self, loader, indent):
154 raise NotImplementedError
155
156
157class TrivialArgument(Argument):
158 '''Non-array type arguments.'''
159
160 def __init__(self, **kw):
161 self.value = kw.pop('value')
162 self.decorators = kw.pop('decorators', [])
163 if kw.get('type', None):
164 self.decorators.insert(0, Literal(kw['type']))
165 if kw.get('type', None) == 'string':
166 self.decorators.insert(0, Quote())
167 if kw.get('type', None) == 'boolean':
168 self.decorators.insert(0, FixBool())
169
170 super(TrivialArgument, self).__init__(**kw)
171
172 def argument(self, loader, indent):
173 a = str(self.value)
174 for d in self.decorators:
175 a = d(a)
176
177 return a
178
179
Brad Bishop34a7acd2017-04-27 23:47:23 -0400180class Indent(object):
181 '''Help templates be depth agnostic.'''
Matthew Barthdb440d42017-04-17 15:49:37 -0500182
Brad Bishop34a7acd2017-04-27 23:47:23 -0400183 def __init__(self, depth=0):
184 self.depth = depth
Matthew Barthdb440d42017-04-17 15:49:37 -0500185
Brad Bishop34a7acd2017-04-27 23:47:23 -0400186 def __add__(self, depth):
187 return Indent(self.depth + depth)
188
189 def __call__(self, depth):
190 '''Render an indent at the current depth plus depth.'''
191 return 4*' '*(depth + self.depth)
192
193
Brad Bishop05b0c1e2017-05-23 00:24:01 -0400194class ConfigEntry(NamedElement):
195 '''Base interface for rendered items.'''
196
197 def __init__(self, *a, **kw):
198 '''Pop the configfile/class/subclass keywords.'''
199
200 self.configfile = kw.pop('configfile')
201 self.cls = kw.pop('class')
202 self.subclass = kw.pop(self.cls)
203 super(ConfigEntry, self).__init__(**kw)
204
205 def factory(self, objs):
206 ''' Optional factory interface for subclasses to add
207 additional items to be rendered.'''
208
209 pass
210
211 def setup(self, objs):
212 ''' Optional setup interface for subclasses, invoked
213 after all factory methods have been run.'''
214
215 pass
216
217
Brad Bishop0e7df132017-05-23 17:58:12 -0400218class Path(ConfigEntry):
219 '''Path/metadata association.'''
220
221 def __init__(self, *a, **kw):
222 super(Path, self).__init__(**kw)
223
224 def factory(self, objs):
225 '''Create path and metadata elements.'''
226
227 args = {
228 'class': 'pathname',
229 'pathname': 'element',
230 'name': self.name['path']
231 }
232 add_unique(ConfigEntry(
233 configfile=self.configfile, **args), objs)
234
235 args = {
236 'class': 'meta',
237 'meta': 'element',
238 'name': self.name['meta']
239 }
240 add_unique(ConfigEntry(
241 configfile=self.configfile, **args), objs)
242
243 super(Path, self).factory(objs)
244
245 def setup(self, objs):
246 '''Resolve path and metadata names to indicies.'''
247
248 self.path = get_index(
249 objs, 'pathname', self.name['path'])
250 self.meta = get_index(
251 objs, 'meta', self.name['meta'])
252
253 super(Path, self).setup(objs)
254
255
Brad Bishope73b2c32017-05-23 18:01:54 -0400256class Property(ConfigEntry):
257 '''Property/interface/metadata association.'''
258
259 def __init__(self, *a, **kw):
260 super(Property, self).__init__(**kw)
261
262 def factory(self, objs):
263 '''Create interface, property name and metadata elements.'''
264
265 args = {
266 'class': 'interface',
267 'interface': 'element',
268 'name': self.name['interface']
269 }
270 add_unique(ConfigEntry(
271 configfile=self.configfile, **args), objs)
272
273 args = {
274 'class': 'propertyname',
275 'propertyname': 'element',
276 'name': self.name['property']
277 }
278 add_unique(ConfigEntry(
279 configfile=self.configfile, **args), objs)
280
281 args = {
282 'class': 'meta',
283 'meta': 'element',
284 'name': self.name['meta']
285 }
286 add_unique(ConfigEntry(
287 configfile=self.configfile, **args), objs)
288
289 super(Property, self).factory(objs)
290
291 def setup(self, objs):
292 '''Resolve interface, property and metadata to indicies.'''
293
294 self.interface = get_index(
295 objs, 'interface', self.name['interface'])
296 self.prop = get_index(
297 objs, 'propertyname', self.name['property'])
298 self.meta = get_index(
299 objs, 'meta', self.name['meta'])
300
301 super(Property, self).setup(objs)
302
303
Brad Bishop4b916f12017-05-23 18:06:38 -0400304class Instance(ConfigEntry):
305 '''Property/Path association.'''
306
307 def __init__(self, *a, **kw):
308 super(Instance, self).__init__(**kw)
309
310 def setup(self, objs):
311 '''Resolve elements to indicies.'''
312
313 self.interface = get_index(
314 objs, 'interface', self.name['property']['interface'])
315 self.prop = get_index(
316 objs, 'propertyname', self.name['property']['property'])
317 self.propmeta = get_index(
318 objs, 'meta', self.name['property']['meta'])
319 self.path = get_index(
320 objs, 'pathname', self.name['path']['path'])
321 self.pathmeta = get_index(
322 objs, 'meta', self.name['path']['meta'])
323
324 super(Instance, self).setup(objs)
325
326
Brad Bishop05b0c1e2017-05-23 00:24:01 -0400327class Group(ConfigEntry):
328 '''Pop the members keyword for groups.'''
329
330 def __init__(self, *a, **kw):
331 self.members = kw.pop('members')
332 super(Group, self).__init__(**kw)
333
334
335class ImplicitGroup(Group):
336 '''Provide a factory method for groups whose members are
337 not explicitly declared in the config files.'''
338
339 def __init__(self, *a, **kw):
340 super(ImplicitGroup, self).__init__(**kw)
341
342 def factory(self, objs):
343 '''Create group members.'''
344
345 factory = Everything.classmap(self.subclass, 'element')
346 for m in self.members:
347 args = {
348 'class': self.subclass,
349 self.subclass: 'element',
350 'name': m
351 }
352
353 obj = factory(configfile=self.configfile, **args)
354 add_unique(obj, objs)
355 obj.factory(objs)
356
357 super(ImplicitGroup, self).factory(objs)
358
359
Brad Bishop0e7df132017-05-23 17:58:12 -0400360class GroupOfPaths(ImplicitGroup):
361 '''Path group config file directive.'''
362
363 def __init__(self, *a, **kw):
364 super(GroupOfPaths, self).__init__(**kw)
365
366 def setup(self, objs):
367 '''Resolve group members.'''
368
369 def map_member(x):
370 path = get_index(
371 objs, 'pathname', x['path'])
372 meta = get_index(
373 objs, 'meta', x['meta'])
374 return (path, meta)
375
376 self.members = map(
377 map_member,
378 self.members)
379
380 super(GroupOfPaths, self).setup(objs)
381
382
Brad Bishope73b2c32017-05-23 18:01:54 -0400383class GroupOfProperties(ImplicitGroup):
384 '''Property group config file directive.'''
385
386 def __init__(self, *a, **kw):
387 self.datatype = sdbusplus.property.Property(
388 name=kw.get('name'),
389 type=kw.pop('type')).cppTypeName
390
391 super(GroupOfProperties, self).__init__(**kw)
392
393 def setup(self, objs):
394 '''Resolve group members.'''
395
396 def map_member(x):
397 iface = get_index(
398 objs, 'interface', x['interface'])
399 prop = get_index(
400 objs, 'propertyname', x['property'])
401 meta = get_index(
402 objs, 'meta', x['meta'])
403
404 return (iface, prop, meta)
405
406 self.members = map(
407 map_member,
408 self.members)
409
410 super(GroupOfProperties, self).setup(objs)
411
412
Brad Bishop4b916f12017-05-23 18:06:38 -0400413class GroupOfInstances(ImplicitGroup):
414 '''A group of property instances.'''
415
416 def __init__(self, *a, **kw):
417 super(GroupOfInstances, self).__init__(**kw)
418
419 def setup(self, objs):
420 '''Resolve group members.'''
421
422 def map_member(x):
423 path = get_index(objs, 'pathname', x['path']['path'])
424 pathmeta = get_index(objs, 'meta', x['path']['meta'])
425 interface = get_index(
426 objs, 'interface', x['property']['interface'])
427 prop = get_index(objs, 'propertyname', x['property']['property'])
428 propmeta = get_index(objs, 'meta', x['property']['meta'])
429 instance = get_index(objs, 'instance', x)
430
431 return (path, pathmeta, interface, prop, propmeta, instance)
432
433 self.members = map(
434 map_member,
435 self.members)
436
437 super(GroupOfInstances, self).setup(objs)
438
439
440class HasPropertyIndex(ConfigEntry):
441 '''Handle config file directives that require an index to be
442 constructed.'''
443
444 def __init__(self, *a, **kw):
445 self.paths = kw.pop('paths')
446 self.properties = kw.pop('properties')
447 super(HasPropertyIndex, self).__init__(**kw)
448
449 def factory(self, objs):
450 '''Create a group of instances for this index.'''
451
452 members = []
453 path_group = get_index(
454 objs, 'pathgroup', self.paths, config=self.configfile)
455 property_group = get_index(
456 objs, 'propertygroup', self.properties, config=self.configfile)
457
458 for path in objs['pathgroup'][path_group].members:
459 for prop in objs['propertygroup'][property_group].members:
460 member = {
461 'path': path,
462 'property': prop,
463 }
464 members.append(member)
465
466 args = {
467 'members': members,
468 'class': 'instancegroup',
469 'instancegroup': 'instance',
470 'name': '{0} {1}'.format(self.paths, self.properties)
471 }
472
473 group = GroupOfInstances(configfile=self.configfile, **args)
474 add_unique(group, objs, config=self.configfile)
475 group.factory(objs)
476
477 super(HasPropertyIndex, self).factory(objs)
478
479 def setup(self, objs):
480 '''Resolve path, property, and instance groups.'''
481
482 self.instances = get_index(
483 objs,
484 'instancegroup',
485 '{0} {1}'.format(self.paths, self.properties),
486 config=self.configfile)
487 self.paths = get_index(
488 objs,
489 'pathgroup',
490 self.paths,
491 config=self.configfile)
492 self.properties = get_index(
493 objs,
494 'propertygroup',
495 self.properties,
496 config=self.configfile)
497 self.datatype = objs['propertygroup'][self.properties].datatype
498
499 super(HasPropertyIndex, self).setup(objs)
500
501
502class PropertyWatch(HasPropertyIndex):
503 '''Handle the property watch config file directive.'''
504
505 def __init__(self, *a, **kw):
Brad Bishopfccdc392017-05-22 21:11:09 -0400506 self.callback = kw.pop('callback', None)
Brad Bishop4b916f12017-05-23 18:06:38 -0400507 super(PropertyWatch, self).__init__(**kw)
508
Brad Bishopfccdc392017-05-22 21:11:09 -0400509 def setup(self, objs):
510 '''Resolve optional callback.'''
511
512 if self.callback:
513 self.callback = get_index(
514 objs,
515 'callback',
516 self.callback,
517 config=self.configfile)
518
519 super(PropertyWatch, self).setup(objs)
520
Brad Bishop4b916f12017-05-23 18:06:38 -0400521
Brad Bishopc1283ae2017-05-20 21:42:38 -0400522class Callback(HasPropertyIndex):
523 '''Interface and common logic for callbacks.'''
524
525 def __init__(self, *a, **kw):
526 super(Callback, self).__init__(**kw)
527
528
Brad Bishop4041d722017-05-21 10:06:07 -0400529class ConditionCallback(ConfigEntry, Renderer):
530 '''Handle the journal callback config file directive.'''
531
532 def __init__(self, *a, **kw):
533 self.condition = kw.pop('condition')
534 self.instance = kw.pop('instance')
535 super(ConditionCallback, self).__init__(**kw)
536
537 def factory(self, objs):
538 '''Create a graph instance for this callback.'''
539
540 args = {
541 'configfile': self.configfile,
542 'members': [self.instance],
543 'class': 'callbackgroup',
544 'callbackgroup': 'callback',
545 'name': [self.instance]
546 }
547
548 entry = CallbackGraphEntry(**args)
549 add_unique(entry, objs, config=self.configfile)
550
551 super(ConditionCallback, self).factory(objs)
552
553 def setup(self, objs):
554 '''Resolve condition and graph entry.'''
555
556 self.graph = get_index(
557 objs,
558 'callbackgroup',
559 [self.instance],
560 config=self.configfile)
561
562 self.condition = get_index(
563 objs,
564 'condition',
565 self.name,
566 config=self.configfile)
567
568 super(ConditionCallback, self).setup(objs)
569
570 def construct(self, loader, indent):
571 return self.render(
572 loader,
573 'conditional.mako.cpp',
574 c=self,
575 indent=indent)
576
577
578class Condition(HasPropertyIndex):
579 '''Interface and common logic for conditions.'''
580
581 def __init__(self, *a, **kw):
582 self.callback = kw.pop('callback')
583 super(Condition, self).__init__(**kw)
584
585 def factory(self, objs):
586 '''Create a callback instance for this conditional.'''
587
588 args = {
589 'configfile': self.configfile,
590 'condition': self.name,
591 'class': 'callback',
592 'callback': 'conditional',
593 'instance': self.callback,
594 'name': self.name,
595 }
596
597 callback = ConditionCallback(**args)
598 add_unique(callback, objs, config=self.configfile)
599 callback.factory(objs)
600
601 super(Condition, self).factory(objs)
602
603
604class CountCondition(Condition, Renderer):
605 '''Handle the count condition config file directive.'''
606
607 def __init__(self, *a, **kw):
608 self.countop = kw.pop('countop')
609 self.countbound = kw.pop('countbound')
610 self.op = kw.pop('op')
611 self.bound = kw.pop('bound')
612 super(CountCondition, self).__init__(**kw)
613
614 def construct(self, loader, indent):
615 return self.render(
616 loader,
617 'count.mako.cpp',
618 c=self,
619 indent=indent)
620
621
Brad Bishopc1283ae2017-05-20 21:42:38 -0400622class Journal(Callback, Renderer):
623 '''Handle the journal callback config file directive.'''
624
625 def __init__(self, *a, **kw):
626 self.severity = kw.pop('severity')
627 self.message = kw.pop('message')
628 super(Journal, self).__init__(**kw)
629
630 def construct(self, loader, indent):
631 return self.render(
632 loader,
633 'journal.mako.cpp',
634 c=self,
635 indent=indent)
636
637
Brad Bishop49e66172017-05-23 19:16:21 -0400638class CallbackGraphEntry(Group):
639 '''An entry in a traversal list for groups of callbacks.'''
640
641 def __init__(self, *a, **kw):
642 super(CallbackGraphEntry, self).__init__(**kw)
643
644 def setup(self, objs):
645 '''Resolve group members.'''
646
647 def map_member(x):
648 return get_index(
649 objs, 'callback', x, config=self.configfile)
650
651 self.members = map(
652 map_member,
653 self.members)
654
655 super(CallbackGraphEntry, self).setup(objs)
656
657
658class GroupOfCallbacks(ConfigEntry, Renderer):
659 '''Handle the callback group config file directive.'''
660
661 def __init__(self, *a, **kw):
662 self.members = kw.pop('members')
663 super(GroupOfCallbacks, self).__init__(**kw)
664
665 def factory(self, objs):
666 '''Create a graph instance for this group of callbacks.'''
667
668 args = {
669 'configfile': self.configfile,
670 'members': self.members,
671 'class': 'callbackgroup',
672 'callbackgroup': 'callback',
673 'name': self.members
674 }
675
676 entry = CallbackGraphEntry(**args)
677 add_unique(entry, objs, config=self.configfile)
678
679 super(GroupOfCallbacks, self).factory(objs)
680
681 def setup(self, objs):
682 '''Resolve graph entry.'''
683
684 self.graph = get_index(
685 objs, 'callbackgroup', self.members, config=self.configfile)
686
687 super(GroupOfCallbacks, self).setup(objs)
688
689 def construct(self, loader, indent):
690 return self.render(
691 loader,
692 'callbackgroup.mako.cpp',
693 c=self,
694 indent=indent)
695
696
Brad Bishop34a7acd2017-04-27 23:47:23 -0400697class Everything(Renderer):
698 '''Parse/render entry point.'''
699
700 @staticmethod
Brad Bishop05b0c1e2017-05-23 00:24:01 -0400701 def classmap(cls, sub=None):
702 '''Map render item class and subclass entries to the appropriate
703 handler methods.'''
704
705 class_map = {
Brad Bishop0e7df132017-05-23 17:58:12 -0400706 'path': {
707 'element': Path,
708 },
709 'pathgroup': {
710 'path': GroupOfPaths,
711 },
Brad Bishope73b2c32017-05-23 18:01:54 -0400712 'propertygroup': {
713 'property': GroupOfProperties,
714 },
715 'property': {
716 'element': Property,
717 },
Brad Bishop4b916f12017-05-23 18:06:38 -0400718 'watch': {
719 'property': PropertyWatch,
720 },
721 'instance': {
722 'element': Instance,
723 },
Brad Bishopc1283ae2017-05-20 21:42:38 -0400724 'callback': {
725 'journal': Journal,
Brad Bishop49e66172017-05-23 19:16:21 -0400726 'group': GroupOfCallbacks,
Brad Bishopc1283ae2017-05-20 21:42:38 -0400727 },
Brad Bishop4041d722017-05-21 10:06:07 -0400728 'condition': {
729 'count': CountCondition,
730 },
Brad Bishop05b0c1e2017-05-23 00:24:01 -0400731 }
732
733 if cls not in class_map:
734 raise NotImplementedError('Unknown class: "{0}"'.format(cls))
735 if sub not in class_map[cls]:
736 raise NotImplementedError('Unknown {0} type: "{1}"'.format(
737 cls, sub))
738
739 return class_map[cls][sub]
740
741 @staticmethod
742 def load_one_yaml(path, fd, objs):
743 '''Parse a single YAML file. Parsing occurs in three phases.
744 In the first phase a factory method associated with each
745 configuration file directive is invoked. These factory
746 methods generate more factory methods. In the second
747 phase the factory methods created in the first phase
748 are invoked. In the last phase a callback is invoked on
749 each object created in phase two. Typically the callback
750 resolves references to other configuration file directives.'''
751
752 factory_objs = {}
753 for x in yaml.safe_load(fd.read()) or {}:
754
755 # Create factory object for this config file directive.
756 cls = x['class']
757 sub = x.get(cls)
758 if cls == 'group':
759 cls = '{0}group'.format(sub)
760
761 factory = Everything.classmap(cls, sub)
762 obj = factory(configfile=path, **x)
763
764 # For a given class of directive, validate the file
765 # doesn't have any duplicate names (duplicates are
766 # ok across config files).
767 if exists(factory_objs, obj.cls, obj.name, config=path):
768 raise NotUniqueError(path, cls, obj.name)
769
770 factory_objs.setdefault(cls, []).append(obj)
771 objs.setdefault(cls, []).append(obj)
772
773 for cls, items in factory_objs.items():
774 for obj in items:
775 # Add objects for template consumption.
776 obj.factory(objs)
777
778 @staticmethod
Brad Bishop34a7acd2017-04-27 23:47:23 -0400779 def load(args):
780 '''Aggregate all the YAML in the input directory
781 into a single aggregate.'''
782
Brad Bishop05b0c1e2017-05-23 00:24:01 -0400783 objs = {}
784 yaml_files = filter(
785 lambda x: x.endswith('.yaml'),
786 os.listdir(args.inputdir))
Brad Bishop34a7acd2017-04-27 23:47:23 -0400787
Brad Bishop05b0c1e2017-05-23 00:24:01 -0400788 yaml_files.sort()
Brad Bishop34a7acd2017-04-27 23:47:23 -0400789
Brad Bishop05b0c1e2017-05-23 00:24:01 -0400790 for x in yaml_files:
791 path = os.path.join(args.inputdir, x)
792 with open(path, 'r') as fd:
793 Everything.load_one_yaml(path, fd, objs)
794
795 # Configuration file directives reference each other via
796 # the name attribute; however, when rendered the reference
797 # is just an array index.
798 #
799 # At this point all objects have been created but references
800 # have not been resolved to array indicies. Instruct objects
801 # to do that now.
802 for cls, items in objs.items():
803 for obj in items:
804 obj.setup(objs)
805
806 return Everything(**objs)
Brad Bishop34a7acd2017-04-27 23:47:23 -0400807
808 def __init__(self, *a, **kw):
Brad Bishop0e7df132017-05-23 17:58:12 -0400809 self.pathmeta = kw.pop('path', [])
810 self.paths = kw.pop('pathname', [])
811 self.meta = kw.pop('meta', [])
812 self.pathgroups = kw.pop('pathgroup', [])
Brad Bishope73b2c32017-05-23 18:01:54 -0400813 self.interfaces = kw.pop('interface', [])
814 self.properties = kw.pop('property', [])
815 self.propertynames = kw.pop('propertyname', [])
816 self.propertygroups = kw.pop('propertygroup', [])
Brad Bishop4b916f12017-05-23 18:06:38 -0400817 self.instances = kw.pop('instance', [])
818 self.instancegroups = kw.pop('instancegroup', [])
819 self.watches = kw.pop('watch', [])
Brad Bishopc1283ae2017-05-20 21:42:38 -0400820 self.callbacks = kw.pop('callback', [])
Brad Bishop49e66172017-05-23 19:16:21 -0400821 self.callbackgroups = kw.pop('callbackgroup', [])
Brad Bishop4041d722017-05-21 10:06:07 -0400822 self.conditions = kw.pop('condition', [])
Brad Bishop0e7df132017-05-23 17:58:12 -0400823
Brad Bishop34a7acd2017-04-27 23:47:23 -0400824 super(Everything, self).__init__(**kw)
825
826 def generate_cpp(self, loader):
827 '''Render the template with the provided data.'''
Brad Bishope3a01af2017-05-15 17:09:04 -0400828 with open(args.output, 'w') as fd:
Brad Bishop34a7acd2017-04-27 23:47:23 -0400829 fd.write(
830 self.render(
831 loader,
Brad Bishope3a01af2017-05-15 17:09:04 -0400832 args.template,
Brad Bishop0e7df132017-05-23 17:58:12 -0400833 meta=self.meta,
Brad Bishope73b2c32017-05-23 18:01:54 -0400834 properties=self.properties,
835 propertynames=self.propertynames,
836 interfaces=self.interfaces,
Brad Bishop0e7df132017-05-23 17:58:12 -0400837 paths=self.paths,
838 pathmeta=self.pathmeta,
839 pathgroups=self.pathgroups,
Brad Bishope73b2c32017-05-23 18:01:54 -0400840 propertygroups=self.propertygroups,
Brad Bishop4b916f12017-05-23 18:06:38 -0400841 instances=self.instances,
842 watches=self.watches,
843 instancegroups=self.instancegroups,
Brad Bishopc1283ae2017-05-20 21:42:38 -0400844 callbacks=self.callbacks,
Brad Bishop49e66172017-05-23 19:16:21 -0400845 callbackgroups=self.callbackgroups,
Brad Bishop4041d722017-05-21 10:06:07 -0400846 conditions=self.conditions,
Brad Bishop34a7acd2017-04-27 23:47:23 -0400847 indent=Indent()))
Matthew Barthdb440d42017-04-17 15:49:37 -0500848
849if __name__ == '__main__':
Brad Bishop34a7acd2017-04-27 23:47:23 -0400850 script_dir = os.path.dirname(os.path.realpath(__file__))
851 valid_commands = {
852 'generate-cpp': 'generate_cpp',
853 }
854
855 parser = ArgumentParser(
856 description='Phosphor DBus Monitor (PDM) YAML '
857 'scanner and code generator.')
858
Matthew Barthdb440d42017-04-17 15:49:37 -0500859 parser.add_argument(
Brad Bishope3a01af2017-05-15 17:09:04 -0400860 "-o", "--out", dest="output",
861 default='generated.cpp',
862 help="Generated output file name and path.")
863 parser.add_argument(
864 '-t', '--template', dest='template',
Brad Bishop870c3fc2017-05-22 23:23:13 -0400865 default='generated.mako.hpp',
Brad Bishope3a01af2017-05-15 17:09:04 -0400866 help='The top level template to render.')
867 parser.add_argument(
868 '-p', '--template-path', dest='template_search',
869 default=script_dir,
870 help='The space delimited mako template search path.')
Brad Bishop34a7acd2017-04-27 23:47:23 -0400871 parser.add_argument(
872 '-d', '--dir', dest='inputdir',
873 default=os.path.join(script_dir, 'example'),
874 help='Location of files to process.')
875 parser.add_argument(
876 'command', metavar='COMMAND', type=str,
877 choices=valid_commands.keys(),
878 help='%s.' % " | ".join(valid_commands.keys()))
Matthew Barthdb440d42017-04-17 15:49:37 -0500879
Brad Bishop34a7acd2017-04-27 23:47:23 -0400880 args = parser.parse_args()
881
882 if sys.version_info < (3, 0):
883 lookup = mako.lookup.TemplateLookup(
Brad Bishope3a01af2017-05-15 17:09:04 -0400884 directories=args.template_search.split(),
Brad Bishop34a7acd2017-04-27 23:47:23 -0400885 disable_unicode=True)
886 else:
887 lookup = mako.lookup.TemplateLookup(
Brad Bishope3a01af2017-05-15 17:09:04 -0400888 directories=args.template_search.split())
Brad Bishop05b0c1e2017-05-23 00:24:01 -0400889 try:
890 function = getattr(
891 Everything.load(args),
892 valid_commands[args.command])
893 function(lookup)
894 except InvalidConfigError as e:
895 sys.stdout.write('{0}: {1}\n\n'.format(e.config, e.msg))
896 raise