blob: ea05a0fc5526193ade8e169594277b094d5bb9d9 [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
Matt Spinlerd08dbe22017-04-11 13:52:54 -050014tmpl = '''/* This is a generated file. */
Matthew Barth34f1bda2017-05-31 13:45:36 -050015#include <sdbusplus/bus.hpp>
Matt Spinlerd08dbe22017-04-11 13:52:54 -050016#include "manager.hpp"
Matthew Barthba102b32017-05-16 16:13:56 -050017#include "functor.hpp"
18#include "actions.hpp"
Matthew Barth7e527c12017-05-17 10:14:15 -050019#include "handlers.hpp"
Matthew Barth9af190c2017-08-08 14:20:43 -050020#include "preconditions.hpp"
Matt Spinlerd08dbe22017-04-11 13:52:54 -050021
22using namespace phosphor::fan::control;
Matthew Barth34f1bda2017-05-31 13:45:36 -050023using namespace sdbusplus::bus::match::rules;
Matt Spinlerd08dbe22017-04-11 13:52:54 -050024
Matt Spinleree7f6422017-05-09 11:03:14 -050025const unsigned int Manager::_powerOnDelay{${mgr_data['power_on_delay']}};
26
Matt Spinlerd08dbe22017-04-11 13:52:54 -050027const std::vector<ZoneGroup> Manager::_zoneLayouts
28{
29%for zone_group in zones:
Matthew Barth9c726672017-05-18 13:44:46 -050030 ZoneGroup{
Gunnar Millsee8a2812017-06-02 14:26:47 -050031 std::vector<Condition>{
32 %for condition in zone_group['conditions']:
33 Condition{
34 "${condition['type']}",
35 std::vector<ConditionProperty>{
36 %for property in condition['properties']:
37 ConditionProperty{
38 "${property['property']}",
39 "${property['interface']}",
40 "${property['path']}",
41 static_cast<${property['type']}>(${property['value']}),
42 },
43 %endfor
44 },
45 },
46 %endfor
47 },
Matthew Barth9c726672017-05-18 13:44:46 -050048 std::vector<ZoneDefinition>{
49 %for zone in zone_group['zones']:
50 ZoneDefinition{
51 ${zone['num']},
52 ${zone['full_speed']},
Matthew Barth1de66622017-06-12 13:13:02 -050053 ${zone['default_floor']},
Matthew Bartha9561842017-06-29 11:43:45 -050054 ${zone['increase_delay']},
55 ${zone['decrease_interval']},
Matthew Barth9c726672017-05-18 13:44:46 -050056 std::vector<FanDefinition>{
57 %for fan in zone['fans']:
58 FanDefinition{
59 "${fan['name']}",
60 std::vector<std::string>{
61 %for sensor in fan['sensors']:
62 "${sensor}",
63 %endfor
64 }
65 },
66 %endfor
67 },
68 std::vector<SetSpeedEvent>{
69 %for event in zone['events']:
Matthew Barth9af190c2017-08-08 14:20:43 -050070 %if ('pc' in event) and \
71 (event['pc'] is not None):
72 SetSpeedEvent{
73 Group{
74 %for member in event['pc']['pcgrp']:
75 {
76 "${member['name']}",
77 {"${member['interface']}",
78 "${member['property']}"}
79 },
80 %endfor
81 },
82 make_action(
83 precondition::${event['pc']['pcact']['name']}(
84 %for i, p in enumerate(event['pc']['pcact']['params']):
85 ${p['type']}${p['open']}
86 %for j, v in enumerate(p['values']):
87 %if (j+1) != len(p['values']):
88 ${v['value']},
89 %else:
90 ${v['value']}
91 %endif
92 %endfor
93 ${p['close']},
94 %endfor
95 %endif
Matthew Barth9c726672017-05-18 13:44:46 -050096 SetSpeedEvent{
97 Group{
98 %for member in event['group']:
99 {
100 "${member['name']}",
101 {"${member['interface']}",
102 "${member['property']}"}
103 },
104 %endfor
105 },
106 make_action(action::${event['action']['name']}(
107 %for i, p in enumerate(event['action']['parameters']):
108 %if (i+1) != len(event['action']['parameters']):
109 static_cast<${p['type']}>(${p['value']}),
110 %else:
111 static_cast<${p['type']}>(${p['value']})
112 %endif
113 %endfor
114 )),
115 std::vector<PropertyChange>{
116 %for s in event['signal']:
117 PropertyChange{
Matthew Barth34f1bda2017-05-31 13:45:36 -0500118 interface("org.freedesktop.DBus.Properties") +
119 member("PropertiesChanged") +
120 type::signal() +
121 path("${s['path']}") +
122 arg0namespace("${s['interface']}"),
Matthew Barth9c726672017-05-18 13:44:46 -0500123 make_handler(propertySignal<${s['type']}>(
124 "${s['interface']}",
125 "${s['property']}",
126 handler::setProperty<${s['type']}>(
127 "${s['member']}",
Matthew Barthcec5ab72017-06-02 15:20:56 -0500128 "${s['interface']}",
Matthew Barth9c726672017-05-18 13:44:46 -0500129 "${s['property']}"
130 )
131 ))
132 },
133 %endfor
134 }
Matthew Barth9af190c2017-08-08 14:20:43 -0500135 %if ('pc' in event) and (event['pc'] is not None):
136 }
137 )),
138 std::vector<PropertyChange>{
139 %for s in event['pc']['pcsig']:
140 PropertyChange{
141 interfacesAdded("${s['obj_path']}"),
142 make_handler(objectSignal<${s['type']}>(
143 "${s['path']}",
144 "${s['interface']}",
145 "${s['property']}",
146 handler::setProperty<${s['type']}>(
147 "${s['path']}",
148 "${s['interface']}",
149 "${s['property']}"
150 )
151 ))
152 },
153 PropertyChange{
154 interface("org.freedesktop.DBus.Properties") +
155 member("PropertiesChanged") +
156 type::signal() +
157 path("${s['path']}") +
158 arg0namespace("${s['interface']}"),
159 make_handler(propertySignal<${s['type']}>(
160 "${s['interface']}",
161 "${s['property']}",
162 handler::setProperty<${s['type']}>(
163 "${s['path']}",
164 "${s['interface']}",
165 "${s['property']}"
166 )
167 ))
168 },
169 %endfor
170 }
171 %endif
Matthew Barth9c726672017-05-18 13:44:46 -0500172 },
173 %endfor
174 }
175 },
176 %endfor
177 }
Matt Spinler78498c92017-04-11 13:59:46 -0500178 },
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500179%endfor
180};
181'''
182
Matt Spinler78498c92017-04-11 13:59:46 -0500183
Matthew Barthbb12c922017-06-13 13:57:40 -0500184def convertToMap(listOfDict):
185 """
186 Converts a list of dictionary entries to a std::map initialization list.
187 """
188 listOfDict = listOfDict.replace('[', '{')
189 listOfDict = listOfDict.replace(']', '}')
190 listOfDict = listOfDict.replace(':', ',')
191 return listOfDict
192
193
Matthew Barth9af190c2017-08-08 14:20:43 -0500194def addPrecondition(event, events_data):
195 """
196 Parses the precondition section of an event and populates the necessary
197 structures to generate a precondition for a set speed event.
198 """
199 precond = {}
200 # Add set speed event precondition group
201 group = []
202 for grp in event['precondition']['groups']:
203 groups = next(g for g in events_data['groups']
204 if g['name'] == grp['name'])
205 for member in groups['members']:
206 members = {}
207 members['obj_path'] = groups['type']
208 members['name'] = (groups['type'] +
209 member)
210 members['interface'] = grp['interface']
211 members['property'] = grp['property']['name']
212 members['type'] = grp['property']['type']
213 members['value'] = grp['property']['value']
214 group.append(members)
215 precond['pcgrp'] = group
216
217 # Add set speed event precondition action
218 pc = {}
219 pc['name'] = event['precondition']['name']
220 pcs = next(p for p in events_data['preconditions']
221 if p['name'] == event['precondition']['name'])
222 params = []
223 for p in pcs['parameters']:
224 param = {}
225 if p == 'groups':
226 param['type'] = "std::vector<PrecondGroup>"
227 param['open'] = "{"
228 param['close'] = "}"
229 values = []
230 for pcgrp in group:
231 value = {}
232 value['value'] = (
233 "PrecondGroup{\"" +
234 str(pcgrp['name']) + "\",\"" +
235 str(pcgrp['interface']) + "\",\"" +
236 str(pcgrp['property']) + "\"," +
237 "static_cast<" +
238 str(pcgrp['type']).lower() + ">" +
239 "(" + str(pcgrp['value']).lower() + ")}")
240 values.append(value)
241 param['values'] = values
242 params.append(param)
243 pc['params'] = params
244 precond['pcact'] = pc
245
246 # Add precondition property change signal handler
247 signal = []
248 for member in group:
249 signals = {}
250 signals['obj_path'] = member['obj_path']
251 signals['path'] = member['name']
252 signals['interface'] = member['interface']
253 signals['property'] = member['property']
254 signals['type'] = member['type']
255 signal.append(signals)
256 precond['pcsig'] = signal
257
258 return precond
259
260
Gunnar Millsb751f322017-06-06 15:14:11 -0500261def getEventsInZone(zone_num, zone_conditions, events_data):
Matthew Barthd4d0f082017-05-16 13:51:10 -0500262 """
263 Constructs the event entries defined for each zone using the events yaml
264 provided.
265 """
266 events = []
Matthew Barthba102b32017-05-16 16:13:56 -0500267
Matthew Barthd4d0f082017-05-16 13:51:10 -0500268 if 'events' in events_data:
269 for e in events_data['events']:
Gunnar Millsb751f322017-06-06 15:14:11 -0500270
271 # Zone numbers are optional in the events yaml but skip if this
272 # zone's zone number is not in the event's zone numbers
273 if all('zones' in z and z['zones'] is not None and
274 zone_num not in z['zones'] for z in e['zone_conditions']):
275 continue
276
277 # Zone conditions are optional in the events yaml but skip if this
278 # event's condition is not in this zone's conditions
279 if all('name' in z and z['name'] is not None and
280 not any(c['name'] == z['name'] for c in zone_conditions)
281 for z in e['zone_conditions']):
282 continue
Matthew Barthd4d0f082017-05-16 13:51:10 -0500283
284 event = {}
Matthew Barth9af190c2017-08-08 14:20:43 -0500285 # Add precondition if given
286 if ('precondition' in e) and \
287 (e['precondition'] is not None):
288 event['pc'] = addPrecondition(e, events_data)
289
Matthew Barthd4d0f082017-05-16 13:51:10 -0500290 # Add set speed event group
291 group = []
292 groups = next(g for g in events_data['groups']
293 if g['name'] == e['group'])
294 for member in groups['members']:
295 members = {}
Matthew Barth7e527c12017-05-17 10:14:15 -0500296 members['type'] = groups['type']
Matthew Barth9af190c2017-08-08 14:20:43 -0500297 members['name'] = (groups['type'] +
Matthew Barthd4d0f082017-05-16 13:51:10 -0500298 member)
299 members['interface'] = e['interface']
300 members['property'] = e['property']['name']
301 group.append(members)
302 event['group'] = group
Matthew Barthba102b32017-05-16 16:13:56 -0500303
304 # Add set speed action and function parameters
305 action = {}
306 actions = next(a for a in events_data['actions']
Matthew Barth9c726672017-05-18 13:44:46 -0500307 if a['name'] == e['action']['name'])
Matthew Barthba102b32017-05-16 16:13:56 -0500308 action['name'] = actions['name']
309 params = []
310 for p in actions['parameters']:
311 param = {}
312 if type(e['action'][p]) is not dict:
313 if p == 'property':
314 param['value'] = str(e['action'][p]).lower()
315 param['type'] = str(e['property']['type']).lower()
316 else:
317 # Default type to 'size_t' when not given
318 param['value'] = str(e['action'][p]).lower()
319 param['type'] = 'size_t'
320 params.append(param)
321 else:
Matthew Barthba102b32017-05-16 16:13:56 -0500322 param['type'] = str(e['action'][p]['type']).lower()
Matthew Barthbb12c922017-06-13 13:57:40 -0500323 if p != 'map':
324 param['value'] = str(e['action'][p]['value']).lower()
325 else:
326 emap = convertToMap(str(e['action'][p]['value']))
327 param['value'] = param['type'] + emap
Matthew Barthba102b32017-05-16 16:13:56 -0500328 params.append(param)
329 action['parameters'] = params
330 event['action'] = action
331
Matthew Barth7e527c12017-05-17 10:14:15 -0500332 # Add property change signal handler
333 signal = []
334 for path in group:
335 signals = {}
336 signals['path'] = path['name']
337 signals['interface'] = e['interface']
338 signals['property'] = e['property']['name']
339 signals['type'] = e['property']['type']
340 signals['member'] = path['name']
341 signal.append(signals)
342 event['signal'] = signal
343
Matthew Barthd4d0f082017-05-16 13:51:10 -0500344 events.append(event)
345
346 return events
347
348
Matt Spinler78498c92017-04-11 13:59:46 -0500349def getFansInZone(zone_num, profiles, fan_data):
350 """
351 Parses the fan definition YAML files to find the fans
352 that match both the zone passed in and one of the
353 cooling profiles.
354 """
355
356 fans = []
357
358 for f in fan_data['fans']:
359
360 if zone_num != f['cooling_zone']:
361 continue
362
Gunnar Mills67e95512017-06-02 14:35:18 -0500363 # 'cooling_profile' is optional (use 'all' instead)
Matt Spinler78498c92017-04-11 13:59:46 -0500364 if f.get('cooling_profile') is None:
365 profile = "all"
366 else:
367 profile = f['cooling_profile']
368
369 if profile not in profiles:
370 continue
371
372 fan = {}
373 fan['name'] = f['inventory']
374 fan['sensors'] = f['sensors']
375 fans.append(fan)
376
377 return fans
378
379
Gunnar Millsee8a2812017-06-02 14:26:47 -0500380def getConditionInZoneConditions(zone_condition, zone_conditions_data):
381 """
382 Parses the zone conditions definition YAML files to find the condition
383 that match both the zone condition passed in.
384 """
385
386 condition = {}
387
388 for c in zone_conditions_data['conditions']:
389
390 if zone_condition != c['name']:
391 continue
392 condition['type'] = c['type']
393 properties = []
394 for p in c['properties']:
395 property = {}
396 property['property'] = p['property']
397 property['interface'] = p['interface']
398 property['path'] = p['path']
399 property['type'] = p['type'].lower()
400 property['value'] = str(p['value']).lower()
401 properties.append(property)
402 condition['properties'] = properties
403
404 return condition
405
406
407def buildZoneData(zone_data, fan_data, events_data, zone_conditions_data):
Matt Spinler78498c92017-04-11 13:59:46 -0500408 """
409 Combines the zone definition YAML and fan
410 definition YAML to create a data structure defining
411 the fan cooling zones.
412 """
413
414 zone_groups = []
415
416 for group in zone_data:
417 conditions = []
Gunnar Millsee8a2812017-06-02 14:26:47 -0500418 # zone conditions are optional
419 if 'zone_conditions' in group and group['zone_conditions'] is not None:
420 for c in group['zone_conditions']:
421
422 if not zone_conditions_data:
Gunnar Millsb751f322017-06-06 15:14:11 -0500423 sys.exit("No zone_conditions YAML file but " +
Gunnar Millsee8a2812017-06-02 14:26:47 -0500424 "zone_conditions used in zone YAML")
425
426 condition = getConditionInZoneConditions(c['name'],
427 zone_conditions_data)
428
429 if not condition:
430 sys.exit("Missing zone condition " + c['name'])
431
432 conditions.append(condition)
Matt Spinler78498c92017-04-11 13:59:46 -0500433
434 zone_group = {}
435 zone_group['conditions'] = conditions
436
437 zones = []
438 for z in group['zones']:
439 zone = {}
440
Gunnar Mills67e95512017-06-02 14:35:18 -0500441 # 'zone' is required
442 if ('zone' not in z) or (z['zone'] is None):
Matt Spinler78498c92017-04-11 13:59:46 -0500443 sys.exit("Missing fan zone number in " + zone_yaml)
444
445 zone['num'] = z['zone']
446
447 zone['full_speed'] = z['full_speed']
448
Matthew Barth1de66622017-06-12 13:13:02 -0500449 zone['default_floor'] = z['default_floor']
450
Matthew Bartha9561842017-06-29 11:43:45 -0500451 # 'increase_delay' is optional (use 0 by default)
452 key = 'increase_delay'
453 zone[key] = z.setdefault(key, 0)
454
455 # 'decrease_interval' is optional (use 0 by default)
456 key = 'decrease_interval'
457 zone[key] = z.setdefault(key, 0)
458
Gunnar Mills67e95512017-06-02 14:35:18 -0500459 # 'cooling_profiles' is optional (use 'all' instead)
460 if ('cooling_profiles' not in z) or \
Matt Spinler78498c92017-04-11 13:59:46 -0500461 (z['cooling_profiles'] is None):
462 profiles = ["all"]
463 else:
464 profiles = z['cooling_profiles']
465
466 fans = getFansInZone(z['zone'], profiles, fan_data)
Gunnar Millsb751f322017-06-06 15:14:11 -0500467 events = getEventsInZone(z['zone'], group['zone_conditions'],
468 events_data)
Matt Spinler78498c92017-04-11 13:59:46 -0500469
470 if len(fans) == 0:
471 sys.exit("Didn't find any fans in zone " + str(zone['num']))
472
473 zone['fans'] = fans
Matthew Barthd4d0f082017-05-16 13:51:10 -0500474 zone['events'] = events
Matt Spinler78498c92017-04-11 13:59:46 -0500475 zones.append(zone)
476
477 zone_group['zones'] = zones
478 zone_groups.append(zone_group)
479
480 return zone_groups
481
482
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500483if __name__ == '__main__':
484 parser = ArgumentParser(
485 description="Phosphor fan zone definition parser")
486
487 parser.add_argument('-z', '--zone_yaml', dest='zone_yaml',
488 default="example/zones.yaml",
489 help='fan zone definitional yaml')
490 parser.add_argument('-f', '--fan_yaml', dest='fan_yaml',
491 default="example/fans.yaml",
492 help='fan definitional yaml')
Matthew Barthd4d0f082017-05-16 13:51:10 -0500493 parser.add_argument('-e', '--events_yaml', dest='events_yaml',
494 help='events to set speeds yaml')
Gunnar Millsee8a2812017-06-02 14:26:47 -0500495 parser.add_argument('-c', '--zone_conditions_yaml',
496 dest='zone_conditions_yaml',
497 help='conditions to determine zone yaml')
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500498 parser.add_argument('-o', '--output_dir', dest='output_dir',
499 default=".",
500 help='output directory')
501 args = parser.parse_args()
502
503 if not args.zone_yaml or not args.fan_yaml:
504 parser.print_usage()
505 sys.exit(-1)
506
507 with open(args.zone_yaml, 'r') as zone_input:
508 zone_data = yaml.safe_load(zone_input) or {}
509
510 with open(args.fan_yaml, 'r') as fan_input:
511 fan_data = yaml.safe_load(fan_input) or {}
512
Matthew Barthd4d0f082017-05-16 13:51:10 -0500513 events_data = {}
514 if args.events_yaml:
515 with open(args.events_yaml, 'r') as events_input:
516 events_data = yaml.safe_load(events_input) or {}
517
Gunnar Millsee8a2812017-06-02 14:26:47 -0500518 zone_conditions_data = {}
519 if args.zone_conditions_yaml:
520 with open(args.zone_conditions_yaml, 'r') as zone_conditions_input:
521 zone_conditions_data = yaml.safe_load(zone_conditions_input) or {}
522
Matt Spinleree7f6422017-05-09 11:03:14 -0500523 zone_config = buildZoneData(zone_data.get('zone_configuration', {}),
Gunnar Millsee8a2812017-06-02 14:26:47 -0500524 fan_data, events_data, zone_conditions_data)
Matt Spinleree7f6422017-05-09 11:03:14 -0500525
526 manager_config = zone_data.get('manager_configuration', {})
527
528 if manager_config.get('power_on_delay') is None:
529 manager_config['power_on_delay'] = 0
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500530
531 output_file = os.path.join(args.output_dir, "fan_zone_defs.cpp")
532 with open(output_file, 'w') as output:
Matt Spinleree7f6422017-05-09 11:03:14 -0500533 output.write(Template(tmpl).render(zones=zone_config,
534 mgr_data=manager_config))