blob: 963c7b370b2a0bff1153a7d964876e2ce45dd56b [file] [log] [blame]
Matthew Barthdb440d42017-04-17 15:49:37 -05001#!/usr/bin/env python
2
Brad Bishop34a7acd2017-04-27 23:47:23 -04003'''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
Matthew Barthdb440d42017-04-17 15:49:37 -050018import os
19import sys
20import yaml
Brad Bishop34a7acd2017-04-27 23:47:23 -040021import mako.lookup
Matthew Barthdb440d42017-04-17 15:49:37 -050022from argparse import ArgumentParser
Brad Bishop34a7acd2017-04-27 23:47:23 -040023from sdbusplus.renderer import Renderer
Brad Bishop05b0c1e2017-05-23 00:24:01 -040024from sdbusplus.namedelement import NamedElement
Brad Bishope73b2c32017-05-23 18:01:54 -040025import sdbusplus.property
Brad Bishop05b0c1e2017-05-23 00:24:01 -040026
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)
Matthew Barthdb440d42017-04-17 15:49:37 -050085
86
Brad Bishop01079892017-05-26 10:56:45 -040087class 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 = [
Brad Bishop01079892017-05-26 10:56:45 -0400104 'int16',
105 'int32',
106 'int64',
Brad Bishop01079892017-05-26 10:56:45 -0400107 '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)
Brad Bishopec2ed2f2017-05-31 21:10:43 -0400123 elif self.type == 'byte':
124 return Cast('static', 'uint8_t'.format(self.type))(arg)
Brad Bishop01079892017-05-26 10:56:45 -0400125
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):
Gunnar Mills06fdaff2018-04-08 16:28:18 -0500147 '''Define argument type interface.'''
Brad Bishop01079892017-05-26 10:56:45 -0400148
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
Matt Spinler80e9b652017-11-02 14:21:04 -0500179
Gunnar Mills30474cf2017-08-11 09:38:54 -0500180class Metadata(Argument):
181 '''Metadata type arguments.'''
182
183 def __init__(self, **kw):
184 self.value = kw.pop('value')
185 self.decorators = kw.pop('decorators', [])
186 if kw.get('type', None) == 'string':
187 self.decorators.insert(0, Quote())
188
189 super(Metadata, self).__init__(**kw)
190
191 def argument(self, loader, indent):
192 a = str(self.value)
193 for d in self.decorators:
194 a = d(a)
195
196 return a
Brad Bishop01079892017-05-26 10:56:45 -0400197
Matt Spinler80e9b652017-11-02 14:21:04 -0500198
Brad Bishop34a7acd2017-04-27 23:47:23 -0400199class Indent(object):
200 '''Help templates be depth agnostic.'''
Matthew Barthdb440d42017-04-17 15:49:37 -0500201
Brad Bishop34a7acd2017-04-27 23:47:23 -0400202 def __init__(self, depth=0):
203 self.depth = depth
Matthew Barthdb440d42017-04-17 15:49:37 -0500204
Brad Bishop34a7acd2017-04-27 23:47:23 -0400205 def __add__(self, depth):
206 return Indent(self.depth + depth)
207
208 def __call__(self, depth):
209 '''Render an indent at the current depth plus depth.'''
210 return 4*' '*(depth + self.depth)
211
212
Brad Bishop05b0c1e2017-05-23 00:24:01 -0400213class ConfigEntry(NamedElement):
214 '''Base interface for rendered items.'''
215
216 def __init__(self, *a, **kw):
217 '''Pop the configfile/class/subclass keywords.'''
218
219 self.configfile = kw.pop('configfile')
220 self.cls = kw.pop('class')
221 self.subclass = kw.pop(self.cls)
222 super(ConfigEntry, self).__init__(**kw)
223
224 def factory(self, objs):
225 ''' Optional factory interface for subclasses to add
226 additional items to be rendered.'''
227
228 pass
229
230 def setup(self, objs):
231 ''' Optional setup interface for subclasses, invoked
232 after all factory methods have been run.'''
233
234 pass
235
236
Brad Bishop0e7df132017-05-23 17:58:12 -0400237class Path(ConfigEntry):
238 '''Path/metadata association.'''
239
240 def __init__(self, *a, **kw):
241 super(Path, self).__init__(**kw)
242
Brad Bishopbabf3b72017-05-31 19:44:53 -0400243 if self.name['meta'].upper() != self.name['meta']:
244 raise InvalidConfigError(
245 self.configfile,
246 'Metadata tag "{0}" must be upper case.'.format(
247 self.name['meta']))
248
Brad Bishop0e7df132017-05-23 17:58:12 -0400249 def factory(self, objs):
250 '''Create path and metadata elements.'''
251
252 args = {
253 'class': 'pathname',
254 'pathname': 'element',
255 'name': self.name['path']
256 }
257 add_unique(ConfigEntry(
258 configfile=self.configfile, **args), objs)
259
260 args = {
261 'class': 'meta',
262 'meta': 'element',
263 'name': self.name['meta']
264 }
265 add_unique(ConfigEntry(
266 configfile=self.configfile, **args), objs)
267
268 super(Path, self).factory(objs)
269
270 def setup(self, objs):
Gunnar Mills78199b42017-10-25 16:30:18 -0500271 '''Resolve path and metadata names to indices.'''
Brad Bishop0e7df132017-05-23 17:58:12 -0400272
273 self.path = get_index(
274 objs, 'pathname', self.name['path'])
275 self.meta = get_index(
276 objs, 'meta', self.name['meta'])
277
278 super(Path, self).setup(objs)
279
280
Brad Bishope73b2c32017-05-23 18:01:54 -0400281class Property(ConfigEntry):
282 '''Property/interface/metadata association.'''
283
284 def __init__(self, *a, **kw):
285 super(Property, self).__init__(**kw)
286
Brad Bishopbabf3b72017-05-31 19:44:53 -0400287 if self.name['meta'].upper() != self.name['meta']:
288 raise InvalidConfigError(
289 self.configfile,
290 'Metadata tag "{0}" must be upper case.'.format(
291 self.name['meta']))
292
Brad Bishope73b2c32017-05-23 18:01:54 -0400293 def factory(self, objs):
294 '''Create interface, property name and metadata elements.'''
295
296 args = {
297 'class': 'interface',
298 'interface': 'element',
299 'name': self.name['interface']
300 }
301 add_unique(ConfigEntry(
302 configfile=self.configfile, **args), objs)
303
304 args = {
305 'class': 'propertyname',
306 'propertyname': 'element',
307 'name': self.name['property']
308 }
309 add_unique(ConfigEntry(
310 configfile=self.configfile, **args), objs)
311
312 args = {
313 'class': 'meta',
314 'meta': 'element',
315 'name': self.name['meta']
316 }
317 add_unique(ConfigEntry(
318 configfile=self.configfile, **args), objs)
319
320 super(Property, self).factory(objs)
321
322 def setup(self, objs):
Gunnar Mills78199b42017-10-25 16:30:18 -0500323 '''Resolve interface, property and metadata to indices.'''
Brad Bishope73b2c32017-05-23 18:01:54 -0400324
325 self.interface = get_index(
326 objs, 'interface', self.name['interface'])
327 self.prop = get_index(
328 objs, 'propertyname', self.name['property'])
329 self.meta = get_index(
330 objs, 'meta', self.name['meta'])
331
332 super(Property, self).setup(objs)
333
334
Brad Bishop4b916f12017-05-23 18:06:38 -0400335class Instance(ConfigEntry):
336 '''Property/Path association.'''
337
338 def __init__(self, *a, **kw):
339 super(Instance, self).__init__(**kw)
340
341 def setup(self, objs):
Gunnar Mills78199b42017-10-25 16:30:18 -0500342 '''Resolve elements to indices.'''
Brad Bishop4b916f12017-05-23 18:06:38 -0400343
344 self.interface = get_index(
345 objs, 'interface', self.name['property']['interface'])
346 self.prop = get_index(
347 objs, 'propertyname', self.name['property']['property'])
348 self.propmeta = get_index(
349 objs, 'meta', self.name['property']['meta'])
350 self.path = get_index(
351 objs, 'pathname', self.name['path']['path'])
352 self.pathmeta = get_index(
353 objs, 'meta', self.name['path']['meta'])
354
355 super(Instance, self).setup(objs)
356
Marri Devender Rao80c70612018-04-12 09:22:55 -0500357class PathInstance(ConfigEntry):
358 '''Path association.'''
359
360 def __init__(self, *a, **kw):
361 super(PathInstance, self).__init__(**kw)
362
363 def setup(self, objs):
364 '''Resolve elements to indices.'''
365 self.path = self.name['path']['path']
366 self.pathmeta = self.name['path']['meta']
367 super(PathInstance, self).setup(objs)
Brad Bishop4b916f12017-05-23 18:06:38 -0400368
Brad Bishop05b0c1e2017-05-23 00:24:01 -0400369class Group(ConfigEntry):
370 '''Pop the members keyword for groups.'''
371
372 def __init__(self, *a, **kw):
373 self.members = kw.pop('members')
374 super(Group, self).__init__(**kw)
375
376
377class ImplicitGroup(Group):
378 '''Provide a factory method for groups whose members are
379 not explicitly declared in the config files.'''
380
381 def __init__(self, *a, **kw):
382 super(ImplicitGroup, self).__init__(**kw)
383
384 def factory(self, objs):
385 '''Create group members.'''
386
387 factory = Everything.classmap(self.subclass, 'element')
388 for m in self.members:
389 args = {
390 'class': self.subclass,
391 self.subclass: 'element',
392 'name': m
393 }
394
395 obj = factory(configfile=self.configfile, **args)
396 add_unique(obj, objs)
397 obj.factory(objs)
398
399 super(ImplicitGroup, self).factory(objs)
400
401
Brad Bishop0e7df132017-05-23 17:58:12 -0400402class GroupOfPaths(ImplicitGroup):
403 '''Path group config file directive.'''
404
405 def __init__(self, *a, **kw):
406 super(GroupOfPaths, self).__init__(**kw)
407
408 def setup(self, objs):
409 '''Resolve group members.'''
410
411 def map_member(x):
412 path = get_index(
413 objs, 'pathname', x['path'])
414 meta = get_index(
415 objs, 'meta', x['meta'])
416 return (path, meta)
417
418 self.members = map(
419 map_member,
420 self.members)
421
422 super(GroupOfPaths, self).setup(objs)
423
424
Brad Bishope73b2c32017-05-23 18:01:54 -0400425class GroupOfProperties(ImplicitGroup):
426 '''Property group config file directive.'''
427
428 def __init__(self, *a, **kw):
Brad Bishopec2ed2f2017-05-31 21:10:43 -0400429 self.type = kw.pop('type')
Brad Bishope73b2c32017-05-23 18:01:54 -0400430 self.datatype = sdbusplus.property.Property(
431 name=kw.get('name'),
Brad Bishopec2ed2f2017-05-31 21:10:43 -0400432 type=self.type).cppTypeName
Brad Bishope73b2c32017-05-23 18:01:54 -0400433
434 super(GroupOfProperties, self).__init__(**kw)
435
436 def setup(self, objs):
437 '''Resolve group members.'''
438
439 def map_member(x):
440 iface = get_index(
441 objs, 'interface', x['interface'])
442 prop = get_index(
443 objs, 'propertyname', x['property'])
444 meta = get_index(
445 objs, 'meta', x['meta'])
446
447 return (iface, prop, meta)
448
449 self.members = map(
450 map_member,
451 self.members)
452
453 super(GroupOfProperties, self).setup(objs)
454
455
Brad Bishop4b916f12017-05-23 18:06:38 -0400456class GroupOfInstances(ImplicitGroup):
457 '''A group of property instances.'''
458
459 def __init__(self, *a, **kw):
460 super(GroupOfInstances, self).__init__(**kw)
461
462 def setup(self, objs):
463 '''Resolve group members.'''
464
465 def map_member(x):
466 path = get_index(objs, 'pathname', x['path']['path'])
467 pathmeta = get_index(objs, 'meta', x['path']['meta'])
468 interface = get_index(
469 objs, 'interface', x['property']['interface'])
470 prop = get_index(objs, 'propertyname', x['property']['property'])
471 propmeta = get_index(objs, 'meta', x['property']['meta'])
472 instance = get_index(objs, 'instance', x)
473
474 return (path, pathmeta, interface, prop, propmeta, instance)
475
476 self.members = map(
477 map_member,
478 self.members)
479
480 super(GroupOfInstances, self).setup(objs)
481
Marri Devender Rao80c70612018-04-12 09:22:55 -0500482class GroupOfPathInstances(ImplicitGroup):
483 '''A group of path instances.'''
484
485 def __init__(self, *a, **kw):
486 super(GroupOfPathInstances, self).__init__(**kw)
487
488 def setup(self, objs):
489 '''Resolve group members.'''
490
491 def map_member(x):
492 path = get_index(objs, 'pathname', x['path']['path'])
493 pathmeta = get_index(objs, 'meta', x['path']['meta'])
494 pathinstance = get_index(objs, 'pathinstance', x)
495 return (path, pathmeta, pathinstance)
496
497 self.members = map(
498 map_member,
499 self.members)
500
501 super(GroupOfPathInstances, self).setup(objs)
502
Brad Bishop4b916f12017-05-23 18:06:38 -0400503
504class HasPropertyIndex(ConfigEntry):
505 '''Handle config file directives that require an index to be
506 constructed.'''
507
508 def __init__(self, *a, **kw):
509 self.paths = kw.pop('paths')
510 self.properties = kw.pop('properties')
511 super(HasPropertyIndex, self).__init__(**kw)
512
513 def factory(self, objs):
514 '''Create a group of instances for this index.'''
515
516 members = []
517 path_group = get_index(
518 objs, 'pathgroup', self.paths, config=self.configfile)
519 property_group = get_index(
520 objs, 'propertygroup', self.properties, config=self.configfile)
521
522 for path in objs['pathgroup'][path_group].members:
523 for prop in objs['propertygroup'][property_group].members:
524 member = {
525 'path': path,
526 'property': prop,
527 }
528 members.append(member)
529
530 args = {
531 'members': members,
532 'class': 'instancegroup',
533 'instancegroup': 'instance',
534 'name': '{0} {1}'.format(self.paths, self.properties)
535 }
536
537 group = GroupOfInstances(configfile=self.configfile, **args)
538 add_unique(group, objs, config=self.configfile)
539 group.factory(objs)
540
541 super(HasPropertyIndex, self).factory(objs)
542
543 def setup(self, objs):
544 '''Resolve path, property, and instance groups.'''
545
546 self.instances = get_index(
547 objs,
548 'instancegroup',
549 '{0} {1}'.format(self.paths, self.properties),
550 config=self.configfile)
551 self.paths = get_index(
552 objs,
553 'pathgroup',
554 self.paths,
555 config=self.configfile)
556 self.properties = get_index(
557 objs,
558 'propertygroup',
559 self.properties,
560 config=self.configfile)
561 self.datatype = objs['propertygroup'][self.properties].datatype
Brad Bishopec2ed2f2017-05-31 21:10:43 -0400562 self.type = objs['propertygroup'][self.properties].type
Brad Bishop4b916f12017-05-23 18:06:38 -0400563
564 super(HasPropertyIndex, self).setup(objs)
565
Marri Devender Rao80c70612018-04-12 09:22:55 -0500566class HasPathIndex(ConfigEntry):
567 '''Handle config file directives that require an index to be
568 constructed.'''
569
570 def __init__(self, *a, **kw):
571 self.paths = kw.pop('paths')
572 super(HasPathIndex, self).__init__(**kw)
573
574 def factory(self, objs):
575 '''Create a group of instances for this index.'''
576
577 members = []
578 path_group = get_index(
579 objs, 'pathgroup', self.paths, config=self.configfile)
580
581 for path in objs['pathgroup'][path_group].members:
582 member = {
583 'path': path,
584 }
585 members.append(member)
586
587 args = {
588 'members': members,
589 'class': 'pathinstancegroup',
590 'pathinstancegroup': 'pathinstance',
591 'name': '{0}'.format(self.paths)
592 }
593
594 group = GroupOfPathInstances(configfile=self.configfile, **args)
595 add_unique(group, objs, config=self.configfile)
596 group.factory(objs)
597
598 super(HasPathIndex, self).factory(objs)
599
600 def setup(self, objs):
601 '''Resolve path and instance groups.'''
602
603 self.pathinstances = get_index(
604 objs,
605 'pathinstancegroup',
606 '{0}'.format(self.paths),
607 config=self.configfile)
608 self.paths = get_index(
609 objs,
610 'pathgroup',
611 self.paths,
612 config=self.configfile)
613 super(HasPathIndex, self).setup(objs)
Brad Bishop4b916f12017-05-23 18:06:38 -0400614
615class PropertyWatch(HasPropertyIndex):
616 '''Handle the property watch config file directive.'''
617
618 def __init__(self, *a, **kw):
Brad Bishopfccdc392017-05-22 21:11:09 -0400619 self.callback = kw.pop('callback', None)
Brad Bishop4b916f12017-05-23 18:06:38 -0400620 super(PropertyWatch, self).__init__(**kw)
621
Brad Bishopfccdc392017-05-22 21:11:09 -0400622 def setup(self, objs):
623 '''Resolve optional callback.'''
624
625 if self.callback:
626 self.callback = get_index(
627 objs,
628 'callback',
629 self.callback,
630 config=self.configfile)
631
632 super(PropertyWatch, self).setup(objs)
633
Marri Devender Rao80c70612018-04-12 09:22:55 -0500634class PathWatch(HasPathIndex):
635 '''Handle the path watch config file directive.'''
636
637 def __init__(self, *a, **kw):
638 self.pathcallback = kw.pop('pathcallback', None)
639 super(PathWatch, self).__init__(**kw)
640
641 def setup(self, objs):
642 '''Resolve optional callback.'''
643 if self.pathcallback:
644 self.pathcallback = get_index(
645 objs,
646 'pathcallback',
647 self.pathcallback,
648 config=self.configfile)
649 super(PathWatch, self).setup(objs)
650
Brad Bishop4b916f12017-05-23 18:06:38 -0400651
Brad Bishopc1283ae2017-05-20 21:42:38 -0400652class Callback(HasPropertyIndex):
653 '''Interface and common logic for callbacks.'''
654
655 def __init__(self, *a, **kw):
656 super(Callback, self).__init__(**kw)
657
Marri Devender Rao80c70612018-04-12 09:22:55 -0500658class PathCallback(HasPathIndex):
659 '''Interface and common logic for callbacks.'''
660
661 def __init__(self, *a, **kw):
662 super(PathCallback, self).__init__(**kw)
Brad Bishopc1283ae2017-05-20 21:42:38 -0400663
Brad Bishop4041d722017-05-21 10:06:07 -0400664class ConditionCallback(ConfigEntry, Renderer):
665 '''Handle the journal callback config file directive.'''
666
667 def __init__(self, *a, **kw):
668 self.condition = kw.pop('condition')
669 self.instance = kw.pop('instance')
Brad Bishop3539db62017-05-30 14:21:12 -0400670 self.defer = kw.pop('defer', None)
Brad Bishop4041d722017-05-21 10:06:07 -0400671 super(ConditionCallback, self).__init__(**kw)
672
673 def factory(self, objs):
674 '''Create a graph instance for this callback.'''
675
676 args = {
677 'configfile': self.configfile,
678 'members': [self.instance],
679 'class': 'callbackgroup',
680 'callbackgroup': 'callback',
681 'name': [self.instance]
682 }
683
684 entry = CallbackGraphEntry(**args)
685 add_unique(entry, objs, config=self.configfile)
686
687 super(ConditionCallback, self).factory(objs)
688
689 def setup(self, objs):
690 '''Resolve condition and graph entry.'''
691
692 self.graph = get_index(
693 objs,
694 'callbackgroup',
695 [self.instance],
696 config=self.configfile)
697
698 self.condition = get_index(
699 objs,
700 'condition',
701 self.name,
702 config=self.configfile)
703
704 super(ConditionCallback, self).setup(objs)
705
706 def construct(self, loader, indent):
707 return self.render(
708 loader,
709 'conditional.mako.cpp',
710 c=self,
711 indent=indent)
712
713
714class Condition(HasPropertyIndex):
715 '''Interface and common logic for conditions.'''
716
717 def __init__(self, *a, **kw):
718 self.callback = kw.pop('callback')
Brad Bishop3539db62017-05-30 14:21:12 -0400719 self.defer = kw.pop('defer', None)
Brad Bishop4041d722017-05-21 10:06:07 -0400720 super(Condition, self).__init__(**kw)
721
722 def factory(self, objs):
723 '''Create a callback instance for this conditional.'''
724
725 args = {
726 'configfile': self.configfile,
727 'condition': self.name,
728 'class': 'callback',
729 'callback': 'conditional',
730 'instance': self.callback,
731 'name': self.name,
Brad Bishop3539db62017-05-30 14:21:12 -0400732 'defer': self.defer
Brad Bishop4041d722017-05-21 10:06:07 -0400733 }
734
735 callback = ConditionCallback(**args)
736 add_unique(callback, objs, config=self.configfile)
737 callback.factory(objs)
738
739 super(Condition, self).factory(objs)
740
741
742class CountCondition(Condition, Renderer):
743 '''Handle the count condition config file directive.'''
744
745 def __init__(self, *a, **kw):
746 self.countop = kw.pop('countop')
747 self.countbound = kw.pop('countbound')
748 self.op = kw.pop('op')
749 self.bound = kw.pop('bound')
Matt Spinlerc458dee2018-02-19 13:09:10 -0600750 self.oneshot = TrivialArgument(
751 type='boolean',
752 value=kw.pop('oneshot', False))
Brad Bishop4041d722017-05-21 10:06:07 -0400753 super(CountCondition, self).__init__(**kw)
754
Brad Bishopec2ed2f2017-05-31 21:10:43 -0400755 def setup(self, objs):
756 '''Resolve type.'''
757
758 super(CountCondition, self).setup(objs)
759 self.bound = TrivialArgument(
760 type=self.type,
761 value=self.bound)
762
Brad Bishop4041d722017-05-21 10:06:07 -0400763 def construct(self, loader, indent):
764 return self.render(
765 loader,
766 'count.mako.cpp',
767 c=self,
768 indent=indent)
769
770
Matthew Barthefdd03c2019-09-04 15:44:35 -0500771class MedianCondition(Condition, Renderer):
772 '''Handle the median condition config file directive.'''
773
774 def __init__(self, *a, **kw):
775 self.op = kw.pop('op')
776 self.bound = kw.pop('bound')
777 self.oneshot = TrivialArgument(
778 type='boolean',
779 value=kw.pop('oneshot', False))
780 super(MedianCondition, self).__init__(**kw)
781
782 def setup(self, objs):
783 '''Resolve type.'''
784
785 super(MedianCondition, self).setup(objs)
786 self.bound = TrivialArgument(
787 type=self.type,
788 value=self.bound)
789
790 def construct(self, loader, indent):
791 return self.render(
792 loader,
793 'median.mako.cpp',
794 c=self,
795 indent=indent)
796
797
Brad Bishopc1283ae2017-05-20 21:42:38 -0400798class Journal(Callback, Renderer):
799 '''Handle the journal callback config file directive.'''
800
801 def __init__(self, *a, **kw):
802 self.severity = kw.pop('severity')
803 self.message = kw.pop('message')
804 super(Journal, self).__init__(**kw)
805
806 def construct(self, loader, indent):
807 return self.render(
808 loader,
809 'journal.mako.cpp',
810 c=self,
811 indent=indent)
812
813
Gunnar Millsd5faea32017-08-08 14:19:36 -0500814class Elog(Callback, Renderer):
815 '''Handle the elog callback config file directive.'''
816
817 def __init__(self, *a, **kw):
818 self.error = kw.pop('error')
Gunnar Mills30474cf2017-08-11 09:38:54 -0500819 self.metadata = [Metadata(**x) for x in kw.pop('metadata', {})]
Gunnar Millsd5faea32017-08-08 14:19:36 -0500820 super(Elog, self).__init__(**kw)
821
822 def construct(self, loader, indent):
Gunnar Millsd5faea32017-08-08 14:19:36 -0500823 with open('errors.hpp', 'a') as fd:
824 fd.write(
825 self.render(
826 loader,
827 'errors.mako.hpp',
828 c=self))
829 return self.render(
830 loader,
831 'elog.mako.cpp',
832 c=self,
833 indent=indent)
834
Ratan Gupta90bfaea2017-10-06 20:56:31 +0530835class Event(Callback, Renderer):
836 '''Handle the event callback config file directive.'''
837
838 def __init__(self, *a, **kw):
839 self.eventName = kw.pop('eventName')
840 self.eventMessage = kw.pop('eventMessage')
841 super(Event, self).__init__(**kw)
842
843 def construct(self, loader, indent):
844 return self.render(
845 loader,
846 'event.mako.cpp',
847 c=self,
848 indent=indent)
Gunnar Millsd5faea32017-08-08 14:19:36 -0500849
Marri Devender Rao80c70612018-04-12 09:22:55 -0500850class EventPath(PathCallback, Renderer):
851 '''Handle the event path callback config file directive.'''
852
853 def __init__(self, *a, **kw):
854 self.eventType = kw.pop('eventType')
855 super(EventPath, self).__init__(**kw)
856
857 def construct(self, loader, indent):
858 return self.render(
859 loader,
860 'eventpath.mako.cpp',
861 c=self,
862 indent=indent)
Matt Spinler3c5318d2018-02-19 14:03:05 -0600863
864class ElogWithMetadata(Callback, Renderer):
865 '''Handle the elog_with_metadata callback config file directive.'''
866
867 def __init__(self, *a, **kw):
868 self.error = kw.pop('error')
869 self.metadata = kw.pop('metadata')
870 super(ElogWithMetadata, self).__init__(**kw)
871
872 def construct(self, loader, indent):
873 with open('errors.hpp', 'a') as fd:
874 fd.write(
875 self.render(
876 loader,
877 'errors.mako.hpp',
878 c=self))
879 return self.render(
880 loader,
881 'elog_with_metadata.mako.cpp',
882 c=self,
883 indent=indent)
884
885
Matt Spinler1d6ca482017-11-01 10:48:02 -0500886class ResolveCallout(Callback, Renderer):
887 '''Handle the 'resolve callout' callback config file directive.'''
888
889 def __init__(self, *a, **kw):
890 self.callout = kw.pop('callout')
891 super(ResolveCallout, self).__init__(**kw)
892
893 def construct(self, loader, indent):
894 return self.render(
895 loader,
896 'resolve_errors.mako.cpp',
897 c=self,
898 indent=indent)
899
900
Brad Bishop0df00be2017-05-25 23:38:37 -0400901class Method(ConfigEntry, Renderer):
902 '''Handle the method callback config file directive.'''
903
904 def __init__(self, *a, **kw):
905 self.service = kw.pop('service')
906 self.path = kw.pop('path')
907 self.interface = kw.pop('interface')
908 self.method = kw.pop('method')
909 self.args = [TrivialArgument(**x) for x in kw.pop('args', {})]
910 super(Method, self).__init__(**kw)
911
912 def factory(self, objs):
913 args = {
914 'class': 'interface',
915 'interface': 'element',
916 'name': self.service
917 }
918 add_unique(ConfigEntry(
919 configfile=self.configfile, **args), objs)
920
921 args = {
922 'class': 'pathname',
923 'pathname': 'element',
924 'name': self.path
925 }
926 add_unique(ConfigEntry(
927 configfile=self.configfile, **args), objs)
928
929 args = {
930 'class': 'interface',
931 'interface': 'element',
932 'name': self.interface
933 }
934 add_unique(ConfigEntry(
935 configfile=self.configfile, **args), objs)
936
937 args = {
938 'class': 'propertyname',
939 'propertyname': 'element',
940 'name': self.method
941 }
942 add_unique(ConfigEntry(
943 configfile=self.configfile, **args), objs)
944
945 super(Method, self).factory(objs)
946
947 def setup(self, objs):
948 '''Resolve elements.'''
949
950 self.service = get_index(
951 objs,
952 'interface',
953 self.service)
954
955 self.path = get_index(
956 objs,
957 'pathname',
958 self.path)
959
960 self.interface = get_index(
961 objs,
962 'interface',
963 self.interface)
964
965 self.method = get_index(
966 objs,
967 'propertyname',
968 self.method)
969
970 super(Method, self).setup(objs)
971
972 def construct(self, loader, indent):
973 return self.render(
974 loader,
975 'method.mako.cpp',
976 c=self,
977 indent=indent)
978
979
Brad Bishop49e66172017-05-23 19:16:21 -0400980class CallbackGraphEntry(Group):
981 '''An entry in a traversal list for groups of callbacks.'''
982
983 def __init__(self, *a, **kw):
984 super(CallbackGraphEntry, self).__init__(**kw)
985
986 def setup(self, objs):
987 '''Resolve group members.'''
988
989 def map_member(x):
990 return get_index(
991 objs, 'callback', x, config=self.configfile)
992
993 self.members = map(
994 map_member,
995 self.members)
996
997 super(CallbackGraphEntry, self).setup(objs)
998
Marri Devender Rao80c70612018-04-12 09:22:55 -0500999class PathCallbackGraphEntry(Group):
1000 '''An entry in a traversal list for groups of callbacks.'''
1001
1002 def __init__(self, *a, **kw):
1003 super(PathCallbackGraphEntry, self).__init__(**kw)
1004
1005 def setup(self, objs):
1006 '''Resolve group members.'''
1007
1008 def map_member(x):
1009 return get_index(
1010 objs, 'pathcallback', x, config=self.configfile)
1011
1012 self.members = map(
1013 map_member,
1014 self.members)
1015
1016 super(PathCallbackGraphEntry, self).setup(objs)
Brad Bishop49e66172017-05-23 19:16:21 -04001017
1018class GroupOfCallbacks(ConfigEntry, Renderer):
1019 '''Handle the callback group config file directive.'''
1020
1021 def __init__(self, *a, **kw):
1022 self.members = kw.pop('members')
1023 super(GroupOfCallbacks, self).__init__(**kw)
1024
1025 def factory(self, objs):
1026 '''Create a graph instance for this group of callbacks.'''
1027
1028 args = {
1029 'configfile': self.configfile,
1030 'members': self.members,
1031 'class': 'callbackgroup',
1032 'callbackgroup': 'callback',
1033 'name': self.members
1034 }
1035
1036 entry = CallbackGraphEntry(**args)
1037 add_unique(entry, objs, config=self.configfile)
1038
1039 super(GroupOfCallbacks, self).factory(objs)
1040
1041 def setup(self, objs):
1042 '''Resolve graph entry.'''
1043
1044 self.graph = get_index(
1045 objs, 'callbackgroup', self.members, config=self.configfile)
1046
1047 super(GroupOfCallbacks, self).setup(objs)
1048
1049 def construct(self, loader, indent):
1050 return self.render(
1051 loader,
1052 'callbackgroup.mako.cpp',
1053 c=self,
1054 indent=indent)
1055
Marri Devender Rao80c70612018-04-12 09:22:55 -05001056class GroupOfPathCallbacks(ConfigEntry, Renderer):
1057 '''Handle the callback group config file directive.'''
1058
1059 def __init__(self, *a, **kw):
1060 self.members = kw.pop('members')
1061 super(GroupOfPathCallbacks, self).__init__(**kw)
1062
1063 def factory(self, objs):
1064 '''Create a graph instance for this group of callbacks.'''
1065
1066 args = {
1067 'configfile': self.configfile,
1068 'members': self.members,
1069 'class': 'pathcallbackgroup',
1070 'pathcallbackgroup': 'pathcallback',
1071 'name': self.members
1072 }
1073
1074 entry = PathCallbackGraphEntry(**args)
1075 add_unique(entry, objs, config=self.configfile)
1076 super(GroupOfPathCallbacks, self).factory(objs)
1077
1078 def setup(self, objs):
1079 '''Resolve graph entry.'''
1080
1081 self.graph = get_index(
1082 objs, 'callbackpathgroup', self.members, config=self.configfile)
1083
1084 super(GroupOfPathCallbacks, self).setup(objs)
1085
1086 def construct(self, loader, indent):
1087 return self.render(
1088 loader,
1089 'callbackpathgroup.mako.cpp',
1090 c=self,
1091 indent=indent)
Brad Bishop49e66172017-05-23 19:16:21 -04001092
Brad Bishop34a7acd2017-04-27 23:47:23 -04001093class Everything(Renderer):
1094 '''Parse/render entry point.'''
1095
1096 @staticmethod
Brad Bishop05b0c1e2017-05-23 00:24:01 -04001097 def classmap(cls, sub=None):
1098 '''Map render item class and subclass entries to the appropriate
1099 handler methods.'''
Brad Bishop05b0c1e2017-05-23 00:24:01 -04001100 class_map = {
Brad Bishop0e7df132017-05-23 17:58:12 -04001101 'path': {
1102 'element': Path,
1103 },
1104 'pathgroup': {
1105 'path': GroupOfPaths,
1106 },
Brad Bishope73b2c32017-05-23 18:01:54 -04001107 'propertygroup': {
1108 'property': GroupOfProperties,
1109 },
1110 'property': {
1111 'element': Property,
1112 },
Brad Bishop4b916f12017-05-23 18:06:38 -04001113 'watch': {
1114 'property': PropertyWatch,
1115 },
Marri Devender Rao80c70612018-04-12 09:22:55 -05001116 'pathwatch': {
1117 'path': PathWatch,
1118 },
Brad Bishop4b916f12017-05-23 18:06:38 -04001119 'instance': {
1120 'element': Instance,
1121 },
Marri Devender Rao80c70612018-04-12 09:22:55 -05001122 'pathinstance': {
1123 'element': PathInstance,
1124 },
Brad Bishopc1283ae2017-05-20 21:42:38 -04001125 'callback': {
1126 'journal': Journal,
Gunnar Millsd5faea32017-08-08 14:19:36 -05001127 'elog': Elog,
Matt Spinler3c5318d2018-02-19 14:03:05 -06001128 'elog_with_metadata': ElogWithMetadata,
Ratan Gupta90bfaea2017-10-06 20:56:31 +05301129 'event': Event,
Brad Bishop49e66172017-05-23 19:16:21 -04001130 'group': GroupOfCallbacks,
Brad Bishop0df00be2017-05-25 23:38:37 -04001131 'method': Method,
Matt Spinler1d6ca482017-11-01 10:48:02 -05001132 'resolve callout': ResolveCallout,
Brad Bishopc1283ae2017-05-20 21:42:38 -04001133 },
Marri Devender Rao80c70612018-04-12 09:22:55 -05001134 'pathcallback': {
1135 'eventpath': EventPath,
1136 'grouppath': GroupOfPathCallbacks,
1137 },
Brad Bishop4041d722017-05-21 10:06:07 -04001138 'condition': {
1139 'count': CountCondition,
Matthew Barthefdd03c2019-09-04 15:44:35 -05001140 'median': MedianCondition,
Brad Bishop4041d722017-05-21 10:06:07 -04001141 },
Brad Bishop05b0c1e2017-05-23 00:24:01 -04001142 }
1143
1144 if cls not in class_map:
1145 raise NotImplementedError('Unknown class: "{0}"'.format(cls))
1146 if sub not in class_map[cls]:
1147 raise NotImplementedError('Unknown {0} type: "{1}"'.format(
1148 cls, sub))
1149
1150 return class_map[cls][sub]
1151
1152 @staticmethod
1153 def load_one_yaml(path, fd, objs):
1154 '''Parse a single YAML file. Parsing occurs in three phases.
1155 In the first phase a factory method associated with each
1156 configuration file directive is invoked. These factory
1157 methods generate more factory methods. In the second
1158 phase the factory methods created in the first phase
1159 are invoked. In the last phase a callback is invoked on
1160 each object created in phase two. Typically the callback
1161 resolves references to other configuration file directives.'''
1162
1163 factory_objs = {}
1164 for x in yaml.safe_load(fd.read()) or {}:
1165
1166 # Create factory object for this config file directive.
1167 cls = x['class']
1168 sub = x.get(cls)
1169 if cls == 'group':
1170 cls = '{0}group'.format(sub)
1171
1172 factory = Everything.classmap(cls, sub)
1173 obj = factory(configfile=path, **x)
1174
1175 # For a given class of directive, validate the file
1176 # doesn't have any duplicate names (duplicates are
1177 # ok across config files).
1178 if exists(factory_objs, obj.cls, obj.name, config=path):
1179 raise NotUniqueError(path, cls, obj.name)
1180
1181 factory_objs.setdefault(cls, []).append(obj)
1182 objs.setdefault(cls, []).append(obj)
1183
1184 for cls, items in factory_objs.items():
1185 for obj in items:
1186 # Add objects for template consumption.
1187 obj.factory(objs)
1188
1189 @staticmethod
Brad Bishop34a7acd2017-04-27 23:47:23 -04001190 def load(args):
1191 '''Aggregate all the YAML in the input directory
1192 into a single aggregate.'''
1193
Brad Bishop05b0c1e2017-05-23 00:24:01 -04001194 objs = {}
1195 yaml_files = filter(
1196 lambda x: x.endswith('.yaml'),
1197 os.listdir(args.inputdir))
Brad Bishop34a7acd2017-04-27 23:47:23 -04001198
Brad Bishop05b0c1e2017-05-23 00:24:01 -04001199 yaml_files.sort()
Brad Bishop34a7acd2017-04-27 23:47:23 -04001200
Brad Bishop05b0c1e2017-05-23 00:24:01 -04001201 for x in yaml_files:
1202 path = os.path.join(args.inputdir, x)
1203 with open(path, 'r') as fd:
1204 Everything.load_one_yaml(path, fd, objs)
1205
1206 # Configuration file directives reference each other via
1207 # the name attribute; however, when rendered the reference
1208 # is just an array index.
1209 #
1210 # At this point all objects have been created but references
Gunnar Mills78199b42017-10-25 16:30:18 -05001211 # have not been resolved to array indices. Instruct objects
Brad Bishop05b0c1e2017-05-23 00:24:01 -04001212 # to do that now.
1213 for cls, items in objs.items():
1214 for obj in items:
1215 obj.setup(objs)
1216
1217 return Everything(**objs)
Brad Bishop34a7acd2017-04-27 23:47:23 -04001218
1219 def __init__(self, *a, **kw):
Brad Bishop0e7df132017-05-23 17:58:12 -04001220 self.pathmeta = kw.pop('path', [])
1221 self.paths = kw.pop('pathname', [])
1222 self.meta = kw.pop('meta', [])
1223 self.pathgroups = kw.pop('pathgroup', [])
Brad Bishope73b2c32017-05-23 18:01:54 -04001224 self.interfaces = kw.pop('interface', [])
1225 self.properties = kw.pop('property', [])
1226 self.propertynames = kw.pop('propertyname', [])
1227 self.propertygroups = kw.pop('propertygroup', [])
Brad Bishop4b916f12017-05-23 18:06:38 -04001228 self.instances = kw.pop('instance', [])
Marri Devender Rao80c70612018-04-12 09:22:55 -05001229 self.pathinstances = kw.pop('pathinstance', [])
Brad Bishop4b916f12017-05-23 18:06:38 -04001230 self.instancegroups = kw.pop('instancegroup', [])
Marri Devender Rao80c70612018-04-12 09:22:55 -05001231 self.pathinstancegroups = kw.pop('pathinstancegroup', [])
Brad Bishop4b916f12017-05-23 18:06:38 -04001232 self.watches = kw.pop('watch', [])
Marri Devender Rao80c70612018-04-12 09:22:55 -05001233 self.pathwatches = kw.pop('pathwatch', [])
Brad Bishopc1283ae2017-05-20 21:42:38 -04001234 self.callbacks = kw.pop('callback', [])
Marri Devender Rao80c70612018-04-12 09:22:55 -05001235 self.pathcallbacks = kw.pop('pathcallback', [])
Brad Bishop49e66172017-05-23 19:16:21 -04001236 self.callbackgroups = kw.pop('callbackgroup', [])
Marri Devender Rao80c70612018-04-12 09:22:55 -05001237 self.pathcallbackgroups = kw.pop('pathcallbackgroup', [])
Brad Bishop4041d722017-05-21 10:06:07 -04001238 self.conditions = kw.pop('condition', [])
Brad Bishop0e7df132017-05-23 17:58:12 -04001239
Brad Bishop34a7acd2017-04-27 23:47:23 -04001240 super(Everything, self).__init__(**kw)
1241
1242 def generate_cpp(self, loader):
1243 '''Render the template with the provided data.'''
Gunnar Millsd5faea32017-08-08 14:19:36 -05001244 # errors.hpp is used by generated.hpp to included any error.hpp files
1245 open('errors.hpp', 'w+')
1246
Brad Bishope3a01af2017-05-15 17:09:04 -04001247 with open(args.output, 'w') as fd:
Brad Bishop34a7acd2017-04-27 23:47:23 -04001248 fd.write(
1249 self.render(
1250 loader,
Brad Bishope3a01af2017-05-15 17:09:04 -04001251 args.template,
Brad Bishop0e7df132017-05-23 17:58:12 -04001252 meta=self.meta,
Brad Bishope73b2c32017-05-23 18:01:54 -04001253 properties=self.properties,
1254 propertynames=self.propertynames,
1255 interfaces=self.interfaces,
Brad Bishop0e7df132017-05-23 17:58:12 -04001256 paths=self.paths,
1257 pathmeta=self.pathmeta,
1258 pathgroups=self.pathgroups,
Brad Bishope73b2c32017-05-23 18:01:54 -04001259 propertygroups=self.propertygroups,
Brad Bishop4b916f12017-05-23 18:06:38 -04001260 instances=self.instances,
Marri Devender Rao80c70612018-04-12 09:22:55 -05001261 pathinstances=self.pathinstances,
Brad Bishop4b916f12017-05-23 18:06:38 -04001262 watches=self.watches,
Marri Devender Rao80c70612018-04-12 09:22:55 -05001263 pathwatches=self.pathwatches,
Brad Bishop4b916f12017-05-23 18:06:38 -04001264 instancegroups=self.instancegroups,
Marri Devender Rao80c70612018-04-12 09:22:55 -05001265 pathinstancegroups=self.pathinstancegroups,
Brad Bishopc1283ae2017-05-20 21:42:38 -04001266 callbacks=self.callbacks,
Marri Devender Rao80c70612018-04-12 09:22:55 -05001267 pathcallbacks=self.pathcallbacks,
Brad Bishop49e66172017-05-23 19:16:21 -04001268 callbackgroups=self.callbackgroups,
Marri Devender Rao80c70612018-04-12 09:22:55 -05001269 pathcallbackgroups=self.pathcallbackgroups,
Brad Bishop4041d722017-05-21 10:06:07 -04001270 conditions=self.conditions,
Brad Bishop34a7acd2017-04-27 23:47:23 -04001271 indent=Indent()))
Matthew Barthdb440d42017-04-17 15:49:37 -05001272
1273if __name__ == '__main__':
Brad Bishop34a7acd2017-04-27 23:47:23 -04001274 script_dir = os.path.dirname(os.path.realpath(__file__))
1275 valid_commands = {
1276 'generate-cpp': 'generate_cpp',
1277 }
1278
1279 parser = ArgumentParser(
1280 description='Phosphor DBus Monitor (PDM) YAML '
1281 'scanner and code generator.')
1282
Matthew Barthdb440d42017-04-17 15:49:37 -05001283 parser.add_argument(
Brad Bishope3a01af2017-05-15 17:09:04 -04001284 "-o", "--out", dest="output",
1285 default='generated.cpp',
1286 help="Generated output file name and path.")
1287 parser.add_argument(
1288 '-t', '--template', dest='template',
Brad Bishop870c3fc2017-05-22 23:23:13 -04001289 default='generated.mako.hpp',
Brad Bishope3a01af2017-05-15 17:09:04 -04001290 help='The top level template to render.')
1291 parser.add_argument(
1292 '-p', '--template-path', dest='template_search',
1293 default=script_dir,
1294 help='The space delimited mako template search path.')
Brad Bishop34a7acd2017-04-27 23:47:23 -04001295 parser.add_argument(
1296 '-d', '--dir', dest='inputdir',
1297 default=os.path.join(script_dir, 'example'),
1298 help='Location of files to process.')
1299 parser.add_argument(
1300 'command', metavar='COMMAND', type=str,
1301 choices=valid_commands.keys(),
1302 help='%s.' % " | ".join(valid_commands.keys()))
Matthew Barthdb440d42017-04-17 15:49:37 -05001303
Brad Bishop34a7acd2017-04-27 23:47:23 -04001304 args = parser.parse_args()
1305
1306 if sys.version_info < (3, 0):
1307 lookup = mako.lookup.TemplateLookup(
Brad Bishope3a01af2017-05-15 17:09:04 -04001308 directories=args.template_search.split(),
Brad Bishop34a7acd2017-04-27 23:47:23 -04001309 disable_unicode=True)
1310 else:
1311 lookup = mako.lookup.TemplateLookup(
Brad Bishope3a01af2017-05-15 17:09:04 -04001312 directories=args.template_search.split())
Brad Bishop05b0c1e2017-05-23 00:24:01 -04001313 try:
1314 function = getattr(
1315 Everything.load(args),
1316 valid_commands[args.command])
1317 function(lookup)
1318 except InvalidConfigError as e:
1319 sys.stdout.write('{0}: {1}\n\n'.format(e.config, e.msg))
1320 raise