blob: f53771680b5a63789c4b1a1b74fe9d5fa1967167 [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 Spinleree7f6422017-05-09 11:03:14 -050014#Note: Condition is a TODO (openbmc/openbmc#1500)
Matt Spinlerd08dbe22017-04-11 13:52:54 -050015tmpl = '''/* This is a generated file. */
16#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;
22
Matt Spinleree7f6422017-05-09 11:03:14 -050023const unsigned int Manager::_powerOnDelay{${mgr_data['power_on_delay']}};
24
Matt Spinlerd08dbe22017-04-11 13:52:54 -050025const std::vector<ZoneGroup> Manager::_zoneLayouts
26{
27%for zone_group in zones:
Matthew Barth9c726672017-05-18 13:44:46 -050028 ZoneGroup{
29 std::vector<Condition>{},
30 std::vector<ZoneDefinition>{
31 %for zone in zone_group['zones']:
32 ZoneDefinition{
33 ${zone['num']},
34 ${zone['full_speed']},
35 std::vector<FanDefinition>{
36 %for fan in zone['fans']:
37 FanDefinition{
38 "${fan['name']}",
39 std::vector<std::string>{
40 %for sensor in fan['sensors']:
41 "${sensor}",
42 %endfor
43 }
44 },
45 %endfor
46 },
47 std::vector<SetSpeedEvent>{
48 %for event in zone['events']:
49 SetSpeedEvent{
50 Group{
51 %for member in event['group']:
52 {
53 "${member['name']}",
54 {"${member['interface']}",
55 "${member['property']}"}
56 },
57 %endfor
58 },
59 make_action(action::${event['action']['name']}(
60 %for i, p in enumerate(event['action']['parameters']):
61 %if (i+1) != len(event['action']['parameters']):
62 static_cast<${p['type']}>(${p['value']}),
63 %else:
64 static_cast<${p['type']}>(${p['value']})
65 %endif
66 %endfor
67 )),
68 std::vector<PropertyChange>{
69 %for s in event['signal']:
70 PropertyChange{
71 "interface='org.freedesktop.DBus.Properties',"
72 "member='PropertiesChanged',"
73 "type='signal',"
74 "path='${s['path']}'",
75 make_handler(propertySignal<${s['type']}>(
76 "${s['interface']}",
77 "${s['property']}",
78 handler::setProperty<${s['type']}>(
79 "${s['member']}",
80 "${s['property']}"
81 )
82 ))
83 },
84 %endfor
85 }
86 },
87 %endfor
88 }
89 },
90 %endfor
91 }
Matt Spinler78498c92017-04-11 13:59:46 -050092 },
Matt Spinlerd08dbe22017-04-11 13:52:54 -050093%endfor
94};
95'''
96
Matt Spinler78498c92017-04-11 13:59:46 -050097
Matthew Barthd4d0f082017-05-16 13:51:10 -050098def getEventsInZone(zone_num, events_data):
99 """
100 Constructs the event entries defined for each zone using the events yaml
101 provided.
102 """
103 events = []
Matthew Barthba102b32017-05-16 16:13:56 -0500104
Matthew Barthd4d0f082017-05-16 13:51:10 -0500105 if 'events' in events_data:
106 for e in events_data['events']:
107 for z in e['zone_conditions']:
108 if zone_num not in z['zones']:
109 continue
110
111 event = {}
112 # Add set speed event group
113 group = []
114 groups = next(g for g in events_data['groups']
115 if g['name'] == e['group'])
116 for member in groups['members']:
117 members = {}
Matthew Barth7e527c12017-05-17 10:14:15 -0500118 members['type'] = groups['type']
Matthew Barthd4d0f082017-05-16 13:51:10 -0500119 members['name'] = ("/xyz/openbmc_project/" +
120 groups['type'] +
121 member)
122 members['interface'] = e['interface']
123 members['property'] = e['property']['name']
124 group.append(members)
125 event['group'] = group
Matthew Barthba102b32017-05-16 16:13:56 -0500126
127 # Add set speed action and function parameters
128 action = {}
129 actions = next(a for a in events_data['actions']
Matthew Barth9c726672017-05-18 13:44:46 -0500130 if a['name'] == e['action']['name'])
Matthew Barthba102b32017-05-16 16:13:56 -0500131 action['name'] = actions['name']
132 params = []
133 for p in actions['parameters']:
134 param = {}
135 if type(e['action'][p]) is not dict:
136 if p == 'property':
137 param['value'] = str(e['action'][p]).lower()
138 param['type'] = str(e['property']['type']).lower()
139 else:
140 # Default type to 'size_t' when not given
141 param['value'] = str(e['action'][p]).lower()
142 param['type'] = 'size_t'
143 params.append(param)
144 else:
145 param['value'] = str(e['action'][p]['value']).lower()
146 param['type'] = str(e['action'][p]['type']).lower()
147 params.append(param)
148 action['parameters'] = params
149 event['action'] = action
150
Matthew Barth7e527c12017-05-17 10:14:15 -0500151 # Add property change signal handler
152 signal = []
153 for path in group:
154 signals = {}
155 signals['path'] = path['name']
156 signals['interface'] = e['interface']
157 signals['property'] = e['property']['name']
158 signals['type'] = e['property']['type']
159 signals['member'] = path['name']
160 signal.append(signals)
161 event['signal'] = signal
162
Matthew Barthd4d0f082017-05-16 13:51:10 -0500163 events.append(event)
164
165 return events
166
167
Matt Spinler78498c92017-04-11 13:59:46 -0500168def getFansInZone(zone_num, profiles, fan_data):
169 """
170 Parses the fan definition YAML files to find the fans
171 that match both the zone passed in and one of the
172 cooling profiles.
173 """
174
175 fans = []
176
177 for f in fan_data['fans']:
178
179 if zone_num != f['cooling_zone']:
180 continue
181
182 #'cooling_profile' is optional (use 'all' instead)
183 if f.get('cooling_profile') is None:
184 profile = "all"
185 else:
186 profile = f['cooling_profile']
187
188 if profile not in profiles:
189 continue
190
191 fan = {}
192 fan['name'] = f['inventory']
193 fan['sensors'] = f['sensors']
194 fans.append(fan)
195
196 return fans
197
198
Matthew Barthd4d0f082017-05-16 13:51:10 -0500199def buildZoneData(zone_data, fan_data, events_data):
Matt Spinler78498c92017-04-11 13:59:46 -0500200 """
201 Combines the zone definition YAML and fan
202 definition YAML to create a data structure defining
203 the fan cooling zones.
204 """
205
206 zone_groups = []
207
208 for group in zone_data:
209 conditions = []
210 for c in group['zone_conditions']:
211 conditions.append(c['name'])
212
213 zone_group = {}
214 zone_group['conditions'] = conditions
215
216 zones = []
217 for z in group['zones']:
218 zone = {}
219
220 #'zone' is required
221 if (not 'zone' in z) or (z['zone'] is None):
222 sys.exit("Missing fan zone number in " + zone_yaml)
223
224 zone['num'] = z['zone']
225
226 zone['full_speed'] = z['full_speed']
227
228 #'cooling_profiles' is optional (use 'all' instead)
229 if (not 'cooling_profiles' in z) or \
230 (z['cooling_profiles'] is None):
231 profiles = ["all"]
232 else:
233 profiles = z['cooling_profiles']
234
235 fans = getFansInZone(z['zone'], profiles, fan_data)
Matthew Barthd4d0f082017-05-16 13:51:10 -0500236 events = getEventsInZone(z['zone'], events_data)
Matt Spinler78498c92017-04-11 13:59:46 -0500237
238 if len(fans) == 0:
239 sys.exit("Didn't find any fans in zone " + str(zone['num']))
240
241 zone['fans'] = fans
Matthew Barthd4d0f082017-05-16 13:51:10 -0500242 zone['events'] = events
Matt Spinler78498c92017-04-11 13:59:46 -0500243 zones.append(zone)
244
245 zone_group['zones'] = zones
246 zone_groups.append(zone_group)
247
248 return zone_groups
249
250
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500251if __name__ == '__main__':
252 parser = ArgumentParser(
253 description="Phosphor fan zone definition parser")
254
255 parser.add_argument('-z', '--zone_yaml', dest='zone_yaml',
256 default="example/zones.yaml",
257 help='fan zone definitional yaml')
258 parser.add_argument('-f', '--fan_yaml', dest='fan_yaml',
259 default="example/fans.yaml",
260 help='fan definitional yaml')
Matthew Barthd4d0f082017-05-16 13:51:10 -0500261 parser.add_argument('-e', '--events_yaml', dest='events_yaml',
262 help='events to set speeds yaml')
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500263 parser.add_argument('-o', '--output_dir', dest='output_dir',
264 default=".",
265 help='output directory')
266 args = parser.parse_args()
267
268 if not args.zone_yaml or not args.fan_yaml:
269 parser.print_usage()
270 sys.exit(-1)
271
272 with open(args.zone_yaml, 'r') as zone_input:
273 zone_data = yaml.safe_load(zone_input) or {}
274
275 with open(args.fan_yaml, 'r') as fan_input:
276 fan_data = yaml.safe_load(fan_input) or {}
277
Matthew Barthd4d0f082017-05-16 13:51:10 -0500278 events_data = {}
279 if args.events_yaml:
280 with open(args.events_yaml, 'r') as events_input:
281 events_data = yaml.safe_load(events_input) or {}
282
Matt Spinleree7f6422017-05-09 11:03:14 -0500283 zone_config = buildZoneData(zone_data.get('zone_configuration', {}),
Matthew Barthd4d0f082017-05-16 13:51:10 -0500284 fan_data, events_data)
Matt Spinleree7f6422017-05-09 11:03:14 -0500285
286 manager_config = zone_data.get('manager_configuration', {})
287
288 if manager_config.get('power_on_delay') is None:
289 manager_config['power_on_delay'] = 0
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500290
291 output_file = os.path.join(args.output_dir, "fan_zone_defs.cpp")
292 with open(output_file, 'w') as output:
Matt Spinleree7f6422017-05-09 11:03:14 -0500293 output.write(Template(tmpl).render(zones=zone_config,
294 mgr_data=manager_config))