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        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.datatype = sdbusplus.property.Property(
400            name=kw.get('name'),
401            type=kw.pop('type')).cppTypeName
402
403        super(GroupOfProperties, self).__init__(**kw)
404
405    def setup(self, objs):
406        '''Resolve group members.'''
407
408        def map_member(x):
409            iface = get_index(
410                objs, 'interface', x['interface'])
411            prop = get_index(
412                objs, 'propertyname', x['property'])
413            meta = get_index(
414                objs, 'meta', x['meta'])
415
416            return (iface, prop, meta)
417
418        self.members = map(
419            map_member,
420            self.members)
421
422        super(GroupOfProperties, self).setup(objs)
423
424
425class GroupOfInstances(ImplicitGroup):
426    '''A group of property instances.'''
427
428    def __init__(self, *a, **kw):
429        super(GroupOfInstances, self).__init__(**kw)
430
431    def setup(self, objs):
432        '''Resolve group members.'''
433
434        def map_member(x):
435            path = get_index(objs, 'pathname', x['path']['path'])
436            pathmeta = get_index(objs, 'meta', x['path']['meta'])
437            interface = get_index(
438                objs, 'interface', x['property']['interface'])
439            prop = get_index(objs, 'propertyname', x['property']['property'])
440            propmeta = get_index(objs, 'meta', x['property']['meta'])
441            instance = get_index(objs, 'instance', x)
442
443            return (path, pathmeta, interface, prop, propmeta, instance)
444
445        self.members = map(
446            map_member,
447            self.members)
448
449        super(GroupOfInstances, self).setup(objs)
450
451
452class HasPropertyIndex(ConfigEntry):
453    '''Handle config file directives that require an index to be
454    constructed.'''
455
456    def __init__(self, *a, **kw):
457        self.paths = kw.pop('paths')
458        self.properties = kw.pop('properties')
459        super(HasPropertyIndex, self).__init__(**kw)
460
461    def factory(self, objs):
462        '''Create a group of instances for this index.'''
463
464        members = []
465        path_group = get_index(
466            objs, 'pathgroup', self.paths, config=self.configfile)
467        property_group = get_index(
468            objs, 'propertygroup', self.properties, config=self.configfile)
469
470        for path in objs['pathgroup'][path_group].members:
471            for prop in objs['propertygroup'][property_group].members:
472                member = {
473                    'path': path,
474                    'property': prop,
475                }
476                members.append(member)
477
478        args = {
479            'members': members,
480            'class': 'instancegroup',
481            'instancegroup': 'instance',
482            'name': '{0} {1}'.format(self.paths, self.properties)
483        }
484
485        group = GroupOfInstances(configfile=self.configfile, **args)
486        add_unique(group, objs, config=self.configfile)
487        group.factory(objs)
488
489        super(HasPropertyIndex, self).factory(objs)
490
491    def setup(self, objs):
492        '''Resolve path, property, and instance groups.'''
493
494        self.instances = get_index(
495            objs,
496            'instancegroup',
497            '{0} {1}'.format(self.paths, self.properties),
498            config=self.configfile)
499        self.paths = get_index(
500            objs,
501            'pathgroup',
502            self.paths,
503            config=self.configfile)
504        self.properties = get_index(
505            objs,
506            'propertygroup',
507            self.properties,
508            config=self.configfile)
509        self.datatype = objs['propertygroup'][self.properties].datatype
510
511        super(HasPropertyIndex, self).setup(objs)
512
513
514class PropertyWatch(HasPropertyIndex):
515    '''Handle the property watch config file directive.'''
516
517    def __init__(self, *a, **kw):
518        self.callback = kw.pop('callback', None)
519        super(PropertyWatch, self).__init__(**kw)
520
521    def setup(self, objs):
522        '''Resolve optional callback.'''
523
524        if self.callback:
525            self.callback = get_index(
526                objs,
527                'callback',
528                self.callback,
529                config=self.configfile)
530
531        super(PropertyWatch, self).setup(objs)
532
533
534class Callback(HasPropertyIndex):
535    '''Interface and common logic for callbacks.'''
536
537    def __init__(self, *a, **kw):
538        super(Callback, self).__init__(**kw)
539
540
541class ConditionCallback(ConfigEntry, Renderer):
542    '''Handle the journal callback config file directive.'''
543
544    def __init__(self, *a, **kw):
545        self.condition = kw.pop('condition')
546        self.instance = kw.pop('instance')
547        self.defer = kw.pop('defer', None)
548        super(ConditionCallback, self).__init__(**kw)
549
550    def factory(self, objs):
551        '''Create a graph instance for this callback.'''
552
553        args = {
554            'configfile': self.configfile,
555            'members': [self.instance],
556            'class': 'callbackgroup',
557            'callbackgroup': 'callback',
558            'name': [self.instance]
559        }
560
561        entry = CallbackGraphEntry(**args)
562        add_unique(entry, objs, config=self.configfile)
563
564        super(ConditionCallback, self).factory(objs)
565
566    def setup(self, objs):
567        '''Resolve condition and graph entry.'''
568
569        self.graph = get_index(
570            objs,
571            'callbackgroup',
572            [self.instance],
573            config=self.configfile)
574
575        self.condition = get_index(
576            objs,
577            'condition',
578            self.name,
579            config=self.configfile)
580
581        super(ConditionCallback, self).setup(objs)
582
583    def construct(self, loader, indent):
584        return self.render(
585            loader,
586            'conditional.mako.cpp',
587            c=self,
588            indent=indent)
589
590
591class Condition(HasPropertyIndex):
592    '''Interface and common logic for conditions.'''
593
594    def __init__(self, *a, **kw):
595        self.callback = kw.pop('callback')
596        self.defer = kw.pop('defer', None)
597        super(Condition, self).__init__(**kw)
598
599    def factory(self, objs):
600        '''Create a callback instance for this conditional.'''
601
602        args = {
603            'configfile': self.configfile,
604            'condition': self.name,
605            'class': 'callback',
606            'callback': 'conditional',
607            'instance': self.callback,
608            'name': self.name,
609            'defer': self.defer
610        }
611
612        callback = ConditionCallback(**args)
613        add_unique(callback, objs, config=self.configfile)
614        callback.factory(objs)
615
616        super(Condition, self).factory(objs)
617
618
619class CountCondition(Condition, Renderer):
620    '''Handle the count condition config file directive.'''
621
622    def __init__(self, *a, **kw):
623        self.countop = kw.pop('countop')
624        self.countbound = kw.pop('countbound')
625        self.op = kw.pop('op')
626        self.bound = kw.pop('bound')
627        super(CountCondition, self).__init__(**kw)
628
629    def construct(self, loader, indent):
630        return self.render(
631            loader,
632            'count.mako.cpp',
633            c=self,
634            indent=indent)
635
636
637class Journal(Callback, Renderer):
638    '''Handle the journal callback config file directive.'''
639
640    def __init__(self, *a, **kw):
641        self.severity = kw.pop('severity')
642        self.message = kw.pop('message')
643        super(Journal, self).__init__(**kw)
644
645    def construct(self, loader, indent):
646        return self.render(
647            loader,
648            'journal.mako.cpp',
649            c=self,
650            indent=indent)
651
652
653class Method(ConfigEntry, Renderer):
654    '''Handle the method callback config file directive.'''
655
656    def __init__(self, *a, **kw):
657        self.service = kw.pop('service')
658        self.path = kw.pop('path')
659        self.interface = kw.pop('interface')
660        self.method = kw.pop('method')
661        self.args = [TrivialArgument(**x) for x in kw.pop('args', {})]
662        super(Method, self).__init__(**kw)
663
664    def factory(self, objs):
665        args = {
666            'class': 'interface',
667            'interface': 'element',
668            'name': self.service
669        }
670        add_unique(ConfigEntry(
671            configfile=self.configfile, **args), objs)
672
673        args = {
674            'class': 'pathname',
675            'pathname': 'element',
676            'name': self.path
677        }
678        add_unique(ConfigEntry(
679            configfile=self.configfile, **args), objs)
680
681        args = {
682            'class': 'interface',
683            'interface': 'element',
684            'name': self.interface
685        }
686        add_unique(ConfigEntry(
687            configfile=self.configfile, **args), objs)
688
689        args = {
690            'class': 'propertyname',
691            'propertyname': 'element',
692            'name': self.method
693        }
694        add_unique(ConfigEntry(
695            configfile=self.configfile, **args), objs)
696
697        super(Method, self).factory(objs)
698
699    def setup(self, objs):
700        '''Resolve elements.'''
701
702        self.service = get_index(
703            objs,
704            'interface',
705            self.service)
706
707        self.path = get_index(
708            objs,
709            'pathname',
710            self.path)
711
712        self.interface = get_index(
713            objs,
714            'interface',
715            self.interface)
716
717        self.method = get_index(
718            objs,
719            'propertyname',
720            self.method)
721
722        super(Method, self).setup(objs)
723
724    def construct(self, loader, indent):
725        return self.render(
726            loader,
727            'method.mako.cpp',
728            c=self,
729            indent=indent)
730
731
732class CallbackGraphEntry(Group):
733    '''An entry in a traversal list for groups of callbacks.'''
734
735    def __init__(self, *a, **kw):
736        super(CallbackGraphEntry, self).__init__(**kw)
737
738    def setup(self, objs):
739        '''Resolve group members.'''
740
741        def map_member(x):
742            return get_index(
743                objs, 'callback', x, config=self.configfile)
744
745        self.members = map(
746            map_member,
747            self.members)
748
749        super(CallbackGraphEntry, self).setup(objs)
750
751
752class GroupOfCallbacks(ConfigEntry, Renderer):
753    '''Handle the callback group config file directive.'''
754
755    def __init__(self, *a, **kw):
756        self.members = kw.pop('members')
757        super(GroupOfCallbacks, self).__init__(**kw)
758
759    def factory(self, objs):
760        '''Create a graph instance for this group of callbacks.'''
761
762        args = {
763            'configfile': self.configfile,
764            'members': self.members,
765            'class': 'callbackgroup',
766            'callbackgroup': 'callback',
767            'name': self.members
768        }
769
770        entry = CallbackGraphEntry(**args)
771        add_unique(entry, objs, config=self.configfile)
772
773        super(GroupOfCallbacks, self).factory(objs)
774
775    def setup(self, objs):
776        '''Resolve graph entry.'''
777
778        self.graph = get_index(
779            objs, 'callbackgroup', self.members, config=self.configfile)
780
781        super(GroupOfCallbacks, self).setup(objs)
782
783    def construct(self, loader, indent):
784        return self.render(
785            loader,
786            'callbackgroup.mako.cpp',
787            c=self,
788            indent=indent)
789
790
791class Everything(Renderer):
792    '''Parse/render entry point.'''
793
794    @staticmethod
795    def classmap(cls, sub=None):
796        '''Map render item class and subclass entries to the appropriate
797        handler methods.'''
798
799        class_map = {
800            'path': {
801                'element': Path,
802            },
803            'pathgroup': {
804                'path': GroupOfPaths,
805            },
806            'propertygroup': {
807                'property': GroupOfProperties,
808            },
809            'property': {
810                'element': Property,
811            },
812            'watch': {
813                'property': PropertyWatch,
814            },
815            'instance': {
816                'element': Instance,
817            },
818            'callback': {
819                'journal': Journal,
820                'group': GroupOfCallbacks,
821                'method': Method,
822            },
823            'condition': {
824                'count': CountCondition,
825            },
826        }
827
828        if cls not in class_map:
829            raise NotImplementedError('Unknown class: "{0}"'.format(cls))
830        if sub not in class_map[cls]:
831            raise NotImplementedError('Unknown {0} type: "{1}"'.format(
832                cls, sub))
833
834        return class_map[cls][sub]
835
836    @staticmethod
837    def load_one_yaml(path, fd, objs):
838        '''Parse a single YAML file.  Parsing occurs in three phases.
839        In the first phase a factory method associated with each
840        configuration file directive is invoked.  These factory
841        methods generate more factory methods.  In the second
842        phase the factory methods created in the first phase
843        are invoked.  In the last phase a callback is invoked on
844        each object created in phase two.  Typically the callback
845        resolves references to other configuration file directives.'''
846
847        factory_objs = {}
848        for x in yaml.safe_load(fd.read()) or {}:
849
850            # Create factory object for this config file directive.
851            cls = x['class']
852            sub = x.get(cls)
853            if cls == 'group':
854                cls = '{0}group'.format(sub)
855
856            factory = Everything.classmap(cls, sub)
857            obj = factory(configfile=path, **x)
858
859            # For a given class of directive, validate the file
860            # doesn't have any duplicate names (duplicates are
861            # ok across config files).
862            if exists(factory_objs, obj.cls, obj.name, config=path):
863                raise NotUniqueError(path, cls, obj.name)
864
865            factory_objs.setdefault(cls, []).append(obj)
866            objs.setdefault(cls, []).append(obj)
867
868        for cls, items in factory_objs.items():
869            for obj in items:
870                # Add objects for template consumption.
871                obj.factory(objs)
872
873    @staticmethod
874    def load(args):
875        '''Aggregate all the YAML in the input directory
876        into a single aggregate.'''
877
878        objs = {}
879        yaml_files = filter(
880            lambda x: x.endswith('.yaml'),
881            os.listdir(args.inputdir))
882
883        yaml_files.sort()
884
885        for x in yaml_files:
886            path = os.path.join(args.inputdir, x)
887            with open(path, 'r') as fd:
888                Everything.load_one_yaml(path, fd, objs)
889
890        # Configuration file directives reference each other via
891        # the name attribute; however, when rendered the reference
892        # is just an array index.
893        #
894        # At this point all objects have been created but references
895        # have not been resolved to array indicies.  Instruct objects
896        # to do that now.
897        for cls, items in objs.items():
898            for obj in items:
899                obj.setup(objs)
900
901        return Everything(**objs)
902
903    def __init__(self, *a, **kw):
904        self.pathmeta = kw.pop('path', [])
905        self.paths = kw.pop('pathname', [])
906        self.meta = kw.pop('meta', [])
907        self.pathgroups = kw.pop('pathgroup', [])
908        self.interfaces = kw.pop('interface', [])
909        self.properties = kw.pop('property', [])
910        self.propertynames = kw.pop('propertyname', [])
911        self.propertygroups = kw.pop('propertygroup', [])
912        self.instances = kw.pop('instance', [])
913        self.instancegroups = kw.pop('instancegroup', [])
914        self.watches = kw.pop('watch', [])
915        self.callbacks = kw.pop('callback', [])
916        self.callbackgroups = kw.pop('callbackgroup', [])
917        self.conditions = kw.pop('condition', [])
918
919        super(Everything, self).__init__(**kw)
920
921    def generate_cpp(self, loader):
922        '''Render the template with the provided data.'''
923        with open(args.output, 'w') as fd:
924            fd.write(
925                self.render(
926                    loader,
927                    args.template,
928                    meta=self.meta,
929                    properties=self.properties,
930                    propertynames=self.propertynames,
931                    interfaces=self.interfaces,
932                    paths=self.paths,
933                    pathmeta=self.pathmeta,
934                    pathgroups=self.pathgroups,
935                    propertygroups=self.propertygroups,
936                    instances=self.instances,
937                    watches=self.watches,
938                    instancegroups=self.instancegroups,
939                    callbacks=self.callbacks,
940                    callbackgroups=self.callbackgroups,
941                    conditions=self.conditions,
942                    indent=Indent()))
943
944if __name__ == '__main__':
945    script_dir = os.path.dirname(os.path.realpath(__file__))
946    valid_commands = {
947        'generate-cpp': 'generate_cpp',
948    }
949
950    parser = ArgumentParser(
951        description='Phosphor DBus Monitor (PDM) YAML '
952        'scanner and code generator.')
953
954    parser.add_argument(
955        "-o", "--out", dest="output",
956        default='generated.cpp',
957        help="Generated output file name and path.")
958    parser.add_argument(
959        '-t', '--template', dest='template',
960        default='generated.mako.hpp',
961        help='The top level template to render.')
962    parser.add_argument(
963        '-p', '--template-path', dest='template_search',
964        default=script_dir,
965        help='The space delimited mako template search path.')
966    parser.add_argument(
967        '-d', '--dir', dest='inputdir',
968        default=os.path.join(script_dir, 'example'),
969        help='Location of files to process.')
970    parser.add_argument(
971        'command', metavar='COMMAND', type=str,
972        choices=valid_commands.keys(),
973        help='%s.' % " | ".join(valid_commands.keys()))
974
975    args = parser.parse_args()
976
977    if sys.version_info < (3, 0):
978        lookup = mako.lookup.TemplateLookup(
979            directories=args.template_search.split(),
980            disable_unicode=True)
981    else:
982        lookup = mako.lookup.TemplateLookup(
983            directories=args.template_search.split())
984    try:
985        function = getattr(
986            Everything.load(args),
987            valid_commands[args.command])
988        function(lookup)
989    except InvalidConfigError as e:
990        sys.stdout.write('{0}: {1}\n\n'.format(e.config, e.msg))
991        raise
992