blob: 82eecb4a13481e7b93fad75269241db7bd0e1013 [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"
Matt Spinlerd08dbe22017-04-11 13:52:54 -050020
21using namespace phosphor::fan::control;
Matthew Barth34f1bda2017-05-31 13:45:36 -050022using namespace sdbusplus::bus::match::rules;
Matt Spinlerd08dbe22017-04-11 13:52:54 -050023
Matt Spinleree7f6422017-05-09 11:03:14 -050024const unsigned int Manager::_powerOnDelay{${mgr_data['power_on_delay']}};
25
Matt Spinlerd08dbe22017-04-11 13:52:54 -050026const std::vector<ZoneGroup> Manager::_zoneLayouts
27{
28%for zone_group in zones:
Matthew Barth9c726672017-05-18 13:44:46 -050029 ZoneGroup{
Gunnar Millsee8a2812017-06-02 14:26:47 -050030 std::vector<Condition>{
31 %for condition in zone_group['conditions']:
32 Condition{
33 "${condition['type']}",
34 std::vector<ConditionProperty>{
35 %for property in condition['properties']:
36 ConditionProperty{
37 "${property['property']}",
38 "${property['interface']}",
39 "${property['path']}",
40 static_cast<${property['type']}>(${property['value']}),
41 },
42 %endfor
43 },
44 },
45 %endfor
46 },
Matthew Barth9c726672017-05-18 13:44:46 -050047 std::vector<ZoneDefinition>{
48 %for zone in zone_group['zones']:
49 ZoneDefinition{
50 ${zone['num']},
51 ${zone['full_speed']},
52 std::vector<FanDefinition>{
53 %for fan in zone['fans']:
54 FanDefinition{
55 "${fan['name']}",
56 std::vector<std::string>{
57 %for sensor in fan['sensors']:
58 "${sensor}",
59 %endfor
60 }
61 },
62 %endfor
63 },
64 std::vector<SetSpeedEvent>{
65 %for event in zone['events']:
66 SetSpeedEvent{
67 Group{
68 %for member in event['group']:
69 {
70 "${member['name']}",
71 {"${member['interface']}",
72 "${member['property']}"}
73 },
74 %endfor
75 },
76 make_action(action::${event['action']['name']}(
77 %for i, p in enumerate(event['action']['parameters']):
78 %if (i+1) != len(event['action']['parameters']):
79 static_cast<${p['type']}>(${p['value']}),
80 %else:
81 static_cast<${p['type']}>(${p['value']})
82 %endif
83 %endfor
84 )),
85 std::vector<PropertyChange>{
86 %for s in event['signal']:
87 PropertyChange{
Matthew Barth34f1bda2017-05-31 13:45:36 -050088 interface("org.freedesktop.DBus.Properties") +
89 member("PropertiesChanged") +
90 type::signal() +
91 path("${s['path']}") +
92 arg0namespace("${s['interface']}"),
Matthew Barth9c726672017-05-18 13:44:46 -050093 make_handler(propertySignal<${s['type']}>(
94 "${s['interface']}",
95 "${s['property']}",
96 handler::setProperty<${s['type']}>(
97 "${s['member']}",
98 "${s['property']}"
99 )
100 ))
101 },
102 %endfor
103 }
104 },
105 %endfor
106 }
107 },
108 %endfor
109 }
Matt Spinler78498c92017-04-11 13:59:46 -0500110 },
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500111%endfor
112};
113'''
114
Matt Spinler78498c92017-04-11 13:59:46 -0500115
Matthew Barthd4d0f082017-05-16 13:51:10 -0500116def getEventsInZone(zone_num, events_data):
117 """
118 Constructs the event entries defined for each zone using the events yaml
119 provided.
120 """
121 events = []
Matthew Barthba102b32017-05-16 16:13:56 -0500122
Matthew Barthd4d0f082017-05-16 13:51:10 -0500123 if 'events' in events_data:
124 for e in events_data['events']:
125 for z in e['zone_conditions']:
126 if zone_num not in z['zones']:
127 continue
128
129 event = {}
130 # Add set speed event group
131 group = []
132 groups = next(g for g in events_data['groups']
133 if g['name'] == e['group'])
134 for member in groups['members']:
135 members = {}
Matthew Barth7e527c12017-05-17 10:14:15 -0500136 members['type'] = groups['type']
Matthew Barthd4d0f082017-05-16 13:51:10 -0500137 members['name'] = ("/xyz/openbmc_project/" +
138 groups['type'] +
139 member)
140 members['interface'] = e['interface']
141 members['property'] = e['property']['name']
142 group.append(members)
143 event['group'] = group
Matthew Barthba102b32017-05-16 16:13:56 -0500144
145 # Add set speed action and function parameters
146 action = {}
147 actions = next(a for a in events_data['actions']
Matthew Barth9c726672017-05-18 13:44:46 -0500148 if a['name'] == e['action']['name'])
Matthew Barthba102b32017-05-16 16:13:56 -0500149 action['name'] = actions['name']
150 params = []
151 for p in actions['parameters']:
152 param = {}
153 if type(e['action'][p]) is not dict:
154 if p == 'property':
155 param['value'] = str(e['action'][p]).lower()
156 param['type'] = str(e['property']['type']).lower()
157 else:
158 # Default type to 'size_t' when not given
159 param['value'] = str(e['action'][p]).lower()
160 param['type'] = 'size_t'
161 params.append(param)
162 else:
163 param['value'] = str(e['action'][p]['value']).lower()
164 param['type'] = str(e['action'][p]['type']).lower()
165 params.append(param)
166 action['parameters'] = params
167 event['action'] = action
168
Matthew Barth7e527c12017-05-17 10:14:15 -0500169 # Add property change signal handler
170 signal = []
171 for path in group:
172 signals = {}
173 signals['path'] = path['name']
174 signals['interface'] = e['interface']
175 signals['property'] = e['property']['name']
176 signals['type'] = e['property']['type']
177 signals['member'] = path['name']
178 signal.append(signals)
179 event['signal'] = signal
180
Matthew Barthd4d0f082017-05-16 13:51:10 -0500181 events.append(event)
182
183 return events
184
185
Matt Spinler78498c92017-04-11 13:59:46 -0500186def getFansInZone(zone_num, profiles, fan_data):
187 """
188 Parses the fan definition YAML files to find the fans
189 that match both the zone passed in and one of the
190 cooling profiles.
191 """
192
193 fans = []
194
195 for f in fan_data['fans']:
196
197 if zone_num != f['cooling_zone']:
198 continue
199
200 #'cooling_profile' is optional (use 'all' instead)
201 if f.get('cooling_profile') is None:
202 profile = "all"
203 else:
204 profile = f['cooling_profile']
205
206 if profile not in profiles:
207 continue
208
209 fan = {}
210 fan['name'] = f['inventory']
211 fan['sensors'] = f['sensors']
212 fans.append(fan)
213
214 return fans
215
216
Gunnar Millsee8a2812017-06-02 14:26:47 -0500217def getConditionInZoneConditions(zone_condition, zone_conditions_data):
218 """
219 Parses the zone conditions definition YAML files to find the condition
220 that match both the zone condition passed in.
221 """
222
223 condition = {}
224
225 for c in zone_conditions_data['conditions']:
226
227 if zone_condition != c['name']:
228 continue
229 condition['type'] = c['type']
230 properties = []
231 for p in c['properties']:
232 property = {}
233 property['property'] = p['property']
234 property['interface'] = p['interface']
235 property['path'] = p['path']
236 property['type'] = p['type'].lower()
237 property['value'] = str(p['value']).lower()
238 properties.append(property)
239 condition['properties'] = properties
240
241 return condition
242
243
244def buildZoneData(zone_data, fan_data, events_data, zone_conditions_data):
Matt Spinler78498c92017-04-11 13:59:46 -0500245 """
246 Combines the zone definition YAML and fan
247 definition YAML to create a data structure defining
248 the fan cooling zones.
249 """
250
251 zone_groups = []
252
253 for group in zone_data:
254 conditions = []
Gunnar Millsee8a2812017-06-02 14:26:47 -0500255 # zone conditions are optional
256 if 'zone_conditions' in group and group['zone_conditions'] is not None:
257 for c in group['zone_conditions']:
258
259 if not zone_conditions_data:
260 sys.exit("No zone_conditions YAML file but" +
261 "zone_conditions used in zone YAML")
262
263 condition = getConditionInZoneConditions(c['name'],
264 zone_conditions_data)
265
266 if not condition:
267 sys.exit("Missing zone condition " + c['name'])
268
269 conditions.append(condition)
Matt Spinler78498c92017-04-11 13:59:46 -0500270
271 zone_group = {}
272 zone_group['conditions'] = conditions
273
274 zones = []
275 for z in group['zones']:
276 zone = {}
277
278 #'zone' is required
279 if (not 'zone' in z) or (z['zone'] is None):
280 sys.exit("Missing fan zone number in " + zone_yaml)
281
282 zone['num'] = z['zone']
283
284 zone['full_speed'] = z['full_speed']
285
286 #'cooling_profiles' is optional (use 'all' instead)
287 if (not 'cooling_profiles' in z) or \
288 (z['cooling_profiles'] is None):
289 profiles = ["all"]
290 else:
291 profiles = z['cooling_profiles']
292
293 fans = getFansInZone(z['zone'], profiles, fan_data)
Matthew Barthd4d0f082017-05-16 13:51:10 -0500294 events = getEventsInZone(z['zone'], events_data)
Matt Spinler78498c92017-04-11 13:59:46 -0500295
296 if len(fans) == 0:
297 sys.exit("Didn't find any fans in zone " + str(zone['num']))
298
299 zone['fans'] = fans
Matthew Barthd4d0f082017-05-16 13:51:10 -0500300 zone['events'] = events
Matt Spinler78498c92017-04-11 13:59:46 -0500301 zones.append(zone)
302
303 zone_group['zones'] = zones
304 zone_groups.append(zone_group)
305
306 return zone_groups
307
308
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500309if __name__ == '__main__':
310 parser = ArgumentParser(
311 description="Phosphor fan zone definition parser")
312
313 parser.add_argument('-z', '--zone_yaml', dest='zone_yaml',
314 default="example/zones.yaml",
315 help='fan zone definitional yaml')
316 parser.add_argument('-f', '--fan_yaml', dest='fan_yaml',
317 default="example/fans.yaml",
318 help='fan definitional yaml')
Matthew Barthd4d0f082017-05-16 13:51:10 -0500319 parser.add_argument('-e', '--events_yaml', dest='events_yaml',
320 help='events to set speeds yaml')
Gunnar Millsee8a2812017-06-02 14:26:47 -0500321 parser.add_argument('-c', '--zone_conditions_yaml',
322 dest='zone_conditions_yaml',
323 help='conditions to determine zone yaml')
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500324 parser.add_argument('-o', '--output_dir', dest='output_dir',
325 default=".",
326 help='output directory')
327 args = parser.parse_args()
328
329 if not args.zone_yaml or not args.fan_yaml:
330 parser.print_usage()
331 sys.exit(-1)
332
333 with open(args.zone_yaml, 'r') as zone_input:
334 zone_data = yaml.safe_load(zone_input) or {}
335
336 with open(args.fan_yaml, 'r') as fan_input:
337 fan_data = yaml.safe_load(fan_input) or {}
338
Matthew Barthd4d0f082017-05-16 13:51:10 -0500339 events_data = {}
340 if args.events_yaml:
341 with open(args.events_yaml, 'r') as events_input:
342 events_data = yaml.safe_load(events_input) or {}
343
Gunnar Millsee8a2812017-06-02 14:26:47 -0500344 zone_conditions_data = {}
345 if args.zone_conditions_yaml:
346 with open(args.zone_conditions_yaml, 'r') as zone_conditions_input:
347 zone_conditions_data = yaml.safe_load(zone_conditions_input) or {}
348
Matt Spinleree7f6422017-05-09 11:03:14 -0500349 zone_config = buildZoneData(zone_data.get('zone_configuration', {}),
Gunnar Millsee8a2812017-06-02 14:26:47 -0500350 fan_data, events_data, zone_conditions_data)
Matt Spinleree7f6422017-05-09 11:03:14 -0500351
352 manager_config = zone_data.get('manager_configuration', {})
353
354 if manager_config.get('power_on_delay') is None:
355 manager_config['power_on_delay'] = 0
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500356
357 output_file = os.path.join(args.output_dir, "fan_zone_defs.cpp")
358 with open(output_file, 'w') as output:
Matt Spinleree7f6422017-05-09 11:03:14 -0500359 output.write(Template(tmpl).render(zones=zone_config,
360 mgr_data=manager_config))