blob: a779f34f8a4664e5dd13dbff6a53375c9224d7ff [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:
Matt Spinler78498c92017-04-11 13:59:46 -050028 ZoneGroup{std::vector<Condition>{},
29 std::vector<ZoneDefinition>{
30 %for zone in zone_group['zones']:
31 ZoneDefinition{${zone['num']},
32 ${zone['full_speed']},
33 std::vector<FanDefinition>{
34 %for fan in zone['fans']:
35 FanDefinition{"${fan['name']}",
36 std::vector<std::string>{
37 %for sensor in fan['sensors']:
38 "${sensor}",
39 %endfor
40 }
41 },
42 %endfor
Matthew Barthd4d0f082017-05-16 13:51:10 -050043 },
44 std::vector<SetSpeedEvent>{
45 %for event in zone['events']:
46 SetSpeedEvent{
47 Group{
48 %for member in event['group']:
49 {
50 "${member['name']}",
51 {"${member['interface']}",
52 "${member['property']}"}
53 },
54 %endfor
Matthew Barthba102b32017-05-16 16:13:56 -050055 },
56 make_action(action::${event['action']['name']}(
57 %for index, param in enumerate(event['action']['parameters']):
58 %if (index+1) != len(event['action']['parameters']):
59 static_cast<${param['type']}>(${param['value']}),
60 %else:
61 static_cast<${param['type']}>(${param['value']})
62 %endif
63 %endfor
64 )),
Matthew Barth7e527c12017-05-17 10:14:15 -050065 std::vector<PropertyChange>{
66 %for signal in event['signal']:
67 PropertyChange{
68 "interface='org.freedesktop.DBus.Properties',"
69 "member='PropertiesChanged',"
70 "type='signal',"
71 "path='${signal['path']}'",
72 make_handler(propertySignal<${signal['type']}>(
73 "${signal['interface']}",
74 "${signal['property']}",
75 handler::setProperty<${signal['type']}>(
76 "${signal['member']}",
77 "${signal['property']}"
78 )
79 ))},
80 %endfor
81 }
Matthew Barthd4d0f082017-05-16 13:51:10 -050082 },
83 %endfor
Matt Spinler78498c92017-04-11 13:59:46 -050084 }
85 },
86 %endfor
87 }
88 },
Matt Spinlerd08dbe22017-04-11 13:52:54 -050089%endfor
90};
91'''
92
Matt Spinler78498c92017-04-11 13:59:46 -050093
Matthew Barthd4d0f082017-05-16 13:51:10 -050094def getEventsInZone(zone_num, events_data):
95 """
96 Constructs the event entries defined for each zone using the events yaml
97 provided.
98 """
99 events = []
Matthew Barthba102b32017-05-16 16:13:56 -0500100
Matthew Barthd4d0f082017-05-16 13:51:10 -0500101 if 'events' in events_data:
102 for e in events_data['events']:
103 for z in e['zone_conditions']:
104 if zone_num not in z['zones']:
105 continue
106
107 event = {}
108 # Add set speed event group
109 group = []
110 groups = next(g for g in events_data['groups']
111 if g['name'] == e['group'])
112 for member in groups['members']:
113 members = {}
Matthew Barth7e527c12017-05-17 10:14:15 -0500114 members['type'] = groups['type']
Matthew Barthd4d0f082017-05-16 13:51:10 -0500115 members['name'] = ("/xyz/openbmc_project/" +
116 groups['type'] +
117 member)
118 members['interface'] = e['interface']
119 members['property'] = e['property']['name']
120 group.append(members)
121 event['group'] = group
Matthew Barthba102b32017-05-16 16:13:56 -0500122
123 # Add set speed action and function parameters
124 action = {}
125 actions = next(a for a in events_data['actions']
126 if a['name'] == e['action']['name'])
127 action['name'] = actions['name']
128 params = []
129 for p in actions['parameters']:
130 param = {}
131 if type(e['action'][p]) is not dict:
132 if p == 'property':
133 param['value'] = str(e['action'][p]).lower()
134 param['type'] = str(e['property']['type']).lower()
135 else:
136 # Default type to 'size_t' when not given
137 param['value'] = str(e['action'][p]).lower()
138 param['type'] = 'size_t'
139 params.append(param)
140 else:
141 param['value'] = str(e['action'][p]['value']).lower()
142 param['type'] = str(e['action'][p]['type']).lower()
143 params.append(param)
144 action['parameters'] = params
145 event['action'] = action
146
Matthew Barth7e527c12017-05-17 10:14:15 -0500147 # Add property change signal handler
148 signal = []
149 for path in group:
150 signals = {}
151 signals['path'] = path['name']
152 signals['interface'] = e['interface']
153 signals['property'] = e['property']['name']
154 signals['type'] = e['property']['type']
155 signals['member'] = path['name']
156 signal.append(signals)
157 event['signal'] = signal
158
Matthew Barthd4d0f082017-05-16 13:51:10 -0500159 events.append(event)
160
161 return events
162
163
Matt Spinler78498c92017-04-11 13:59:46 -0500164def getFansInZone(zone_num, profiles, fan_data):
165 """
166 Parses the fan definition YAML files to find the fans
167 that match both the zone passed in and one of the
168 cooling profiles.
169 """
170
171 fans = []
172
173 for f in fan_data['fans']:
174
175 if zone_num != f['cooling_zone']:
176 continue
177
178 #'cooling_profile' is optional (use 'all' instead)
179 if f.get('cooling_profile') is None:
180 profile = "all"
181 else:
182 profile = f['cooling_profile']
183
184 if profile not in profiles:
185 continue
186
187 fan = {}
188 fan['name'] = f['inventory']
189 fan['sensors'] = f['sensors']
190 fans.append(fan)
191
192 return fans
193
194
Matthew Barthd4d0f082017-05-16 13:51:10 -0500195def buildZoneData(zone_data, fan_data, events_data):
Matt Spinler78498c92017-04-11 13:59:46 -0500196 """
197 Combines the zone definition YAML and fan
198 definition YAML to create a data structure defining
199 the fan cooling zones.
200 """
201
202 zone_groups = []
203
204 for group in zone_data:
205 conditions = []
206 for c in group['zone_conditions']:
207 conditions.append(c['name'])
208
209 zone_group = {}
210 zone_group['conditions'] = conditions
211
212 zones = []
213 for z in group['zones']:
214 zone = {}
215
216 #'zone' is required
217 if (not 'zone' in z) or (z['zone'] is None):
218 sys.exit("Missing fan zone number in " + zone_yaml)
219
220 zone['num'] = z['zone']
221
222 zone['full_speed'] = z['full_speed']
223
224 #'cooling_profiles' is optional (use 'all' instead)
225 if (not 'cooling_profiles' in z) or \
226 (z['cooling_profiles'] is None):
227 profiles = ["all"]
228 else:
229 profiles = z['cooling_profiles']
230
231 fans = getFansInZone(z['zone'], profiles, fan_data)
Matthew Barthd4d0f082017-05-16 13:51:10 -0500232 events = getEventsInZone(z['zone'], events_data)
Matt Spinler78498c92017-04-11 13:59:46 -0500233
234 if len(fans) == 0:
235 sys.exit("Didn't find any fans in zone " + str(zone['num']))
236
237 zone['fans'] = fans
Matthew Barthd4d0f082017-05-16 13:51:10 -0500238 zone['events'] = events
Matt Spinler78498c92017-04-11 13:59:46 -0500239 zones.append(zone)
240
241 zone_group['zones'] = zones
242 zone_groups.append(zone_group)
243
244 return zone_groups
245
246
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500247if __name__ == '__main__':
248 parser = ArgumentParser(
249 description="Phosphor fan zone definition parser")
250
251 parser.add_argument('-z', '--zone_yaml', dest='zone_yaml',
252 default="example/zones.yaml",
253 help='fan zone definitional yaml')
254 parser.add_argument('-f', '--fan_yaml', dest='fan_yaml',
255 default="example/fans.yaml",
256 help='fan definitional yaml')
Matthew Barthd4d0f082017-05-16 13:51:10 -0500257 parser.add_argument('-e', '--events_yaml', dest='events_yaml',
258 help='events to set speeds yaml')
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500259 parser.add_argument('-o', '--output_dir', dest='output_dir',
260 default=".",
261 help='output directory')
262 args = parser.parse_args()
263
264 if not args.zone_yaml or not args.fan_yaml:
265 parser.print_usage()
266 sys.exit(-1)
267
268 with open(args.zone_yaml, 'r') as zone_input:
269 zone_data = yaml.safe_load(zone_input) or {}
270
271 with open(args.fan_yaml, 'r') as fan_input:
272 fan_data = yaml.safe_load(fan_input) or {}
273
Matthew Barthd4d0f082017-05-16 13:51:10 -0500274 events_data = {}
275 if args.events_yaml:
276 with open(args.events_yaml, 'r') as events_input:
277 events_data = yaml.safe_load(events_input) or {}
278
Matt Spinleree7f6422017-05-09 11:03:14 -0500279 zone_config = buildZoneData(zone_data.get('zone_configuration', {}),
Matthew Barthd4d0f082017-05-16 13:51:10 -0500280 fan_data, events_data)
Matt Spinleree7f6422017-05-09 11:03:14 -0500281
282 manager_config = zone_data.get('manager_configuration', {})
283
284 if manager_config.get('power_on_delay') is None:
285 manager_config['power_on_delay'] = 0
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500286
287 output_file = os.path.join(args.output_dir, "fan_zone_defs.cpp")
288 with open(output_file, 'w') as output:
Matt Spinleree7f6422017-05-09 11:03:14 -0500289 output.write(Template(tmpl).render(zones=zone_config,
290 mgr_data=manager_config))