blob: 2193c90a2dd7e072e10a6f9afb33e9abde970828 [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):
Brad Bishopc1f47982017-02-09 01:27:38 -050070 self.namespace = kw.pop('namespace', [])
Brad Bishop9b5a12f2017-01-21 14:42:11 -050071 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
Brad Bishopc1f47982017-02-09 01:27:38 -0500209class Filter(MethodCall):
Brad Bishopc93bcc92017-01-21 16:23:39 -0500210 '''Convenience type for filters'''
Brad Bishopbf066a62016-10-19 08:09:44 -0400211
Brad Bishop22cfbe62016-11-30 13:25:10 -0500212 def __init__(self, **kw):
Brad Bishopc1f47982017-02-09 01:27:38 -0500213 kw['name'] = 'make_filter'
Brad Bishop22cfbe62016-11-30 13:25:10 -0500214 super(Filter, self).__init__(**kw)
Brad Bishopbf066a62016-10-19 08:09:44 -0400215
Brad Bishop0a6a4792016-11-12 12:10:07 -0500216
Brad Bishopc1f47982017-02-09 01:27:38 -0500217class Action(MethodCall):
Brad Bishopc93bcc92017-01-21 16:23:39 -0500218 '''Convenience type for actions'''
Brad Bishop561a5652016-10-26 21:13:32 -0500219
Brad Bishop22cfbe62016-11-30 13:25:10 -0500220 def __init__(self, **kw):
Brad Bishopc1f47982017-02-09 01:27:38 -0500221 kw['name'] = 'make_action'
Brad Bishop22cfbe62016-11-30 13:25:10 -0500222 super(Action, self).__init__(**kw)
Brad Bishop92665b22016-10-26 20:51:16 -0500223
Brad Bishopcfb3c892016-11-12 11:43:37 -0500224
Brad Bishopc1f47982017-02-09 01:27:38 -0500225class CreateObjects(MethodCall):
226 '''Assemble a createObjects functor.'''
Brad Bishopdb92c282017-01-21 23:44:28 -0500227
228 def __init__(self, **kw):
229 objs = []
230
231 for path, interfaces in kw.pop('objs').iteritems():
232 key_o = TrivialArgument(
233 value=path,
234 type='string',
235 decorators=[Literal('string')])
236 value_i = []
237
238 for interface, properties, in interfaces.iteritems():
239 key_i = TrivialArgument(value=interface, type='string')
240 value_p = []
241
242 for prop, value in properties.iteritems():
243 key_p = TrivialArgument(value=prop, type='string')
244 value_v = TrivialArgument(
245 decorators=[Literal(value.get('type', None))],
246 **value)
247 value_p.append(InitializerList(values=[key_p, value_v]))
248
249 value_p = InitializerList(values=value_p)
250 value_i.append(InitializerList(values=[key_i, value_p]))
251
252 value_i = InitializerList(values=value_i)
253 objs.append(InitializerList(values=[key_o, value_i]))
254
255 kw['args'] = [InitializerList(values=objs)]
Brad Bishopc1f47982017-02-09 01:27:38 -0500256 kw['namespace'] = ['functor']
Brad Bishopdb92c282017-01-21 23:44:28 -0500257 super(CreateObjects, self).__init__(**kw)
258
259
Brad Bishopc1f47982017-02-09 01:27:38 -0500260class DestroyObjects(MethodCall):
261 '''Assemble a destroyObject functor.'''
Brad Bishop22cfbe62016-11-30 13:25:10 -0500262
263 def __init__(self, **kw):
Brad Bishop7b7e7122017-01-21 21:21:46 -0500264 values = [{'value': x, 'type': 'string'} for x in kw.pop('paths')]
265 args = [InitializerList(
266 values=[TrivialArgument(**x) for x in values])]
Brad Bishopc93bcc92017-01-21 16:23:39 -0500267 kw['args'] = args
Brad Bishopc1f47982017-02-09 01:27:38 -0500268 kw['namespace'] = ['functor']
Brad Bishop7b7e7122017-01-21 21:21:46 -0500269 super(DestroyObjects, self).__init__(**kw)
Brad Bishop22cfbe62016-11-30 13:25:10 -0500270
271
Brad Bishopc1f47982017-02-09 01:27:38 -0500272class SetProperty(MethodCall):
273 '''Assemble a setProperty functor.'''
Brad Bishope2e402f2016-11-30 18:00:17 -0500274
275 def __init__(self, **kw):
Brad Bishopc93bcc92017-01-21 16:23:39 -0500276 args = []
277
278 value = kw.pop('value')
279 prop = kw.pop('property')
280 iface = kw.pop('interface')
281 iface = Interface(iface)
282 namespace = iface.namespace().split('::')[:-1]
283 name = iface[-1]
284 t = Template(namespace=namespace, name=iface[-1])
285
Brad Bishope2e402f2016-11-30 18:00:17 -0500286 member = '&%s' % '::'.join(
Brad Bishopc93bcc92017-01-21 16:23:39 -0500287 namespace + [name, NamedElement(name=prop).camelCase])
288 member_type = cppTypeName(value['type'])
289 member_cast = '{0} ({1}::*)({0})'.format(member_type, t.qualified())
Brad Bishope2e402f2016-11-30 18:00:17 -0500290
Brad Bishop02ca0212017-01-28 23:25:58 -0500291 paths = [{'value': x, 'type': 'string'} for x in kw.pop('paths')]
292 args.append(InitializerList(
293 values=[TrivialArgument(**x) for x in paths]))
294
Brad Bishopc93bcc92017-01-21 16:23:39 -0500295 args.append(TrivialArgument(value=str(iface), type='string'))
296 args.append(TrivialArgument(
297 value=member, decorators=[Cast('static', member_cast)]))
298 args.append(TrivialArgument(**value))
Brad Bishope2e402f2016-11-30 18:00:17 -0500299
Brad Bishopc93bcc92017-01-21 16:23:39 -0500300 kw['templates'] = [Template(name=name, namespace=namespace)]
301 kw['args'] = args
Brad Bishopc1f47982017-02-09 01:27:38 -0500302 kw['namespace'] = ['functor']
Brad Bishope2e402f2016-11-30 18:00:17 -0500303 super(SetProperty, self).__init__(**kw)
304
305
Brad Bishopc1f47982017-02-09 01:27:38 -0500306class PropertyChanged(MethodCall):
307 '''Assemble a propertyChanged functor.'''
Brad Bishop22cfbe62016-11-30 13:25:10 -0500308
309 def __init__(self, **kw):
Brad Bishopc93bcc92017-01-21 16:23:39 -0500310 args = []
311 args.append(TrivialArgument(value=kw.pop('interface'), type='string'))
312 args.append(TrivialArgument(value=kw.pop('property'), type='string'))
313 args.append(TrivialArgument(
314 decorators=[
315 Literal(kw['value'].get('type', None))], **kw.pop('value')))
316 kw['args'] = args
Brad Bishopc1f47982017-02-09 01:27:38 -0500317 kw['namespace'] = ['functor']
Brad Bishop22cfbe62016-11-30 13:25:10 -0500318 super(PropertyChanged, self).__init__(**kw)
319
320
Brad Bishopc1f47982017-02-09 01:27:38 -0500321class PropertyIs(MethodCall):
322 '''Assemble a propertyIs functor.'''
Brad Bishop040e18b2017-01-21 22:04:00 -0500323
324 def __init__(self, **kw):
325 args = []
326 args.append(TrivialArgument(value=kw.pop('path'), type='string'))
327 args.append(TrivialArgument(value=kw.pop('interface'), type='string'))
328 args.append(TrivialArgument(value=kw.pop('property'), type='string'))
329 args.append(TrivialArgument(
330 decorators=[
331 Literal(kw['value'].get('type', None))], **kw.pop('value')))
332
333 service = kw.pop('service', None)
334 if service:
335 args.append(TrivialArgument(value=service, type='string'))
336
337 kw['args'] = args
Brad Bishopc1f47982017-02-09 01:27:38 -0500338 kw['namespace'] = ['functor']
Brad Bishop040e18b2017-01-21 22:04:00 -0500339 super(PropertyIs, self).__init__(**kw)
340
341
Brad Bishopc93bcc92017-01-21 16:23:39 -0500342class Event(MethodCall):
343 '''Assemble an inventory manager event.'''
Brad Bishop22cfbe62016-11-30 13:25:10 -0500344
345 action_map = {
Brad Bishop7b7e7122017-01-21 21:21:46 -0500346 'destroyObjects': DestroyObjects,
Brad Bishopdb92c282017-01-21 23:44:28 -0500347 'createObjects': CreateObjects,
Brad Bishope2e402f2016-11-30 18:00:17 -0500348 'setProperty': SetProperty,
Brad Bishopcfb3c892016-11-12 11:43:37 -0500349 }
350
Brad Bishopc93bcc92017-01-21 16:23:39 -0500351 filter_map = {
352 'propertyChangedTo': PropertyChanged,
Brad Bishop040e18b2017-01-21 22:04:00 -0500353 'propertyIs': PropertyIs,
Brad Bishopc93bcc92017-01-21 16:23:39 -0500354 }
355
Brad Bishop22cfbe62016-11-30 13:25:10 -0500356 def __init__(self, **kw):
Brad Bishopc93bcc92017-01-21 16:23:39 -0500357 self.summary = kw.pop('name')
358
359 filters = [
360 self.filter_map[x['name']](**x) for x in kw.pop('filters', [])]
Brad Bishopc1f47982017-02-09 01:27:38 -0500361 filters = [Filter(args=[x]) for x in filters]
Brad Bishop064c94a2017-01-21 21:33:30 -0500362 filters = Vector(
Brad Bishop12f8a3c2017-02-09 00:02:00 -0500363 templates=[Template(name='Filter', namespace=[])],
Brad Bishop064c94a2017-01-21 21:33:30 -0500364 args=filters)
Brad Bishopc93bcc92017-01-21 16:23:39 -0500365
366 event = MethodCall(
367 name='make_shared',
368 namespace=['std'],
369 templates=[Template(
370 name=kw.pop('event'),
371 namespace=kw.pop('event_namespace', []))],
Brad Bishop064c94a2017-01-21 21:33:30 -0500372 args=kw.pop('event_args', []) + [filters])
Brad Bishopc93bcc92017-01-21 16:23:39 -0500373
374 events = Vector(
Brad Bishop12f8a3c2017-02-09 00:02:00 -0500375 templates=[Template(name='EventBasePtr', namespace=[])],
Brad Bishopc93bcc92017-01-21 16:23:39 -0500376 args=[event])
377
Brad Bishop12f8a3c2017-02-09 00:02:00 -0500378 action_type = Template(name='Action', namespace=[])
Brad Bishopc93bcc92017-01-21 16:23:39 -0500379 action_args = [
380 self.action_map[x['name']](**x) for x in kw.pop('actions', [])]
Brad Bishopc1f47982017-02-09 01:27:38 -0500381 action_args = [Action(args=[x]) for x in action_args]
Brad Bishopc93bcc92017-01-21 16:23:39 -0500382 actions = Vector(
383 templates=[action_type],
384 args=action_args)
385
386 kw['name'] = 'make_tuple'
387 kw['namespace'] = ['std']
388 kw['args'] = [events, actions]
Brad Bishop22cfbe62016-11-30 13:25:10 -0500389 super(Event, self).__init__(**kw)
Brad Bishopcfb3c892016-11-12 11:43:37 -0500390
Brad Bishopcfb3c892016-11-12 11:43:37 -0500391
Brad Bishop22cfbe62016-11-30 13:25:10 -0500392class MatchEvent(Event):
393 '''Associate one or more dbus signal match signatures with
394 a filter.'''
395
Brad Bishop22cfbe62016-11-30 13:25:10 -0500396 def __init__(self, **kw):
Brad Bishopc93bcc92017-01-21 16:23:39 -0500397 kw['event'] = 'DbusSignal'
Brad Bishop12f8a3c2017-02-09 00:02:00 -0500398 kw['event_namespace'] = []
Brad Bishopc93bcc92017-01-21 16:23:39 -0500399 kw['event_args'] = [
400 DbusSignature(**x) for x in kw.pop('signatures', [])]
401
Brad Bishop22cfbe62016-11-30 13:25:10 -0500402 super(MatchEvent, self).__init__(**kw)
403
404
Brad Bishop828df832017-01-21 22:20:43 -0500405class StartupEvent(Event):
406 '''Assemble a startup event.'''
407
408 def __init__(self, **kw):
409 kw['event'] = 'StartupEvent'
Brad Bishop12f8a3c2017-02-09 00:02:00 -0500410 kw['event_namespace'] = []
Brad Bishop828df832017-01-21 22:20:43 -0500411 super(StartupEvent, self).__init__(**kw)
412
413
Brad Bishop22cfbe62016-11-30 13:25:10 -0500414class Everything(Renderer):
415 '''Parse/render entry point.'''
416
417 class_map = {
418 'match': MatchEvent,
Brad Bishop828df832017-01-21 22:20:43 -0500419 'startup': StartupEvent,
Brad Bishop22cfbe62016-11-30 13:25:10 -0500420 }
421
422 @staticmethod
423 def load(args):
Brad Bishop22cfbe62016-11-30 13:25:10 -0500424 # Aggregate all the event YAML in the events.d directory
425 # into a single list of events.
426
Brad Bishop22cfbe62016-11-30 13:25:10 -0500427 events = []
Brad Bishopa6fcd562017-02-03 11:00:27 -0500428 events_dir = os.path.join(args.inputdir, 'events.d')
429
430 if os.path.exists(events_dir):
431 yaml_files = filter(
432 lambda x: x.endswith('.yaml'),
433 os.listdir(events_dir))
434
435 for x in yaml_files:
436 with open(os.path.join(events_dir, x), 'r') as fd:
437 for e in yaml.safe_load(fd.read()).get('events', {}):
438 events.append(e)
Brad Bishop22cfbe62016-11-30 13:25:10 -0500439
440 return Everything(
441 *events,
442 interfaces=Everything.get_interfaces(args))
443
444 @staticmethod
445 def get_interfaces(args):
446 '''Aggregate all the interface YAML in the interfaces.d
447 directory into a single list of interfaces.'''
448
Brad Bishop22cfbe62016-11-30 13:25:10 -0500449 interfaces = []
Brad Bishopa6fcd562017-02-03 11:00:27 -0500450 interfaces_dir = os.path.join(args.inputdir, 'interfaces.d')
451 if os.path.exists(interfaces_dir):
452 yaml_files = filter(
453 lambda x: x.endswith('.yaml'),
454 os.listdir(interfaces_dir))
455
456 for x in yaml_files:
457 with open(os.path.join(interfaces_dir, x), 'r') as fd:
458 for i in yaml.safe_load(fd.read()):
459 interfaces.append(i)
Brad Bishop22cfbe62016-11-30 13:25:10 -0500460
461 return interfaces
462
463 def __init__(self, *a, **kw):
464 self.interfaces = \
465 [Interface(x) for x in kw.pop('interfaces', [])]
466 self.events = [
467 self.class_map[x['type']](**x) for x in a]
468 super(Everything, self).__init__(**kw)
469
Brad Bishop22cfbe62016-11-30 13:25:10 -0500470 def generate_cpp(self, loader):
471 '''Render the template with the provided events and interfaces.'''
472 with open(os.path.join(
473 args.outputdir,
474 'generated.cpp'), 'w') as fd:
475 fd.write(
476 self.render(
477 loader,
478 'generated.mako.cpp',
479 events=self.events,
Brad Bishop9b5a12f2017-01-21 14:42:11 -0500480 interfaces=self.interfaces,
481 indent=Indent()))
Brad Bishopbf066a62016-10-19 08:09:44 -0400482
Brad Bishop95dd98f2016-11-12 12:39:15 -0500483
484if __name__ == '__main__':
485 script_dir = os.path.dirname(os.path.realpath(__file__))
Brad Bishop14a9fe52016-11-12 12:51:26 -0500486 valid_commands = {
487 'generate-cpp': 'generate_cpp',
Brad Bishop22cfbe62016-11-30 13:25:10 -0500488 }
Brad Bishop95dd98f2016-11-12 12:39:15 -0500489
490 parser = argparse.ArgumentParser(
491 description='Phosphor Inventory Manager (PIM) YAML '
492 'scanner and code generator.')
493 parser.add_argument(
494 '-o', '--output-dir', dest='outputdir',
495 default='.', help='Output directory.')
496 parser.add_argument(
497 '-d', '--dir', dest='inputdir',
498 default=os.path.join(script_dir, 'example'),
499 help='Location of files to process.')
Brad Bishopf4666f52016-11-12 12:44:42 -0500500 parser.add_argument(
501 'command', metavar='COMMAND', type=str,
502 choices=valid_commands.keys(),
Brad Bishopc029f6a2017-01-18 19:43:26 -0500503 help='%s.' % " | ".join(valid_commands.keys()))
Brad Bishop95dd98f2016-11-12 12:39:15 -0500504
505 args = parser.parse_args()
Brad Bishop22cfbe62016-11-30 13:25:10 -0500506
507 if sys.version_info < (3, 0):
508 lookup = mako.lookup.TemplateLookup(
509 directories=[script_dir],
510 disable_unicode=True)
511 else:
512 lookup = mako.lookup.TemplateLookup(
513 directories=[script_dir])
514
515 function = getattr(
516 Everything.load(args),
517 valid_commands[args.command])
518 function(lookup)
Brad Bishop95dd98f2016-11-12 12:39:15 -0500519
520
Brad Bishopbf066a62016-10-19 08:09:44 -0400521# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4