xref: /openbmc/phosphor-dbus-monitor/src/pdmgen.py (revision b1e329a6e5763cd40933f446bce759b585169e2d)
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        'int16',
105        'int32',
106        'int64',
107        'uint16',
108        'uint32',
109        'uint64'
110    ]
111
112    def __init__(self, type):
113        self.type = type
114
115    def __call__(self, arg):
116        if 'uint' in self.type:
117            arg = '{0}ull'.format(arg)
118        elif 'int' in self.type:
119            arg = '{0}ll'.format(arg)
120
121        if self.type in self.integer_types:
122            return Cast('static', '{0}_t'.format(self.type))(arg)
123        elif self.type == 'byte':
124            return Cast('static', 'uint8_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        if self.name['meta'].upper() != self.name['meta']:
225            raise InvalidConfigError(
226                self.configfile,
227                'Metadata tag "{0}" must be upper case.'.format(
228                    self.name['meta']))
229
230    def factory(self, objs):
231        '''Create path and metadata elements.'''
232
233        args = {
234            'class': 'pathname',
235            'pathname': 'element',
236            'name': self.name['path']
237        }
238        add_unique(ConfigEntry(
239            configfile=self.configfile, **args), objs)
240
241        args = {
242            'class': 'meta',
243            'meta': 'element',
244            'name': self.name['meta']
245        }
246        add_unique(ConfigEntry(
247            configfile=self.configfile, **args), objs)
248
249        super(Path, self).factory(objs)
250
251    def setup(self, objs):
252        '''Resolve path and metadata names to indicies.'''
253
254        self.path = get_index(
255            objs, 'pathname', self.name['path'])
256        self.meta = get_index(
257            objs, 'meta', self.name['meta'])
258
259        super(Path, self).setup(objs)
260
261
262class Property(ConfigEntry):
263    '''Property/interface/metadata association.'''
264
265    def __init__(self, *a, **kw):
266        super(Property, self).__init__(**kw)
267
268        if self.name['meta'].upper() != self.name['meta']:
269            raise InvalidConfigError(
270                self.configfile,
271                'Metadata tag "{0}" must be upper case.'.format(
272                    self.name['meta']))
273
274    def factory(self, objs):
275        '''Create interface, property name and metadata elements.'''
276
277        args = {
278            'class': 'interface',
279            'interface': 'element',
280            'name': self.name['interface']
281        }
282        add_unique(ConfigEntry(
283            configfile=self.configfile, **args), objs)
284
285        args = {
286            'class': 'propertyname',
287            'propertyname': 'element',
288            'name': self.name['property']
289        }
290        add_unique(ConfigEntry(
291            configfile=self.configfile, **args), objs)
292
293        args = {
294            'class': 'meta',
295            'meta': 'element',
296            'name': self.name['meta']
297        }
298        add_unique(ConfigEntry(
299            configfile=self.configfile, **args), objs)
300
301        super(Property, self).factory(objs)
302
303    def setup(self, objs):
304        '''Resolve interface, property and metadata to indicies.'''
305
306        self.interface = get_index(
307            objs, 'interface', self.name['interface'])
308        self.prop = get_index(
309            objs, 'propertyname', self.name['property'])
310        self.meta = get_index(
311            objs, 'meta', self.name['meta'])
312
313        super(Property, self).setup(objs)
314
315
316class Instance(ConfigEntry):
317    '''Property/Path association.'''
318
319    def __init__(self, *a, **kw):
320        super(Instance, self).__init__(**kw)
321
322    def setup(self, objs):
323        '''Resolve elements to indicies.'''
324
325        self.interface = get_index(
326            objs, 'interface', self.name['property']['interface'])
327        self.prop = get_index(
328            objs, 'propertyname', self.name['property']['property'])
329        self.propmeta = get_index(
330            objs, 'meta', self.name['property']['meta'])
331        self.path = get_index(
332            objs, 'pathname', self.name['path']['path'])
333        self.pathmeta = get_index(
334            objs, 'meta', self.name['path']['meta'])
335
336        super(Instance, self).setup(objs)
337
338
339class Group(ConfigEntry):
340    '''Pop the members keyword for groups.'''
341
342    def __init__(self, *a, **kw):
343        self.members = kw.pop('members')
344        super(Group, self).__init__(**kw)
345
346
347class ImplicitGroup(Group):
348    '''Provide a factory method for groups whose members are
349    not explicitly declared in the config files.'''
350
351    def __init__(self, *a, **kw):
352        super(ImplicitGroup, self).__init__(**kw)
353
354    def factory(self, objs):
355        '''Create group members.'''
356
357        factory = Everything.classmap(self.subclass, 'element')
358        for m in self.members:
359            args = {
360                'class': self.subclass,
361                self.subclass: 'element',
362                'name': m
363            }
364
365            obj = factory(configfile=self.configfile, **args)
366            add_unique(obj, objs)
367            obj.factory(objs)
368
369        super(ImplicitGroup, self).factory(objs)
370
371
372class GroupOfPaths(ImplicitGroup):
373    '''Path group config file directive.'''
374
375    def __init__(self, *a, **kw):
376        super(GroupOfPaths, self).__init__(**kw)
377
378    def setup(self, objs):
379        '''Resolve group members.'''
380
381        def map_member(x):
382            path = get_index(
383                objs, 'pathname', x['path'])
384            meta = get_index(
385                objs, 'meta', x['meta'])
386            return (path, meta)
387
388        self.members = map(
389            map_member,
390            self.members)
391
392        super(GroupOfPaths, self).setup(objs)
393
394
395class GroupOfProperties(ImplicitGroup):
396    '''Property group config file directive.'''
397
398    def __init__(self, *a, **kw):
399        self.type = kw.pop('type')
400        self.datatype = sdbusplus.property.Property(
401            name=kw.get('name'),
402            type=self.type).cppTypeName
403
404        super(GroupOfProperties, self).__init__(**kw)
405
406    def setup(self, objs):
407        '''Resolve group members.'''
408
409        def map_member(x):
410            iface = get_index(
411                objs, 'interface', x['interface'])
412            prop = get_index(
413                objs, 'propertyname', x['property'])
414            meta = get_index(
415                objs, 'meta', x['meta'])
416
417            return (iface, prop, meta)
418
419        self.members = map(
420            map_member,
421            self.members)
422
423        super(GroupOfProperties, self).setup(objs)
424
425
426class GroupOfInstances(ImplicitGroup):
427    '''A group of property instances.'''
428
429    def __init__(self, *a, **kw):
430        super(GroupOfInstances, self).__init__(**kw)
431
432    def setup(self, objs):
433        '''Resolve group members.'''
434
435        def map_member(x):
436            path = get_index(objs, 'pathname', x['path']['path'])
437            pathmeta = get_index(objs, 'meta', x['path']['meta'])
438            interface = get_index(
439                objs, 'interface', x['property']['interface'])
440            prop = get_index(objs, 'propertyname', x['property']['property'])
441            propmeta = get_index(objs, 'meta', x['property']['meta'])
442            instance = get_index(objs, 'instance', x)
443
444            return (path, pathmeta, interface, prop, propmeta, instance)
445
446        self.members = map(
447            map_member,
448            self.members)
449
450        super(GroupOfInstances, self).setup(objs)
451
452
453class HasPropertyIndex(ConfigEntry):
454    '''Handle config file directives that require an index to be
455    constructed.'''
456
457    def __init__(self, *a, **kw):
458        self.paths = kw.pop('paths')
459        self.properties = kw.pop('properties')
460        super(HasPropertyIndex, self).__init__(**kw)
461
462    def factory(self, objs):
463        '''Create a group of instances for this index.'''
464
465        members = []
466        path_group = get_index(
467            objs, 'pathgroup', self.paths, config=self.configfile)
468        property_group = get_index(
469            objs, 'propertygroup', self.properties, config=self.configfile)
470
471        for path in objs['pathgroup'][path_group].members:
472            for prop in objs['propertygroup'][property_group].members:
473                member = {
474                    'path': path,
475                    'property': prop,
476                }
477                members.append(member)
478
479        args = {
480            'members': members,
481            'class': 'instancegroup',
482            'instancegroup': 'instance',
483            'name': '{0} {1}'.format(self.paths, self.properties)
484        }
485
486        group = GroupOfInstances(configfile=self.configfile, **args)
487        add_unique(group, objs, config=self.configfile)
488        group.factory(objs)
489
490        super(HasPropertyIndex, self).factory(objs)
491
492    def setup(self, objs):
493        '''Resolve path, property, and instance groups.'''
494
495        self.instances = get_index(
496            objs,
497            'instancegroup',
498            '{0} {1}'.format(self.paths, self.properties),
499            config=self.configfile)
500        self.paths = get_index(
501            objs,
502            'pathgroup',
503            self.paths,
504            config=self.configfile)
505        self.properties = get_index(
506            objs,
507            'propertygroup',
508            self.properties,
509            config=self.configfile)
510        self.datatype = objs['propertygroup'][self.properties].datatype
511        self.type = objs['propertygroup'][self.properties].type
512
513        super(HasPropertyIndex, self).setup(objs)
514
515
516class PropertyWatch(HasPropertyIndex):
517    '''Handle the property watch config file directive.'''
518
519    def __init__(self, *a, **kw):
520        self.callback = kw.pop('callback', None)
521        super(PropertyWatch, self).__init__(**kw)
522
523    def setup(self, objs):
524        '''Resolve optional callback.'''
525
526        if self.callback:
527            self.callback = get_index(
528                objs,
529                'callback',
530                self.callback,
531                config=self.configfile)
532
533        super(PropertyWatch, self).setup(objs)
534
535
536class Callback(HasPropertyIndex):
537    '''Interface and common logic for callbacks.'''
538
539    def __init__(self, *a, **kw):
540        super(Callback, self).__init__(**kw)
541
542
543class ConditionCallback(ConfigEntry, Renderer):
544    '''Handle the journal callback config file directive.'''
545
546    def __init__(self, *a, **kw):
547        self.condition = kw.pop('condition')
548        self.instance = kw.pop('instance')
549        self.defer = kw.pop('defer', None)
550        super(ConditionCallback, self).__init__(**kw)
551
552    def factory(self, objs):
553        '''Create a graph instance for this callback.'''
554
555        args = {
556            'configfile': self.configfile,
557            'members': [self.instance],
558            'class': 'callbackgroup',
559            'callbackgroup': 'callback',
560            'name': [self.instance]
561        }
562
563        entry = CallbackGraphEntry(**args)
564        add_unique(entry, objs, config=self.configfile)
565
566        super(ConditionCallback, self).factory(objs)
567
568    def setup(self, objs):
569        '''Resolve condition and graph entry.'''
570
571        self.graph = get_index(
572            objs,
573            'callbackgroup',
574            [self.instance],
575            config=self.configfile)
576
577        self.condition = get_index(
578            objs,
579            'condition',
580            self.name,
581            config=self.configfile)
582
583        super(ConditionCallback, self).setup(objs)
584
585    def construct(self, loader, indent):
586        return self.render(
587            loader,
588            'conditional.mako.cpp',
589            c=self,
590            indent=indent)
591
592
593class Condition(HasPropertyIndex):
594    '''Interface and common logic for conditions.'''
595
596    def __init__(self, *a, **kw):
597        self.callback = kw.pop('callback')
598        self.defer = kw.pop('defer', None)
599        super(Condition, self).__init__(**kw)
600
601    def factory(self, objs):
602        '''Create a callback instance for this conditional.'''
603
604        args = {
605            'configfile': self.configfile,
606            'condition': self.name,
607            'class': 'callback',
608            'callback': 'conditional',
609            'instance': self.callback,
610            'name': self.name,
611            'defer': self.defer
612        }
613
614        callback = ConditionCallback(**args)
615        add_unique(callback, objs, config=self.configfile)
616        callback.factory(objs)
617
618        super(Condition, self).factory(objs)
619
620
621class CountCondition(Condition, Renderer):
622    '''Handle the count condition config file directive.'''
623
624    def __init__(self, *a, **kw):
625        self.countop = kw.pop('countop')
626        self.countbound = kw.pop('countbound')
627        self.op = kw.pop('op')
628        self.bound = kw.pop('bound')
629        super(CountCondition, self).__init__(**kw)
630
631    def setup(self, objs):
632        '''Resolve type.'''
633
634        super(CountCondition, self).setup(objs)
635        self.bound = TrivialArgument(
636            type=self.type,
637            value=self.bound)
638
639    def construct(self, loader, indent):
640        return self.render(
641            loader,
642            'count.mako.cpp',
643            c=self,
644            indent=indent)
645
646
647class Journal(Callback, Renderer):
648    '''Handle the journal callback config file directive.'''
649
650    def __init__(self, *a, **kw):
651        self.severity = kw.pop('severity')
652        self.message = kw.pop('message')
653        super(Journal, self).__init__(**kw)
654
655    def construct(self, loader, indent):
656        return self.render(
657            loader,
658            'journal.mako.cpp',
659            c=self,
660            indent=indent)
661
662
663class Method(ConfigEntry, Renderer):
664    '''Handle the method callback config file directive.'''
665
666    def __init__(self, *a, **kw):
667        self.service = kw.pop('service')
668        self.path = kw.pop('path')
669        self.interface = kw.pop('interface')
670        self.method = kw.pop('method')
671        self.args = [TrivialArgument(**x) for x in kw.pop('args', {})]
672        super(Method, self).__init__(**kw)
673
674    def factory(self, objs):
675        args = {
676            'class': 'interface',
677            'interface': 'element',
678            'name': self.service
679        }
680        add_unique(ConfigEntry(
681            configfile=self.configfile, **args), objs)
682
683        args = {
684            'class': 'pathname',
685            'pathname': 'element',
686            'name': self.path
687        }
688        add_unique(ConfigEntry(
689            configfile=self.configfile, **args), objs)
690
691        args = {
692            'class': 'interface',
693            'interface': 'element',
694            'name': self.interface
695        }
696        add_unique(ConfigEntry(
697            configfile=self.configfile, **args), objs)
698
699        args = {
700            'class': 'propertyname',
701            'propertyname': 'element',
702            'name': self.method
703        }
704        add_unique(ConfigEntry(
705            configfile=self.configfile, **args), objs)
706
707        super(Method, self).factory(objs)
708
709    def setup(self, objs):
710        '''Resolve elements.'''
711
712        self.service = get_index(
713            objs,
714            'interface',
715            self.service)
716
717        self.path = get_index(
718            objs,
719            'pathname',
720            self.path)
721
722        self.interface = get_index(
723            objs,
724            'interface',
725            self.interface)
726
727        self.method = get_index(
728            objs,
729            'propertyname',
730            self.method)
731
732        super(Method, self).setup(objs)
733
734    def construct(self, loader, indent):
735        return self.render(
736            loader,
737            'method.mako.cpp',
738            c=self,
739            indent=indent)
740
741
742class CallbackGraphEntry(Group):
743    '''An entry in a traversal list for groups of callbacks.'''
744
745    def __init__(self, *a, **kw):
746        super(CallbackGraphEntry, self).__init__(**kw)
747
748    def setup(self, objs):
749        '''Resolve group members.'''
750
751        def map_member(x):
752            return get_index(
753                objs, 'callback', x, config=self.configfile)
754
755        self.members = map(
756            map_member,
757            self.members)
758
759        super(CallbackGraphEntry, self).setup(objs)
760
761
762class GroupOfCallbacks(ConfigEntry, Renderer):
763    '''Handle the callback group config file directive.'''
764
765    def __init__(self, *a, **kw):
766        self.members = kw.pop('members')
767        super(GroupOfCallbacks, self).__init__(**kw)
768
769    def factory(self, objs):
770        '''Create a graph instance for this group of callbacks.'''
771
772        args = {
773            'configfile': self.configfile,
774            'members': self.members,
775            'class': 'callbackgroup',
776            'callbackgroup': 'callback',
777            'name': self.members
778        }
779
780        entry = CallbackGraphEntry(**args)
781        add_unique(entry, objs, config=self.configfile)
782
783        super(GroupOfCallbacks, self).factory(objs)
784
785    def setup(self, objs):
786        '''Resolve graph entry.'''
787
788        self.graph = get_index(
789            objs, 'callbackgroup', self.members, config=self.configfile)
790
791        super(GroupOfCallbacks, self).setup(objs)
792
793    def construct(self, loader, indent):
794        return self.render(
795            loader,
796            'callbackgroup.mako.cpp',
797            c=self,
798            indent=indent)
799
800
801class Everything(Renderer):
802    '''Parse/render entry point.'''
803
804    @staticmethod
805    def classmap(cls, sub=None):
806        '''Map render item class and subclass entries to the appropriate
807        handler methods.'''
808
809        class_map = {
810            'path': {
811                'element': Path,
812            },
813            'pathgroup': {
814                'path': GroupOfPaths,
815            },
816            'propertygroup': {
817                'property': GroupOfProperties,
818            },
819            'property': {
820                'element': Property,
821            },
822            'watch': {
823                'property': PropertyWatch,
824            },
825            'instance': {
826                'element': Instance,
827            },
828            'callback': {
829                'journal': Journal,
830                'group': GroupOfCallbacks,
831                'method': Method,
832            },
833            'condition': {
834                'count': CountCondition,
835            },
836        }
837
838        if cls not in class_map:
839            raise NotImplementedError('Unknown class: "{0}"'.format(cls))
840        if sub not in class_map[cls]:
841            raise NotImplementedError('Unknown {0} type: "{1}"'.format(
842                cls, sub))
843
844        return class_map[cls][sub]
845
846    @staticmethod
847    def load_one_yaml(path, fd, objs):
848        '''Parse a single YAML file.  Parsing occurs in three phases.
849        In the first phase a factory method associated with each
850        configuration file directive is invoked.  These factory
851        methods generate more factory methods.  In the second
852        phase the factory methods created in the first phase
853        are invoked.  In the last phase a callback is invoked on
854        each object created in phase two.  Typically the callback
855        resolves references to other configuration file directives.'''
856
857        factory_objs = {}
858        for x in yaml.safe_load(fd.read()) or {}:
859
860            # Create factory object for this config file directive.
861            cls = x['class']
862            sub = x.get(cls)
863            if cls == 'group':
864                cls = '{0}group'.format(sub)
865
866            factory = Everything.classmap(cls, sub)
867            obj = factory(configfile=path, **x)
868
869            # For a given class of directive, validate the file
870            # doesn't have any duplicate names (duplicates are
871            # ok across config files).
872            if exists(factory_objs, obj.cls, obj.name, config=path):
873                raise NotUniqueError(path, cls, obj.name)
874
875            factory_objs.setdefault(cls, []).append(obj)
876            objs.setdefault(cls, []).append(obj)
877
878        for cls, items in factory_objs.items():
879            for obj in items:
880                # Add objects for template consumption.
881                obj.factory(objs)
882
883    @staticmethod
884    def load(args):
885        '''Aggregate all the YAML in the input directory
886        into a single aggregate.'''
887
888        objs = {}
889        yaml_files = filter(
890            lambda x: x.endswith('.yaml'),
891            os.listdir(args.inputdir))
892
893        yaml_files.sort()
894
895        for x in yaml_files:
896            path = os.path.join(args.inputdir, x)
897            with open(path, 'r') as fd:
898                Everything.load_one_yaml(path, fd, objs)
899
900        # Configuration file directives reference each other via
901        # the name attribute; however, when rendered the reference
902        # is just an array index.
903        #
904        # At this point all objects have been created but references
905        # have not been resolved to array indicies.  Instruct objects
906        # to do that now.
907        for cls, items in objs.items():
908            for obj in items:
909                obj.setup(objs)
910
911        return Everything(**objs)
912
913    def __init__(self, *a, **kw):
914        self.pathmeta = kw.pop('path', [])
915        self.paths = kw.pop('pathname', [])
916        self.meta = kw.pop('meta', [])
917        self.pathgroups = kw.pop('pathgroup', [])
918        self.interfaces = kw.pop('interface', [])
919        self.properties = kw.pop('property', [])
920        self.propertynames = kw.pop('propertyname', [])
921        self.propertygroups = kw.pop('propertygroup', [])
922        self.instances = kw.pop('instance', [])
923        self.instancegroups = kw.pop('instancegroup', [])
924        self.watches = kw.pop('watch', [])
925        self.callbacks = kw.pop('callback', [])
926        self.callbackgroups = kw.pop('callbackgroup', [])
927        self.conditions = kw.pop('condition', [])
928
929        super(Everything, self).__init__(**kw)
930
931    def generate_cpp(self, loader):
932        '''Render the template with the provided data.'''
933        with open(args.output, 'w') as fd:
934            fd.write(
935                self.render(
936                    loader,
937                    args.template,
938                    meta=self.meta,
939                    properties=self.properties,
940                    propertynames=self.propertynames,
941                    interfaces=self.interfaces,
942                    paths=self.paths,
943                    pathmeta=self.pathmeta,
944                    pathgroups=self.pathgroups,
945                    propertygroups=self.propertygroups,
946                    instances=self.instances,
947                    watches=self.watches,
948                    instancegroups=self.instancegroups,
949                    callbacks=self.callbacks,
950                    callbackgroups=self.callbackgroups,
951                    conditions=self.conditions,
952                    indent=Indent()))
953
954if __name__ == '__main__':
955    script_dir = os.path.dirname(os.path.realpath(__file__))
956    valid_commands = {
957        'generate-cpp': 'generate_cpp',
958    }
959
960    parser = ArgumentParser(
961        description='Phosphor DBus Monitor (PDM) YAML '
962        'scanner and code generator.')
963
964    parser.add_argument(
965        "-o", "--out", dest="output",
966        default='generated.cpp',
967        help="Generated output file name and path.")
968    parser.add_argument(
969        '-t', '--template', dest='template',
970        default='generated.mako.hpp',
971        help='The top level template to render.')
972    parser.add_argument(
973        '-p', '--template-path', dest='template_search',
974        default=script_dir,
975        help='The space delimited mako template search path.')
976    parser.add_argument(
977        '-d', '--dir', dest='inputdir',
978        default=os.path.join(script_dir, 'example'),
979        help='Location of files to process.')
980    parser.add_argument(
981        'command', metavar='COMMAND', type=str,
982        choices=valid_commands.keys(),
983        help='%s.' % " | ".join(valid_commands.keys()))
984
985    args = parser.parse_args()
986
987    if sys.version_info < (3, 0):
988        lookup = mako.lookup.TemplateLookup(
989            directories=args.template_search.split(),
990            disable_unicode=True)
991    else:
992        lookup = mako.lookup.TemplateLookup(
993            directories=args.template_search.split())
994    try:
995        function = getattr(
996            Everything.load(args),
997            valid_commands[args.command])
998        function(lookup)
999    except InvalidConfigError as e:
1000        sys.stdout.write('{0}: {1}\n\n'.format(e.config, e.msg))
1001        raise
1002