blob: be66bb0846f4768df59929c91977a35666fe3a1d [file] [log] [blame]
Brad Bishopbf066a62016-10-19 08:09:44 -04001#!/usr/bin/env python
2
Brad Bishop22cfbe62016-11-30 13:25:10 -05003'''Phosphor Inventory Manager 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
Brad Bishopbf066a62016-10-19 08:09:44 -040018import sys
19import os
Brad Bishopbf066a62016-10-19 08:09:44 -040020import argparse
Brad Bishopcfb3c892016-11-12 11:43:37 -050021import subprocess
Brad Bishop22cfbe62016-11-30 13:25:10 -050022import yaml
23import mako.lookup
24import sdbusplus.property
25from sdbusplus.namedelement import NamedElement
26from sdbusplus.renderer import Renderer
Brad Bishopbf066a62016-10-19 08:09:44 -040027
28
Brad Bishop9b5a12f2017-01-21 14:42:11 -050029def cppTypeName(yaml_type):
30 ''' Convert yaml types to cpp types.'''
31 return sdbusplus.property.Property(type=yaml_type).cppTypeName
32
33
Brad Bishop22cfbe62016-11-30 13:25:10 -050034class Interface(list):
35 '''Provide various interface transformations.'''
36
37 def __init__(self, iface):
38 super(Interface, self).__init__(iface.split('.'))
39
40 def namespace(self):
41 '''Represent as an sdbusplus namespace.'''
42 return '::'.join(['sdbusplus'] + self[:-1] + ['server', self[-1]])
43
44 def header(self):
45 '''Represent as an sdbusplus server binding header.'''
46 return os.sep.join(self + ['server.hpp'])
47
48 def __str__(self):
49 return '.'.join(self)
Brad Bishopbf066a62016-10-19 08:09:44 -040050
Brad Bishopcfb3c892016-11-12 11:43:37 -050051
Brad Bishop9b5a12f2017-01-21 14:42:11 -050052class Indent(object):
53 '''Help templates be depth agnostic.'''
54
55 def __init__(self, depth=0):
56 self.depth = depth
57
58 def __add__(self, depth):
59 return Indent(self.depth + depth)
60
61 def __call__(self, depth):
62 '''Render an indent at the current depth plus depth.'''
63 return 4*' '*(depth + self.depth)
64
65
66class Template(NamedElement):
67 '''Associate a template name with its namespace.'''
68
69 def __init__(self, **kw):
70 self.namespace = kw.pop('namespace')
71 super(Template, self).__init__(**kw)
72
73 def qualified(self):
74 return '::'.join(self.namespace + [self.name])
75
76
77class Quote(object):
78 '''Decorate an argument by quoting it.'''
79
80 def __call__(self, arg):
81 return '"{0}"'.format(arg)
82
83
84class Cast(object):
85 '''Decorate an argument by casting it.'''
86
87 def __init__(self, cast, target):
88 '''cast is the cast type (static, const, etc...).
89 target is the cast target type.'''
90 self.cast = cast
91 self.target = target
92
93 def __call__(self, arg):
94 return '{0}_cast<{1}>({2})'.format(self.cast, self.target, arg)
95
96
97class Literal(object):
98 '''Decorate an argument with a literal operator.'''
99
100 literals = {
101 'string': 's',
102 'int64': 'll',
103 'uint64': 'ull'
104 }
105
106 def __init__(self, type):
107 self.type = type
108
109 def __call__(self, arg):
110 literal = self.literals.get(self.type)
111
112 if literal:
113 return '{0}{1}'.format(arg, literal)
114
115 return arg
116
117
Brad Bishop75800cf2017-01-21 15:24:18 -0500118class Argument(NamedElement, Renderer):
119 '''Define argument type inteface.'''
120
121 def __init__(self, **kw):
122 self.type = kw.pop('type', None)
123 super(Argument, self).__init__(**kw)
124
125 def argument(self, loader, indent):
126 raise NotImplementedError
127
128
129class TrivialArgument(Argument):
130 '''Non-array type arguments.'''
Brad Bishop14a9fe52016-11-12 12:51:26 -0500131
Brad Bishop22cfbe62016-11-30 13:25:10 -0500132 def __init__(self, **kw):
133 self.value = kw.pop('value')
Brad Bishop75800cf2017-01-21 15:24:18 -0500134 self.decorators = kw.pop('decorators', [])
135 if kw.get('type', None) == 'string':
136 self.decorators.insert(0, Quote())
137
Brad Bishop75800cf2017-01-21 15:24:18 -0500138 super(TrivialArgument, self).__init__(**kw)
139
140 def argument(self, loader, indent):
141 a = str(self.value)
142 for d in self.decorators:
143 a = d(a)
144
145 return a
Brad Bishop14a9fe52016-11-12 12:51:26 -0500146
Brad Bishop14a9fe52016-11-12 12:51:26 -0500147
Brad Bishop9b5a12f2017-01-21 14:42:11 -0500148class InitializerList(Argument):
149 '''Initializer list arguments.'''
150
151 def __init__(self, **kw):
152 self.values = kw.pop('values')
153 super(InitializerList, self).__init__(**kw)
154
155 def argument(self, loader, indent):
156 return self.render(
157 loader,
158 'argument.mako.cpp',
159 arg=self,
160 indent=indent)
161
162
Brad Bishop75800cf2017-01-21 15:24:18 -0500163class DbusSignature(Argument):
164 '''DBus signature arguments.'''
165
166 def __init__(self, **kw):
167 self.sig = {x: y for x, y in kw.iteritems()}
168 kw.clear()
169 super(DbusSignature, self).__init__(**kw)
170
171 def argument(self, loader, indent):
172 return self.render(
173 loader,
174 'signature.mako.cpp',
175 signature=self,
176 indent=indent)
177
178
Brad Bishopc93bcc92017-01-21 16:23:39 -0500179class MethodCall(Argument):
Brad Bishop22cfbe62016-11-30 13:25:10 -0500180 '''Render syntatically correct c++ method calls.'''
181
182 def __init__(self, **kw):
183 self.namespace = kw.pop('namespace', [])
Brad Bishopc93bcc92017-01-21 16:23:39 -0500184 self.templates = kw.pop('templates', [])
185 self.args = kw.pop('args', [])
Brad Bishop22cfbe62016-11-30 13:25:10 -0500186 super(MethodCall, self).__init__(**kw)
187
Brad Bishopcab2bdd2017-01-21 15:00:54 -0500188 def call(self, loader, indent):
189 return self.render(
190 loader,
191 'method.mako.cpp',
192 method=self,
193 indent=indent)
194
195 def argument(self, loader, indent):
196 return self.call(loader, indent)
197
Brad Bishop14a9fe52016-11-12 12:51:26 -0500198
Brad Bishop9b5a12f2017-01-21 14:42:11 -0500199class Vector(MethodCall):
200 '''Convenience type for vectors.'''
201
202 def __init__(self, **kw):
203 kw['name'] = 'vector'
204 kw['namespace'] = ['std']
205 kw['args'] = [InitializerList(values=kw.pop('args'))]
206 super(Vector, self).__init__(**kw)
207
208
209class Wrapper(MethodCall):
210 '''Convenience type for functions that wrap other functions.'''
211
212 def __init__(self, **kw):
213 m = MethodCall(
214 name=kw.pop('name'),
215 namespace=kw.pop('namespace', []),
216 templates=kw.pop('templates', []),
217 args=kw.pop('args', []))
218
219 kw['name'] = kw.pop('wrapper_name')
220 kw['namespace'] = kw.pop('wrapper_namespace', [])
221 kw['args'] = [m]
222 super(Wrapper, self).__init__(**kw)
223
224
Brad Bishopc93bcc92017-01-21 16:23:39 -0500225class Filter(Wrapper):
226 '''Convenience type for filters'''
Brad Bishopbf066a62016-10-19 08:09:44 -0400227
Brad Bishop22cfbe62016-11-30 13:25:10 -0500228 def __init__(self, **kw):
Brad Bishopc93bcc92017-01-21 16:23:39 -0500229 kw['wrapper_name'] = 'make_filter'
Brad Bishop12f8a3c2017-02-09 00:02:00 -0500230 kw['wrapper_namespace'] = []
Brad Bishop22cfbe62016-11-30 13:25:10 -0500231 kw['namespace'] = ['filters']
232 super(Filter, self).__init__(**kw)
Brad Bishopbf066a62016-10-19 08:09:44 -0400233
Brad Bishop0a6a4792016-11-12 12:10:07 -0500234
Brad Bishopc93bcc92017-01-21 16:23:39 -0500235class Action(Wrapper):
236 '''Convenience type for actions'''
Brad Bishop561a5652016-10-26 21:13:32 -0500237
Brad Bishop22cfbe62016-11-30 13:25:10 -0500238 def __init__(self, **kw):
Brad Bishopc93bcc92017-01-21 16:23:39 -0500239 kw['wrapper_name'] = 'make_action'
Brad Bishop12f8a3c2017-02-09 00:02:00 -0500240 kw['wrapper_namespace'] = []
Brad Bishop22cfbe62016-11-30 13:25:10 -0500241 kw['namespace'] = ['actions']
242 super(Action, self).__init__(**kw)
Brad Bishop92665b22016-10-26 20:51:16 -0500243
Brad Bishopcfb3c892016-11-12 11:43:37 -0500244
Brad Bishopdb92c282017-01-21 23:44:28 -0500245class CreateObjects(Action):
246 '''Assemble a createObjects action.'''
247
248 def __init__(self, **kw):
249 objs = []
250
251 for path, interfaces in kw.pop('objs').iteritems():
252 key_o = TrivialArgument(
253 value=path,
254 type='string',
255 decorators=[Literal('string')])
256 value_i = []
257
258 for interface, properties, in interfaces.iteritems():
259 key_i = TrivialArgument(value=interface, type='string')
260 value_p = []
261
262 for prop, value in properties.iteritems():
263 key_p = TrivialArgument(value=prop, type='string')
264 value_v = TrivialArgument(
265 decorators=[Literal(value.get('type', None))],
266 **value)
267 value_p.append(InitializerList(values=[key_p, value_v]))
268
269 value_p = InitializerList(values=value_p)
270 value_i.append(InitializerList(values=[key_i, value_p]))
271
272 value_i = InitializerList(values=value_i)
273 objs.append(InitializerList(values=[key_o, value_i]))
274
275 kw['args'] = [InitializerList(values=objs)]
276 super(CreateObjects, self).__init__(**kw)
277
278
Brad Bishop7b7e7122017-01-21 21:21:46 -0500279class DestroyObjects(Action):
Brad Bishopc93bcc92017-01-21 16:23:39 -0500280 '''Assemble a destroyObject action.'''
Brad Bishop22cfbe62016-11-30 13:25:10 -0500281
282 def __init__(self, **kw):
Brad Bishop7b7e7122017-01-21 21:21:46 -0500283 values = [{'value': x, 'type': 'string'} for x in kw.pop('paths')]
284 args = [InitializerList(
285 values=[TrivialArgument(**x) for x in values])]
Brad Bishopc93bcc92017-01-21 16:23:39 -0500286 kw['args'] = args
Brad Bishop7b7e7122017-01-21 21:21:46 -0500287 super(DestroyObjects, self).__init__(**kw)
Brad Bishop22cfbe62016-11-30 13:25:10 -0500288
289
Brad Bishope2e402f2016-11-30 18:00:17 -0500290class SetProperty(Action):
Brad Bishopc93bcc92017-01-21 16:23:39 -0500291 '''Assemble a setProperty action.'''
Brad Bishope2e402f2016-11-30 18:00:17 -0500292
293 def __init__(self, **kw):
Brad Bishopc93bcc92017-01-21 16:23:39 -0500294 args = []
295
296 value = kw.pop('value')
297 prop = kw.pop('property')
298 iface = kw.pop('interface')
299 iface = Interface(iface)
300 namespace = iface.namespace().split('::')[:-1]
301 name = iface[-1]
302 t = Template(namespace=namespace, name=iface[-1])
303
Brad Bishope2e402f2016-11-30 18:00:17 -0500304 member = '&%s' % '::'.join(
Brad Bishopc93bcc92017-01-21 16:23:39 -0500305 namespace + [name, NamedElement(name=prop).camelCase])
306 member_type = cppTypeName(value['type'])
307 member_cast = '{0} ({1}::*)({0})'.format(member_type, t.qualified())
Brad Bishope2e402f2016-11-30 18:00:17 -0500308
Brad Bishop02ca0212017-01-28 23:25:58 -0500309 paths = [{'value': x, 'type': 'string'} for x in kw.pop('paths')]
310 args.append(InitializerList(
311 values=[TrivialArgument(**x) for x in paths]))
312
Brad Bishopc93bcc92017-01-21 16:23:39 -0500313 args.append(TrivialArgument(value=str(iface), type='string'))
314 args.append(TrivialArgument(
315 value=member, decorators=[Cast('static', member_cast)]))
316 args.append(TrivialArgument(**value))
Brad Bishope2e402f2016-11-30 18:00:17 -0500317
Brad Bishopc93bcc92017-01-21 16:23:39 -0500318 kw['templates'] = [Template(name=name, namespace=namespace)]
319 kw['args'] = args
Brad Bishope2e402f2016-11-30 18:00:17 -0500320 super(SetProperty, self).__init__(**kw)
321
322
Brad Bishop22cfbe62016-11-30 13:25:10 -0500323class PropertyChanged(Filter):
Brad Bishopc93bcc92017-01-21 16:23:39 -0500324 '''Assemble a propertyChanged filter.'''
Brad Bishop22cfbe62016-11-30 13:25:10 -0500325
326 def __init__(self, **kw):
Brad Bishopc93bcc92017-01-21 16:23:39 -0500327 args = []
328 args.append(TrivialArgument(value=kw.pop('interface'), type='string'))
329 args.append(TrivialArgument(value=kw.pop('property'), type='string'))
330 args.append(TrivialArgument(
331 decorators=[
332 Literal(kw['value'].get('type', None))], **kw.pop('value')))
333 kw['args'] = args
Brad Bishop22cfbe62016-11-30 13:25:10 -0500334 super(PropertyChanged, self).__init__(**kw)
335
336
Brad Bishop040e18b2017-01-21 22:04:00 -0500337class PropertyIs(Filter):
338 '''Assemble a propertyIs filter.'''
339
340 def __init__(self, **kw):
341 args = []
342 args.append(TrivialArgument(value=kw.pop('path'), type='string'))
343 args.append(TrivialArgument(value=kw.pop('interface'), type='string'))
344 args.append(TrivialArgument(value=kw.pop('property'), type='string'))
345 args.append(TrivialArgument(
346 decorators=[
347 Literal(kw['value'].get('type', None))], **kw.pop('value')))
348
349 service = kw.pop('service', None)
350 if service:
351 args.append(TrivialArgument(value=service, type='string'))
352
353 kw['args'] = args
354 super(PropertyIs, self).__init__(**kw)
355
356
Brad Bishopc93bcc92017-01-21 16:23:39 -0500357class Event(MethodCall):
358 '''Assemble an inventory manager event.'''
Brad Bishop22cfbe62016-11-30 13:25:10 -0500359
360 action_map = {
Brad Bishop7b7e7122017-01-21 21:21:46 -0500361 'destroyObjects': DestroyObjects,
Brad Bishopdb92c282017-01-21 23:44:28 -0500362 'createObjects': CreateObjects,
Brad Bishope2e402f2016-11-30 18:00:17 -0500363 'setProperty': SetProperty,
Brad Bishopcfb3c892016-11-12 11:43:37 -0500364 }
365
Brad Bishopc93bcc92017-01-21 16:23:39 -0500366 filter_map = {
367 'propertyChangedTo': PropertyChanged,
Brad Bishop040e18b2017-01-21 22:04:00 -0500368 'propertyIs': PropertyIs,
Brad Bishopc93bcc92017-01-21 16:23:39 -0500369 }
370
Brad Bishop22cfbe62016-11-30 13:25:10 -0500371 def __init__(self, **kw):
Brad Bishopc93bcc92017-01-21 16:23:39 -0500372 self.summary = kw.pop('name')
373
374 filters = [
375 self.filter_map[x['name']](**x) for x in kw.pop('filters', [])]
Brad Bishop064c94a2017-01-21 21:33:30 -0500376 filters = Vector(
Brad Bishop12f8a3c2017-02-09 00:02:00 -0500377 templates=[Template(name='Filter', namespace=[])],
Brad Bishop064c94a2017-01-21 21:33:30 -0500378 args=filters)
Brad Bishopc93bcc92017-01-21 16:23:39 -0500379
380 event = MethodCall(
381 name='make_shared',
382 namespace=['std'],
383 templates=[Template(
384 name=kw.pop('event'),
385 namespace=kw.pop('event_namespace', []))],
Brad Bishop064c94a2017-01-21 21:33:30 -0500386 args=kw.pop('event_args', []) + [filters])
Brad Bishopc93bcc92017-01-21 16:23:39 -0500387
388 events = Vector(
Brad Bishop12f8a3c2017-02-09 00:02:00 -0500389 templates=[Template(name='EventBasePtr', namespace=[])],
Brad Bishopc93bcc92017-01-21 16:23:39 -0500390 args=[event])
391
Brad Bishop12f8a3c2017-02-09 00:02:00 -0500392 action_type = Template(name='Action', namespace=[])
Brad Bishopc93bcc92017-01-21 16:23:39 -0500393 action_args = [
394 self.action_map[x['name']](**x) for x in kw.pop('actions', [])]
395 actions = Vector(
396 templates=[action_type],
397 args=action_args)
398
399 kw['name'] = 'make_tuple'
400 kw['namespace'] = ['std']
401 kw['args'] = [events, actions]
Brad Bishop22cfbe62016-11-30 13:25:10 -0500402 super(Event, self).__init__(**kw)
Brad Bishopcfb3c892016-11-12 11:43:37 -0500403
Brad Bishopcfb3c892016-11-12 11:43:37 -0500404
Brad Bishop22cfbe62016-11-30 13:25:10 -0500405class MatchEvent(Event):
406 '''Associate one or more dbus signal match signatures with
407 a filter.'''
408
Brad Bishop22cfbe62016-11-30 13:25:10 -0500409 def __init__(self, **kw):
Brad Bishopc93bcc92017-01-21 16:23:39 -0500410 kw['event'] = 'DbusSignal'
Brad Bishop12f8a3c2017-02-09 00:02:00 -0500411 kw['event_namespace'] = []
Brad Bishopc93bcc92017-01-21 16:23:39 -0500412 kw['event_args'] = [
413 DbusSignature(**x) for x in kw.pop('signatures', [])]
414
Brad Bishop22cfbe62016-11-30 13:25:10 -0500415 super(MatchEvent, self).__init__(**kw)
416
417
Brad Bishop828df832017-01-21 22:20:43 -0500418class StartupEvent(Event):
419 '''Assemble a startup event.'''
420
421 def __init__(self, **kw):
422 kw['event'] = 'StartupEvent'
Brad Bishop12f8a3c2017-02-09 00:02:00 -0500423 kw['event_namespace'] = []
Brad Bishop828df832017-01-21 22:20:43 -0500424 super(StartupEvent, self).__init__(**kw)
425
426
Brad Bishop22cfbe62016-11-30 13:25:10 -0500427class Everything(Renderer):
428 '''Parse/render entry point.'''
429
430 class_map = {
431 'match': MatchEvent,
Brad Bishop828df832017-01-21 22:20:43 -0500432 'startup': StartupEvent,
Brad Bishop22cfbe62016-11-30 13:25:10 -0500433 }
434
435 @staticmethod
436 def load(args):
Brad Bishop22cfbe62016-11-30 13:25:10 -0500437 # Aggregate all the event YAML in the events.d directory
438 # into a single list of events.
439
Brad Bishop22cfbe62016-11-30 13:25:10 -0500440 events = []
Brad Bishopa6fcd562017-02-03 11:00:27 -0500441 events_dir = os.path.join(args.inputdir, 'events.d')
442
443 if os.path.exists(events_dir):
444 yaml_files = filter(
445 lambda x: x.endswith('.yaml'),
446 os.listdir(events_dir))
447
448 for x in yaml_files:
449 with open(os.path.join(events_dir, x), 'r') as fd:
450 for e in yaml.safe_load(fd.read()).get('events', {}):
451 events.append(e)
Brad Bishop22cfbe62016-11-30 13:25:10 -0500452
453 return Everything(
454 *events,
455 interfaces=Everything.get_interfaces(args))
456
457 @staticmethod
458 def get_interfaces(args):
459 '''Aggregate all the interface YAML in the interfaces.d
460 directory into a single list of interfaces.'''
461
Brad Bishop22cfbe62016-11-30 13:25:10 -0500462 interfaces = []
Brad Bishopa6fcd562017-02-03 11:00:27 -0500463 interfaces_dir = os.path.join(args.inputdir, 'interfaces.d')
464 if os.path.exists(interfaces_dir):
465 yaml_files = filter(
466 lambda x: x.endswith('.yaml'),
467 os.listdir(interfaces_dir))
468
469 for x in yaml_files:
470 with open(os.path.join(interfaces_dir, x), 'r') as fd:
471 for i in yaml.safe_load(fd.read()):
472 interfaces.append(i)
Brad Bishop22cfbe62016-11-30 13:25:10 -0500473
474 return interfaces
475
476 def __init__(self, *a, **kw):
477 self.interfaces = \
478 [Interface(x) for x in kw.pop('interfaces', [])]
479 self.events = [
480 self.class_map[x['type']](**x) for x in a]
481 super(Everything, self).__init__(**kw)
482
Brad Bishop22cfbe62016-11-30 13:25:10 -0500483 def generate_cpp(self, loader):
484 '''Render the template with the provided events and interfaces.'''
485 with open(os.path.join(
486 args.outputdir,
487 'generated.cpp'), 'w') as fd:
488 fd.write(
489 self.render(
490 loader,
491 'generated.mako.cpp',
492 events=self.events,
Brad Bishop9b5a12f2017-01-21 14:42:11 -0500493 interfaces=self.interfaces,
494 indent=Indent()))
Brad Bishopbf066a62016-10-19 08:09:44 -0400495
Brad Bishop95dd98f2016-11-12 12:39:15 -0500496
497if __name__ == '__main__':
498 script_dir = os.path.dirname(os.path.realpath(__file__))
Brad Bishop14a9fe52016-11-12 12:51:26 -0500499 valid_commands = {
500 'generate-cpp': 'generate_cpp',
Brad Bishop22cfbe62016-11-30 13:25:10 -0500501 }
Brad Bishop95dd98f2016-11-12 12:39:15 -0500502
503 parser = argparse.ArgumentParser(
504 description='Phosphor Inventory Manager (PIM) YAML '
505 'scanner and code generator.')
506 parser.add_argument(
507 '-o', '--output-dir', dest='outputdir',
508 default='.', help='Output directory.')
509 parser.add_argument(
510 '-d', '--dir', dest='inputdir',
511 default=os.path.join(script_dir, 'example'),
512 help='Location of files to process.')
Brad Bishopf4666f52016-11-12 12:44:42 -0500513 parser.add_argument(
514 'command', metavar='COMMAND', type=str,
515 choices=valid_commands.keys(),
Brad Bishopc029f6a2017-01-18 19:43:26 -0500516 help='%s.' % " | ".join(valid_commands.keys()))
Brad Bishop95dd98f2016-11-12 12:39:15 -0500517
518 args = parser.parse_args()
Brad Bishop22cfbe62016-11-30 13:25:10 -0500519
520 if sys.version_info < (3, 0):
521 lookup = mako.lookup.TemplateLookup(
522 directories=[script_dir],
523 disable_unicode=True)
524 else:
525 lookup = mako.lookup.TemplateLookup(
526 directories=[script_dir])
527
528 function = getattr(
529 Everything.load(args),
530 valid_commands[args.command])
531 function(lookup)
Brad Bishop95dd98f2016-11-12 12:39:15 -0500532
533
Brad Bishopbf066a62016-10-19 08:09:44 -0400534# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4