blob: a95920bdff9187bb9084c9b9bbb8278487ace3b7 [file] [log] [blame]
Matt Spinlerd08dbe22017-04-11 13:52:54 -05001#!/usr/bin/env python
2
3"""
4This script reads in fan definition and zone definition YAML
5files and generates a set of structures for use by the fan control code.
6"""
7
8import os
9import sys
10import yaml
11from argparse import ArgumentParser
12from mako.template import Template
13
Matthew Barth7f272fd2017-09-12 16:16:56 -050014tmpl = '''
15<%!
16def indent(str, depth):
17 return ''.join(4*' '*depth+line for line in str.splitlines(True))
18%>
19<%def name="genSSE(event)" buffered="True">
20Group{
21%for member in event['group']:
22{
23 "${member['name']}",
24 {"${member['interface']}",
25 "${member['property']}"}
26},
27%endfor
28},
29std::vector<Action>{
30%for a in event['action']:
31make_action(action::${a['name']}(
32%for i, p in enumerate(a['parameters']):
33%if (i+1) != len(a['parameters']):
34 static_cast<${p['type']}>(${p['value']}),
35%else:
36 static_cast<${p['type']}>(${p['value']})
37%endif
38%endfor
39)),
40%endfor
41},
42Timer{
43 ${event['timer']['interval']}
44},
45std::vector<PropertyChange>{
46%for s in event['signal']:
47 PropertyChange{
48 interfacesAdded("${s['obj_path']}"),
49 make_handler(objectSignal<${s['type']}>(
50 "${s['path']}",
51 "${s['interface']}",
52 "${s['property']}",
53 handler::setProperty<${s['type']}>(
54 "${s['path']}",
55 "${s['interface']}",
56 "${s['property']}"
57 )
58 ))
59 },
60 PropertyChange{
61 propertiesChanged(
62 "${s['path']}",
63 "${s['interface']}"),
64 make_handler(propertySignal<${s['type']}>(
65 "${s['interface']}",
66 "${s['property']}",
67 handler::setProperty<${s['type']}>(
68 "${s['path']}",
69 "${s['interface']}",
70 "${s['property']}"
71 )
72 ))
73 },
74%endfor
75}
76</%def>
77/* This is a generated file. */
Matthew Barth34f1bda2017-05-31 13:45:36 -050078#include <sdbusplus/bus.hpp>
Matt Spinlerd08dbe22017-04-11 13:52:54 -050079#include "manager.hpp"
Matthew Barthba102b32017-05-16 16:13:56 -050080#include "functor.hpp"
81#include "actions.hpp"
Matthew Barth7e527c12017-05-17 10:14:15 -050082#include "handlers.hpp"
Matthew Barth9af190c2017-08-08 14:20:43 -050083#include "preconditions.hpp"
Matt Spinlerd08dbe22017-04-11 13:52:54 -050084
85using namespace phosphor::fan::control;
Matthew Barth34f1bda2017-05-31 13:45:36 -050086using namespace sdbusplus::bus::match::rules;
Matt Spinlerd08dbe22017-04-11 13:52:54 -050087
Matt Spinleree7f6422017-05-09 11:03:14 -050088const unsigned int Manager::_powerOnDelay{${mgr_data['power_on_delay']}};
89
Matt Spinlerd08dbe22017-04-11 13:52:54 -050090const std::vector<ZoneGroup> Manager::_zoneLayouts
91{
92%for zone_group in zones:
Matthew Barth9c726672017-05-18 13:44:46 -050093 ZoneGroup{
Gunnar Millsee8a2812017-06-02 14:26:47 -050094 std::vector<Condition>{
95 %for condition in zone_group['conditions']:
96 Condition{
97 "${condition['type']}",
98 std::vector<ConditionProperty>{
99 %for property in condition['properties']:
100 ConditionProperty{
101 "${property['property']}",
102 "${property['interface']}",
103 "${property['path']}",
104 static_cast<${property['type']}>(${property['value']}),
105 },
106 %endfor
107 },
108 },
109 %endfor
110 },
Matthew Barth9c726672017-05-18 13:44:46 -0500111 std::vector<ZoneDefinition>{
112 %for zone in zone_group['zones']:
113 ZoneDefinition{
114 ${zone['num']},
115 ${zone['full_speed']},
Matthew Barth1de66622017-06-12 13:13:02 -0500116 ${zone['default_floor']},
Matthew Bartha9561842017-06-29 11:43:45 -0500117 ${zone['increase_delay']},
118 ${zone['decrease_interval']},
Matthew Barth9c726672017-05-18 13:44:46 -0500119 std::vector<FanDefinition>{
120 %for fan in zone['fans']:
121 FanDefinition{
122 "${fan['name']}",
123 std::vector<std::string>{
124 %for sensor in fan['sensors']:
125 "${sensor}",
126 %endfor
127 }
128 },
129 %endfor
130 },
131 std::vector<SetSpeedEvent>{
132 %for event in zone['events']:
Matthew Barth9af190c2017-08-08 14:20:43 -0500133 %if ('pc' in event) and \
134 (event['pc'] is not None):
135 SetSpeedEvent{
136 Group{
137 %for member in event['pc']['pcgrp']:
138 {
139 "${member['name']}",
140 {"${member['interface']}",
141 "${member['property']}"}
142 },
143 %endfor
144 },
Matthew Barth7f272fd2017-09-12 16:16:56 -0500145 std::vector<Action>{
146 %for i, a in enumerate(event['pc']['pcact']):
Matthew Barth9af190c2017-08-08 14:20:43 -0500147 make_action(
Matthew Barth7f272fd2017-09-12 16:16:56 -0500148 precondition::${a['name']}(
149 %for p in a['params']:
Matthew Barth9af190c2017-08-08 14:20:43 -0500150 ${p['type']}${p['open']}
151 %for j, v in enumerate(p['values']):
152 %if (j+1) != len(p['values']):
153 ${v['value']},
154 %else:
155 ${v['value']}
156 %endif
157 %endfor
158 ${p['close']},
159 %endfor
Matthew Barth7f272fd2017-09-12 16:16:56 -0500160 %if (i+1) != len(event['pc']['pcact']):
161 )),
Matthew Barth9c726672017-05-18 13:44:46 -0500162 %endif
163 %endfor
Matthew Barth7f272fd2017-09-12 16:16:56 -0500164 std::vector<SetSpeedEvent>{
165 %for pcevt in event['pc']['pcevts']:
166 SetSpeedEvent{\
167 ${indent(genSSE(event=pcevt), 6)}\
168 },
169 %endfor
170 %else:
171 SetSpeedEvent{\
172 ${indent(genSSE(event=event), 6)}
173 %endif
Matthew Barth9af190c2017-08-08 14:20:43 -0500174 %if ('pc' in event) and (event['pc'] is not None):
175 }
176 )),
Matthew Barth7f272fd2017-09-12 16:16:56 -0500177 },
Matthew Barth90149802017-08-15 10:51:37 -0500178 Timer{
179 ${event['pc']['pctime']['interval']}
180 },
Matthew Barth9af190c2017-08-08 14:20:43 -0500181 std::vector<PropertyChange>{
182 %for s in event['pc']['pcsig']:
183 PropertyChange{
184 interfacesAdded("${s['obj_path']}"),
185 make_handler(objectSignal<${s['type']}>(
186 "${s['path']}",
187 "${s['interface']}",
188 "${s['property']}",
189 handler::setProperty<${s['type']}>(
190 "${s['path']}",
191 "${s['interface']}",
192 "${s['property']}"
193 )
194 ))
195 },
196 PropertyChange{
Matthew Barthbe250832017-08-11 13:16:28 -0500197 propertiesChanged(
198 "${s['path']}",
199 "${s['interface']}"),
Matthew Barth9af190c2017-08-08 14:20:43 -0500200 make_handler(propertySignal<${s['type']}>(
201 "${s['interface']}",
202 "${s['property']}",
203 handler::setProperty<${s['type']}>(
204 "${s['path']}",
205 "${s['interface']}",
206 "${s['property']}"
207 )
208 ))
209 },
210 %endfor
211 }
212 %endif
Matthew Barth9c726672017-05-18 13:44:46 -0500213 },
214 %endfor
215 }
216 },
217 %endfor
218 }
Matt Spinler78498c92017-04-11 13:59:46 -0500219 },
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500220%endfor
221};
222'''
223
Matt Spinler78498c92017-04-11 13:59:46 -0500224
Matthew Barthbb12c922017-06-13 13:57:40 -0500225def convertToMap(listOfDict):
226 """
227 Converts a list of dictionary entries to a std::map initialization list.
228 """
229 listOfDict = listOfDict.replace('[', '{')
230 listOfDict = listOfDict.replace(']', '}')
231 listOfDict = listOfDict.replace(':', ',')
232 return listOfDict
233
234
Matthew Barth7f272fd2017-09-12 16:16:56 -0500235def getEvent(zone_num, zone_conditions, e, events_data):
236 """
237 Parses the sections of an event and populates the properties
238 that construct an event within the generated source.
239 """
240 event = {}
241 # Zone numbers are optional in the events yaml but skip if this
242 # zone's zone number is not in the event's zone numbers
243 if all('zones' in z and
244 z['zones'] is not None and
245 zone_num not in z['zones']
246 for z in e['zone_conditions']):
247 return
248
249 # Zone conditions are optional in the events yaml but skip
250 # if this event's condition is not in this zone's conditions
251 if all('name' in z and z['name'] is not None and
252 not any(c['name'] == z['name'] for c in zone_conditions)
253 for z in e['zone_conditions']):
254 return
255
256 # Add set speed event group
257 group = []
258 groups = next(g for g in events_data['groups']
259 if g['name'] == e['group'])
260 for member in groups['members']:
261 members = {}
262 members['obj_path'] = groups['type']
263 members['name'] = (groups['type'] +
264 member)
265 members['interface'] = e['interface']
266 members['property'] = e['property']['name']
267 group.append(members)
268 event['group'] = group
269
270 # Add set speed actions and function parameters
271 action = []
272 for eActions in e['actions']:
273 actions = {}
274 eAction = next(a for a in events_data['actions']
275 if a['name'] == eActions['name'])
276 actions['name'] = eAction['name']
277 params = []
278 for p in eAction['parameters']:
279 param = {}
280 if type(eActions[p]) is not dict:
281 if p == 'property':
282 param['value'] = str(eActions[p]).lower()
283 param['type'] = str(
284 e['property']['type']).lower()
285 else:
286 # Default type to 'size_t' when not given
287 param['value'] = str(eActions[p]).lower()
288 param['type'] = 'size_t'
289 params.append(param)
290 else:
291 param['type'] = str(eActions[p]['type']).lower()
292 if p != 'map':
293 param['value'] = str(
294 eActions[p]['value']).lower()
295 else:
296 emap = convertToMap(str(eActions[p]['value']))
297 param['value'] = param['type'] + emap
298 params.append(param)
299 actions['parameters'] = params
300 action.append(actions)
301 event['action'] = action
302
303 # Add property change signal handler
304 signal = []
305 for path in group:
306 signals = {}
307 signals['obj_path'] = path['obj_path']
308 signals['path'] = path['name']
309 signals['interface'] = e['interface']
310 signals['property'] = e['property']['name']
311 signals['type'] = e['property']['type']
312 signal.append(signals)
313 event['signal'] = signal
314
315 # Add optional action call timer
316 timer = {}
317 interval = "static_cast<std::chrono::seconds>"
318 if ('timer' in e) and \
319 (e['timer'] is not None):
320 timer['interval'] = (interval +
321 "(" +
322 str(e['timer']['interval']) +
323 ")")
324 else:
325 timer['interval'] = (interval +
326 "(" + str(0) + ")")
327 event['timer'] = timer
328
329 return event
330
331
332def addPrecondition(zNum, zCond, event, events_data):
Matthew Barth9af190c2017-08-08 14:20:43 -0500333 """
334 Parses the precondition section of an event and populates the necessary
335 structures to generate a precondition for a set speed event.
336 """
337 precond = {}
338 # Add set speed event precondition group
339 group = []
340 for grp in event['precondition']['groups']:
341 groups = next(g for g in events_data['groups']
342 if g['name'] == grp['name'])
343 for member in groups['members']:
344 members = {}
345 members['obj_path'] = groups['type']
346 members['name'] = (groups['type'] +
347 member)
348 members['interface'] = grp['interface']
349 members['property'] = grp['property']['name']
350 members['type'] = grp['property']['type']
351 members['value'] = grp['property']['value']
352 group.append(members)
353 precond['pcgrp'] = group
354
Matthew Barth7f272fd2017-09-12 16:16:56 -0500355 # Add set speed event precondition actions
356 pc = []
357 pcs = {}
358 pcs['name'] = event['precondition']['name']
359 epc = next(p for p in events_data['preconditions']
Matthew Barth9af190c2017-08-08 14:20:43 -0500360 if p['name'] == event['precondition']['name'])
361 params = []
Matthew Barth7f272fd2017-09-12 16:16:56 -0500362 for p in epc['parameters']:
Matthew Barth9af190c2017-08-08 14:20:43 -0500363 param = {}
364 if p == 'groups':
365 param['type'] = "std::vector<PrecondGroup>"
366 param['open'] = "{"
367 param['close'] = "}"
368 values = []
369 for pcgrp in group:
370 value = {}
371 value['value'] = (
372 "PrecondGroup{\"" +
373 str(pcgrp['name']) + "\",\"" +
374 str(pcgrp['interface']) + "\",\"" +
375 str(pcgrp['property']) + "\"," +
376 "static_cast<" +
377 str(pcgrp['type']).lower() + ">" +
378 "(" + str(pcgrp['value']).lower() + ")}")
379 values.append(value)
380 param['values'] = values
381 params.append(param)
Matthew Barth7f272fd2017-09-12 16:16:56 -0500382 pcs['params'] = params
383 pc.append(pcs)
Matthew Barth9af190c2017-08-08 14:20:43 -0500384 precond['pcact'] = pc
385
Matthew Barth7f272fd2017-09-12 16:16:56 -0500386 pcevents = []
387 for pce in event['precondition']['events']:
388 pcevent = getEvent(zNum, zCond, pce, events_data)
389 if not pcevent:
390 continue
391 pcevents.append(pcevent)
392 precond['pcevts'] = pcevents
393
Matthew Barth9af190c2017-08-08 14:20:43 -0500394 # Add precondition property change signal handler
395 signal = []
396 for member in group:
397 signals = {}
398 signals['obj_path'] = member['obj_path']
399 signals['path'] = member['name']
400 signals['interface'] = member['interface']
401 signals['property'] = member['property']
402 signals['type'] = member['type']
403 signal.append(signals)
404 precond['pcsig'] = signal
405
Matthew Barth90149802017-08-15 10:51:37 -0500406 # Add optional action call timer
407 timer = {}
408 interval = "static_cast<std::chrono::seconds>"
409 if ('timer' in event['precondition']) and \
410 (event['precondition']['timer'] is not None):
411 timer['interval'] = (interval +
412 "(" +
413 str(event['precondition']['timer']['interval']) +
414 ")")
415 else:
416 timer['interval'] = (interval +
417 "(" + str(0) + ")")
418 precond['pctime'] = timer
419
Matthew Barth9af190c2017-08-08 14:20:43 -0500420 return precond
421
422
Gunnar Millsb751f322017-06-06 15:14:11 -0500423def getEventsInZone(zone_num, zone_conditions, events_data):
Matthew Barthd4d0f082017-05-16 13:51:10 -0500424 """
425 Constructs the event entries defined for each zone using the events yaml
426 provided.
427 """
428 events = []
Matthew Barthba102b32017-05-16 16:13:56 -0500429
Matthew Barthd4d0f082017-05-16 13:51:10 -0500430 if 'events' in events_data:
431 for e in events_data['events']:
Matthew Barthd4d0f082017-05-16 13:51:10 -0500432 event = {}
Matthew Barth9af190c2017-08-08 14:20:43 -0500433 # Add precondition if given
434 if ('precondition' in e) and \
435 (e['precondition'] is not None):
Matthew Barth7f272fd2017-09-12 16:16:56 -0500436 event['pc'] = addPrecondition(zone_num,
437 zone_conditions,
438 e,
439 events_data)
Matthew Barth90149802017-08-15 10:51:37 -0500440 else:
Matthew Barth7f272fd2017-09-12 16:16:56 -0500441 event = getEvent(zone_num, zone_conditions, e, events_data)
442 if not event:
443 continue
Matthew Barthd4d0f082017-05-16 13:51:10 -0500444 events.append(event)
445
446 return events
447
448
Matt Spinler78498c92017-04-11 13:59:46 -0500449def getFansInZone(zone_num, profiles, fan_data):
450 """
451 Parses the fan definition YAML files to find the fans
452 that match both the zone passed in and one of the
453 cooling profiles.
454 """
455
456 fans = []
457
458 for f in fan_data['fans']:
459
460 if zone_num != f['cooling_zone']:
461 continue
462
Gunnar Mills67e95512017-06-02 14:35:18 -0500463 # 'cooling_profile' is optional (use 'all' instead)
Matt Spinler78498c92017-04-11 13:59:46 -0500464 if f.get('cooling_profile') is None:
465 profile = "all"
466 else:
467 profile = f['cooling_profile']
468
469 if profile not in profiles:
470 continue
471
472 fan = {}
473 fan['name'] = f['inventory']
474 fan['sensors'] = f['sensors']
475 fans.append(fan)
476
477 return fans
478
479
Gunnar Millsee8a2812017-06-02 14:26:47 -0500480def getConditionInZoneConditions(zone_condition, zone_conditions_data):
481 """
482 Parses the zone conditions definition YAML files to find the condition
483 that match both the zone condition passed in.
484 """
485
486 condition = {}
487
488 for c in zone_conditions_data['conditions']:
489
490 if zone_condition != c['name']:
491 continue
492 condition['type'] = c['type']
493 properties = []
494 for p in c['properties']:
495 property = {}
496 property['property'] = p['property']
497 property['interface'] = p['interface']
498 property['path'] = p['path']
499 property['type'] = p['type'].lower()
500 property['value'] = str(p['value']).lower()
501 properties.append(property)
502 condition['properties'] = properties
503
504 return condition
505
506
507def buildZoneData(zone_data, fan_data, events_data, zone_conditions_data):
Matt Spinler78498c92017-04-11 13:59:46 -0500508 """
509 Combines the zone definition YAML and fan
510 definition YAML to create a data structure defining
511 the fan cooling zones.
512 """
513
514 zone_groups = []
515
516 for group in zone_data:
517 conditions = []
Gunnar Millsee8a2812017-06-02 14:26:47 -0500518 # zone conditions are optional
519 if 'zone_conditions' in group and group['zone_conditions'] is not None:
520 for c in group['zone_conditions']:
521
522 if not zone_conditions_data:
Gunnar Millsb751f322017-06-06 15:14:11 -0500523 sys.exit("No zone_conditions YAML file but " +
Gunnar Millsee8a2812017-06-02 14:26:47 -0500524 "zone_conditions used in zone YAML")
525
526 condition = getConditionInZoneConditions(c['name'],
527 zone_conditions_data)
528
529 if not condition:
530 sys.exit("Missing zone condition " + c['name'])
531
532 conditions.append(condition)
Matt Spinler78498c92017-04-11 13:59:46 -0500533
534 zone_group = {}
535 zone_group['conditions'] = conditions
536
537 zones = []
538 for z in group['zones']:
539 zone = {}
540
Gunnar Mills67e95512017-06-02 14:35:18 -0500541 # 'zone' is required
542 if ('zone' not in z) or (z['zone'] is None):
Matt Spinler78498c92017-04-11 13:59:46 -0500543 sys.exit("Missing fan zone number in " + zone_yaml)
544
545 zone['num'] = z['zone']
546
547 zone['full_speed'] = z['full_speed']
548
Matthew Barth1de66622017-06-12 13:13:02 -0500549 zone['default_floor'] = z['default_floor']
550
Matthew Bartha9561842017-06-29 11:43:45 -0500551 # 'increase_delay' is optional (use 0 by default)
552 key = 'increase_delay'
553 zone[key] = z.setdefault(key, 0)
554
555 # 'decrease_interval' is optional (use 0 by default)
556 key = 'decrease_interval'
557 zone[key] = z.setdefault(key, 0)
558
Gunnar Mills67e95512017-06-02 14:35:18 -0500559 # 'cooling_profiles' is optional (use 'all' instead)
560 if ('cooling_profiles' not in z) or \
Matt Spinler78498c92017-04-11 13:59:46 -0500561 (z['cooling_profiles'] is None):
562 profiles = ["all"]
563 else:
564 profiles = z['cooling_profiles']
565
566 fans = getFansInZone(z['zone'], profiles, fan_data)
Gunnar Millsb751f322017-06-06 15:14:11 -0500567 events = getEventsInZone(z['zone'], group['zone_conditions'],
568 events_data)
Matt Spinler78498c92017-04-11 13:59:46 -0500569
570 if len(fans) == 0:
571 sys.exit("Didn't find any fans in zone " + str(zone['num']))
572
573 zone['fans'] = fans
Matthew Barthd4d0f082017-05-16 13:51:10 -0500574 zone['events'] = events
Matt Spinler78498c92017-04-11 13:59:46 -0500575 zones.append(zone)
576
577 zone_group['zones'] = zones
578 zone_groups.append(zone_group)
579
580 return zone_groups
581
582
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500583if __name__ == '__main__':
584 parser = ArgumentParser(
585 description="Phosphor fan zone definition parser")
586
587 parser.add_argument('-z', '--zone_yaml', dest='zone_yaml',
588 default="example/zones.yaml",
589 help='fan zone definitional yaml')
590 parser.add_argument('-f', '--fan_yaml', dest='fan_yaml',
591 default="example/fans.yaml",
592 help='fan definitional yaml')
Matthew Barthd4d0f082017-05-16 13:51:10 -0500593 parser.add_argument('-e', '--events_yaml', dest='events_yaml',
594 help='events to set speeds yaml')
Gunnar Millsee8a2812017-06-02 14:26:47 -0500595 parser.add_argument('-c', '--zone_conditions_yaml',
596 dest='zone_conditions_yaml',
597 help='conditions to determine zone yaml')
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500598 parser.add_argument('-o', '--output_dir', dest='output_dir',
599 default=".",
600 help='output directory')
601 args = parser.parse_args()
602
603 if not args.zone_yaml or not args.fan_yaml:
604 parser.print_usage()
605 sys.exit(-1)
606
607 with open(args.zone_yaml, 'r') as zone_input:
608 zone_data = yaml.safe_load(zone_input) or {}
609
610 with open(args.fan_yaml, 'r') as fan_input:
611 fan_data = yaml.safe_load(fan_input) or {}
612
Matthew Barthd4d0f082017-05-16 13:51:10 -0500613 events_data = {}
614 if args.events_yaml:
615 with open(args.events_yaml, 'r') as events_input:
616 events_data = yaml.safe_load(events_input) or {}
617
Gunnar Millsee8a2812017-06-02 14:26:47 -0500618 zone_conditions_data = {}
619 if args.zone_conditions_yaml:
620 with open(args.zone_conditions_yaml, 'r') as zone_conditions_input:
621 zone_conditions_data = yaml.safe_load(zone_conditions_input) or {}
622
Matt Spinleree7f6422017-05-09 11:03:14 -0500623 zone_config = buildZoneData(zone_data.get('zone_configuration', {}),
Gunnar Millsee8a2812017-06-02 14:26:47 -0500624 fan_data, events_data, zone_conditions_data)
Matt Spinleree7f6422017-05-09 11:03:14 -0500625
626 manager_config = zone_data.get('manager_configuration', {})
627
628 if manager_config.get('power_on_delay') is None:
629 manager_config['power_on_delay'] = 0
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500630
631 output_file = os.path.join(args.output_dir, "fan_zone_defs.cpp")
632 with open(output_file, 'w') as output:
Matt Spinleree7f6422017-05-09 11:03:14 -0500633 output.write(Template(tmpl).render(zones=zone_config,
634 mgr_data=manager_config))