xref: /openbmc/phosphor-dbus-monitor/src/pdmgen.py (revision 3539db640767e6dafa652864fc5a5445db4ee697)
1#!/usr/bin/env python
2
3'''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
18import os
19import sys
20import yaml
21import mako.lookup
22from argparse import ArgumentParser
23from sdbusplus.renderer import Renderer
24from sdbusplus.namedelement import NamedElement
25import sdbusplus.property
26
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)
85
86
87class 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
180class Indent(object):
181    '''Help templates be depth agnostic.'''
182
183    def __init__(self, depth=0):
184        self.depth = depth
185
186    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
194class 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
218class 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
256class 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
304class 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
327class 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
360class 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
383class 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
413class 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):
506        self.callback = kw.pop('callback', None)
507        super(PropertyWatch, self).__init__(**kw)
508
509    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
521
522class Callback(HasPropertyIndex):
523    '''Interface and common logic for callbacks.'''
524
525    def __init__(self, *a, **kw):
526        super(Callback, self).__init__(**kw)
527
528
529class 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        self.defer = kw.pop('defer', None)
536        super(ConditionCallback, self).__init__(**kw)
537
538    def factory(self, objs):
539        '''Create a graph instance for this callback.'''
540
541        args = {
542            'configfile': self.configfile,
543            'members': [self.instance],
544            'class': 'callbackgroup',
545            'callbackgroup': 'callback',
546            'name': [self.instance]
547        }
548
549        entry = CallbackGraphEntry(**args)
550        add_unique(entry, objs, config=self.configfile)
551
552        super(ConditionCallback, self).factory(objs)
553
554    def setup(self, objs):
555        '''Resolve condition and graph entry.'''
556
557        self.graph = get_index(
558            objs,
559            'callbackgroup',
560            [self.instance],
561            config=self.configfile)
562
563        self.condition = get_index(
564            objs,
565            'condition',
566            self.name,
567            config=self.configfile)
568
569        super(ConditionCallback, self).setup(objs)
570
571    def construct(self, loader, indent):
572        return self.render(
573            loader,
574            'conditional.mako.cpp',
575            c=self,
576            indent=indent)
577
578
579class Condition(HasPropertyIndex):
580    '''Interface and common logic for conditions.'''
581
582    def __init__(self, *a, **kw):
583        self.callback = kw.pop('callback')
584        self.defer = kw.pop('defer', None)
585        super(Condition, self).__init__(**kw)
586
587    def factory(self, objs):
588        '''Create a callback instance for this conditional.'''
589
590        args = {
591            'configfile': self.configfile,
592            'condition': self.name,
593            'class': 'callback',
594            'callback': 'conditional',
595            'instance': self.callback,
596            'name': self.name,
597            'defer': self.defer
598        }
599
600        callback = ConditionCallback(**args)
601        add_unique(callback, objs, config=self.configfile)
602        callback.factory(objs)
603
604        super(Condition, self).factory(objs)
605
606
607class CountCondition(Condition, Renderer):
608    '''Handle the count condition config file directive.'''
609
610    def __init__(self, *a, **kw):
611        self.countop = kw.pop('countop')
612        self.countbound = kw.pop('countbound')
613        self.op = kw.pop('op')
614        self.bound = kw.pop('bound')
615        super(CountCondition, self).__init__(**kw)
616
617    def construct(self, loader, indent):
618        return self.render(
619            loader,
620            'count.mako.cpp',
621            c=self,
622            indent=indent)
623
624
625class Journal(Callback, Renderer):
626    '''Handle the journal callback config file directive.'''
627
628    def __init__(self, *a, **kw):
629        self.severity = kw.pop('severity')
630        self.message = kw.pop('message')
631        super(Journal, self).__init__(**kw)
632
633    def construct(self, loader, indent):
634        return self.render(
635            loader,
636            'journal.mako.cpp',
637            c=self,
638            indent=indent)
639
640
641class Method(ConfigEntry, Renderer):
642    '''Handle the method callback config file directive.'''
643
644    def __init__(self, *a, **kw):
645        self.service = kw.pop('service')
646        self.path = kw.pop('path')
647        self.interface = kw.pop('interface')
648        self.method = kw.pop('method')
649        self.args = [TrivialArgument(**x) for x in kw.pop('args', {})]
650        super(Method, self).__init__(**kw)
651
652    def factory(self, objs):
653        args = {
654            'class': 'interface',
655            'interface': 'element',
656            'name': self.service
657        }
658        add_unique(ConfigEntry(
659            configfile=self.configfile, **args), objs)
660
661        args = {
662            'class': 'pathname',
663            'pathname': 'element',
664            'name': self.path
665        }
666        add_unique(ConfigEntry(
667            configfile=self.configfile, **args), objs)
668
669        args = {
670            'class': 'interface',
671            'interface': 'element',
672            'name': self.interface
673        }
674        add_unique(ConfigEntry(
675            configfile=self.configfile, **args), objs)
676
677        args = {
678            'class': 'propertyname',
679            'propertyname': 'element',
680            'name': self.method
681        }
682        add_unique(ConfigEntry(
683            configfile=self.configfile, **args), objs)
684
685        super(Method, self).factory(objs)
686
687    def setup(self, objs):
688        '''Resolve elements.'''
689
690        self.service = get_index(
691            objs,
692            'interface',
693            self.service)
694
695        self.path = get_index(
696            objs,
697            'pathname',
698            self.path)
699
700        self.interface = get_index(
701            objs,
702            'interface',
703            self.interface)
704
705        self.method = get_index(
706            objs,
707            'propertyname',
708            self.method)
709
710        super(Method, self).setup(objs)
711
712    def construct(self, loader, indent):
713        return self.render(
714            loader,
715            'method.mako.cpp',
716            c=self,
717            indent=indent)
718
719
720class CallbackGraphEntry(Group):
721    '''An entry in a traversal list for groups of callbacks.'''
722
723    def __init__(self, *a, **kw):
724        super(CallbackGraphEntry, self).__init__(**kw)
725
726    def setup(self, objs):
727        '''Resolve group members.'''
728
729        def map_member(x):
730            return get_index(
731                objs, 'callback', x, config=self.configfile)
732
733        self.members = map(
734            map_member,
735            self.members)
736
737        super(CallbackGraphEntry, self).setup(objs)
738
739
740class GroupOfCallbacks(ConfigEntry, Renderer):
741    '''Handle the callback group config file directive.'''
742
743    def __init__(self, *a, **kw):
744        self.members = kw.pop('members')
745        super(GroupOfCallbacks, self).__init__(**kw)
746
747    def factory(self, objs):
748        '''Create a graph instance for this group of callbacks.'''
749
750        args = {
751            'configfile': self.configfile,
752            'members': self.members,
753            'class': 'callbackgroup',
754            'callbackgroup': 'callback',
755            'name': self.members
756        }
757
758        entry = CallbackGraphEntry(**args)
759        add_unique(entry, objs, config=self.configfile)
760
761        super(GroupOfCallbacks, self).factory(objs)
762
763    def setup(self, objs):
764        '''Resolve graph entry.'''
765
766        self.graph = get_index(
767            objs, 'callbackgroup', self.members, config=self.configfile)
768
769        super(GroupOfCallbacks, self).setup(objs)
770
771    def construct(self, loader, indent):
772        return self.render(
773            loader,
774            'callbackgroup.mako.cpp',
775            c=self,
776            indent=indent)
777
778
779class Everything(Renderer):
780    '''Parse/render entry point.'''
781
782    @staticmethod
783    def classmap(cls, sub=None):
784        '''Map render item class and subclass entries to the appropriate
785        handler methods.'''
786
787        class_map = {
788            'path': {
789                'element': Path,
790            },
791            'pathgroup': {
792                'path': GroupOfPaths,
793            },
794            'propertygroup': {
795                'property': GroupOfProperties,
796            },
797            'property': {
798                'element': Property,
799            },
800            'watch': {
801                'property': PropertyWatch,
802            },
803            'instance': {
804                'element': Instance,
805            },
806            'callback': {
807                'journal': Journal,
808                'group': GroupOfCallbacks,
809                'method': Method,
810            },
811            'condition': {
812                'count': CountCondition,
813            },
814        }
815
816        if cls not in class_map:
817            raise NotImplementedError('Unknown class: "{0}"'.format(cls))
818        if sub not in class_map[cls]:
819            raise NotImplementedError('Unknown {0} type: "{1}"'.format(
820                cls, sub))
821
822        return class_map[cls][sub]
823
824    @staticmethod
825    def load_one_yaml(path, fd, objs):
826        '''Parse a single YAML file.  Parsing occurs in three phases.
827        In the first phase a factory method associated with each
828        configuration file directive is invoked.  These factory
829        methods generate more factory methods.  In the second
830        phase the factory methods created in the first phase
831        are invoked.  In the last phase a callback is invoked on
832        each object created in phase two.  Typically the callback
833        resolves references to other configuration file directives.'''
834
835        factory_objs = {}
836        for x in yaml.safe_load(fd.read()) or {}:
837
838            # Create factory object for this config file directive.
839            cls = x['class']
840            sub = x.get(cls)
841            if cls == 'group':
842                cls = '{0}group'.format(sub)
843
844            factory = Everything.classmap(cls, sub)
845            obj = factory(configfile=path, **x)
846
847            # For a given class of directive, validate the file
848            # doesn't have any duplicate names (duplicates are
849            # ok across config files).
850            if exists(factory_objs, obj.cls, obj.name, config=path):
851                raise NotUniqueError(path, cls, obj.name)
852
853            factory_objs.setdefault(cls, []).append(obj)
854            objs.setdefault(cls, []).append(obj)
855
856        for cls, items in factory_objs.items():
857            for obj in items:
858                # Add objects for template consumption.
859                obj.factory(objs)
860
861    @staticmethod
862    def load(args):
863        '''Aggregate all the YAML in the input directory
864        into a single aggregate.'''
865
866        objs = {}
867        yaml_files = filter(
868            lambda x: x.endswith('.yaml'),
869            os.listdir(args.inputdir))
870
871        yaml_files.sort()
872
873        for x in yaml_files:
874            path = os.path.join(args.inputdir, x)
875            with open(path, 'r') as fd:
876                Everything.load_one_yaml(path, fd, objs)
877
878        # Configuration file directives reference each other via
879        # the name attribute; however, when rendered the reference
880        # is just an array index.
881        #
882        # At this point all objects have been created but references
883        # have not been resolved to array indicies.  Instruct objects
884        # to do that now.
885        for cls, items in objs.items():
886            for obj in items:
887                obj.setup(objs)
888
889        return Everything(**objs)
890
891    def __init__(self, *a, **kw):
892        self.pathmeta = kw.pop('path', [])
893        self.paths = kw.pop('pathname', [])
894        self.meta = kw.pop('meta', [])
895        self.pathgroups = kw.pop('pathgroup', [])
896        self.interfaces = kw.pop('interface', [])
897        self.properties = kw.pop('property', [])
898        self.propertynames = kw.pop('propertyname', [])
899        self.propertygroups = kw.pop('propertygroup', [])
900        self.instances = kw.pop('instance', [])
901        self.instancegroups = kw.pop('instancegroup', [])
902        self.watches = kw.pop('watch', [])
903        self.callbacks = kw.pop('callback', [])
904        self.callbackgroups = kw.pop('callbackgroup', [])
905        self.conditions = kw.pop('condition', [])
906
907        super(Everything, self).__init__(**kw)
908
909    def generate_cpp(self, loader):
910        '''Render the template with the provided data.'''
911        with open(args.output, 'w') as fd:
912            fd.write(
913                self.render(
914                    loader,
915                    args.template,
916                    meta=self.meta,
917                    properties=self.properties,
918                    propertynames=self.propertynames,
919                    interfaces=self.interfaces,
920                    paths=self.paths,
921                    pathmeta=self.pathmeta,
922                    pathgroups=self.pathgroups,
923                    propertygroups=self.propertygroups,
924                    instances=self.instances,
925                    watches=self.watches,
926                    instancegroups=self.instancegroups,
927                    callbacks=self.callbacks,
928                    callbackgroups=self.callbackgroups,
929                    conditions=self.conditions,
930                    indent=Indent()))
931
932if __name__ == '__main__':
933    script_dir = os.path.dirname(os.path.realpath(__file__))
934    valid_commands = {
935        'generate-cpp': 'generate_cpp',
936    }
937
938    parser = ArgumentParser(
939        description='Phosphor DBus Monitor (PDM) YAML '
940        'scanner and code generator.')
941
942    parser.add_argument(
943        "-o", "--out", dest="output",
944        default='generated.cpp',
945        help="Generated output file name and path.")
946    parser.add_argument(
947        '-t', '--template', dest='template',
948        default='generated.mako.hpp',
949        help='The top level template to render.')
950    parser.add_argument(
951        '-p', '--template-path', dest='template_search',
952        default=script_dir,
953        help='The space delimited mako template search path.')
954    parser.add_argument(
955        '-d', '--dir', dest='inputdir',
956        default=os.path.join(script_dir, 'example'),
957        help='Location of files to process.')
958    parser.add_argument(
959        'command', metavar='COMMAND', type=str,
960        choices=valid_commands.keys(),
961        help='%s.' % " | ".join(valid_commands.keys()))
962
963    args = parser.parse_args()
964
965    if sys.version_info < (3, 0):
966        lookup = mako.lookup.TemplateLookup(
967            directories=args.template_search.split(),
968            disable_unicode=True)
969    else:
970        lookup = mako.lookup.TemplateLookup(
971            directories=args.template_search.split())
972    try:
973        function = getattr(
974            Everything.load(args),
975            valid_commands[args.command])
976        function(lookup)
977    except InvalidConfigError as e:
978        sys.stdout.write('{0}: {1}\n\n'.format(e.config, e.msg))
979        raise
980