blob: 76f4c6c60ee552b43b804d414e500ce3123a01fc [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. */
Matthew Barth34f1bda2017-05-31 13:45:36 -050016#include <sdbusplus/bus.hpp>
Matt Spinlerd08dbe22017-04-11 13:52:54 -050017#include "manager.hpp"
Matthew Barthba102b32017-05-16 16:13:56 -050018#include "functor.hpp"
19#include "actions.hpp"
Matthew Barth7e527c12017-05-17 10:14:15 -050020#include "handlers.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{
31 std::vector<Condition>{},
32 std::vector<ZoneDefinition>{
33 %for zone in zone_group['zones']:
34 ZoneDefinition{
35 ${zone['num']},
36 ${zone['full_speed']},
37 std::vector<FanDefinition>{
38 %for fan in zone['fans']:
39 FanDefinition{
40 "${fan['name']}",
41 std::vector<std::string>{
42 %for sensor in fan['sensors']:
43 "${sensor}",
44 %endfor
45 }
46 },
47 %endfor
48 },
49 std::vector<SetSpeedEvent>{
50 %for event in zone['events']:
51 SetSpeedEvent{
52 Group{
53 %for member in event['group']:
54 {
55 "${member['name']}",
56 {"${member['interface']}",
57 "${member['property']}"}
58 },
59 %endfor
60 },
61 make_action(action::${event['action']['name']}(
62 %for i, p in enumerate(event['action']['parameters']):
63 %if (i+1) != len(event['action']['parameters']):
64 static_cast<${p['type']}>(${p['value']}),
65 %else:
66 static_cast<${p['type']}>(${p['value']})
67 %endif
68 %endfor
69 )),
70 std::vector<PropertyChange>{
71 %for s in event['signal']:
72 PropertyChange{
Matthew Barth34f1bda2017-05-31 13:45:36 -050073 interface("org.freedesktop.DBus.Properties") +
74 member("PropertiesChanged") +
75 type::signal() +
76 path("${s['path']}") +
77 arg0namespace("${s['interface']}"),
Matthew Barth9c726672017-05-18 13:44:46 -050078 make_handler(propertySignal<${s['type']}>(
79 "${s['interface']}",
80 "${s['property']}",
81 handler::setProperty<${s['type']}>(
82 "${s['member']}",
83 "${s['property']}"
84 )
85 ))
86 },
87 %endfor
88 }
89 },
90 %endfor
91 }
92 },
93 %endfor
94 }
Matt Spinler78498c92017-04-11 13:59:46 -050095 },
Matt Spinlerd08dbe22017-04-11 13:52:54 -050096%endfor
97};
98'''
99
Matt Spinler78498c92017-04-11 13:59:46 -0500100
Matthew Barthd4d0f082017-05-16 13:51:10 -0500101def getEventsInZone(zone_num, events_data):
102 """
103 Constructs the event entries defined for each zone using the events yaml
104 provided.
105 """
106 events = []
Matthew Barthba102b32017-05-16 16:13:56 -0500107
Matthew Barthd4d0f082017-05-16 13:51:10 -0500108 if 'events' in events_data:
109 for e in events_data['events']:
110 for z in e['zone_conditions']:
111 if zone_num not in z['zones']:
112 continue
113
114 event = {}
115 # Add set speed event group
116 group = []
117 groups = next(g for g in events_data['groups']
118 if g['name'] == e['group'])
119 for member in groups['members']:
120 members = {}
Matthew Barth7e527c12017-05-17 10:14:15 -0500121 members['type'] = groups['type']
Matthew Barthd4d0f082017-05-16 13:51:10 -0500122 members['name'] = ("/xyz/openbmc_project/" +
123 groups['type'] +
124 member)
125 members['interface'] = e['interface']
126 members['property'] = e['property']['name']
127 group.append(members)
128 event['group'] = group
Matthew Barthba102b32017-05-16 16:13:56 -0500129
130 # Add set speed action and function parameters
131 action = {}
132 actions = next(a for a in events_data['actions']
Matthew Barth9c726672017-05-18 13:44:46 -0500133 if a['name'] == e['action']['name'])
Matthew Barthba102b32017-05-16 16:13:56 -0500134 action['name'] = actions['name']
135 params = []
136 for p in actions['parameters']:
137 param = {}
138 if type(e['action'][p]) is not dict:
139 if p == 'property':
140 param['value'] = str(e['action'][p]).lower()
141 param['type'] = str(e['property']['type']).lower()
142 else:
143 # Default type to 'size_t' when not given
144 param['value'] = str(e['action'][p]).lower()
145 param['type'] = 'size_t'
146 params.append(param)
147 else:
148 param['value'] = str(e['action'][p]['value']).lower()
149 param['type'] = str(e['action'][p]['type']).lower()
150 params.append(param)
151 action['parameters'] = params
152 event['action'] = action
153
Matthew Barth7e527c12017-05-17 10:14:15 -0500154 # Add property change signal handler
155 signal = []
156 for path in group:
157 signals = {}
158 signals['path'] = path['name']
159 signals['interface'] = e['interface']
160 signals['property'] = e['property']['name']
161 signals['type'] = e['property']['type']
162 signals['member'] = path['name']
163 signal.append(signals)
164 event['signal'] = signal
165
Matthew Barthd4d0f082017-05-16 13:51:10 -0500166 events.append(event)
167
168 return events
169
170
Matt Spinler78498c92017-04-11 13:59:46 -0500171def getFansInZone(zone_num, profiles, fan_data):
172 """
173 Parses the fan definition YAML files to find the fans
174 that match both the zone passed in and one of the
175 cooling profiles.
176 """
177
178 fans = []
179
180 for f in fan_data['fans']:
181
182 if zone_num != f['cooling_zone']:
183 continue
184
185 #'cooling_profile' is optional (use 'all' instead)
186 if f.get('cooling_profile') is None:
187 profile = "all"
188 else:
189 profile = f['cooling_profile']
190
191 if profile not in profiles:
192 continue
193
194 fan = {}
195 fan['name'] = f['inventory']
196 fan['sensors'] = f['sensors']
197 fans.append(fan)
198
199 return fans
200
201
Matthew Barthd4d0f082017-05-16 13:51:10 -0500202def buildZoneData(zone_data, fan_data, events_data):
Matt Spinler78498c92017-04-11 13:59:46 -0500203 """
204 Combines the zone definition YAML and fan
205 definition YAML to create a data structure defining
206 the fan cooling zones.
207 """
208
209 zone_groups = []
210
211 for group in zone_data:
212 conditions = []
213 for c in group['zone_conditions']:
214 conditions.append(c['name'])
215
216 zone_group = {}
217 zone_group['conditions'] = conditions
218
219 zones = []
220 for z in group['zones']:
221 zone = {}
222
223 #'zone' is required
224 if (not 'zone' in z) or (z['zone'] is None):
225 sys.exit("Missing fan zone number in " + zone_yaml)
226
227 zone['num'] = z['zone']
228
229 zone['full_speed'] = z['full_speed']
230
231 #'cooling_profiles' is optional (use 'all' instead)
232 if (not 'cooling_profiles' in z) or \
233 (z['cooling_profiles'] is None):
234 profiles = ["all"]
235 else:
236 profiles = z['cooling_profiles']
237
238 fans = getFansInZone(z['zone'], profiles, fan_data)
Matthew Barthd4d0f082017-05-16 13:51:10 -0500239 events = getEventsInZone(z['zone'], events_data)
Matt Spinler78498c92017-04-11 13:59:46 -0500240
241 if len(fans) == 0:
242 sys.exit("Didn't find any fans in zone " + str(zone['num']))
243
244 zone['fans'] = fans
Matthew Barthd4d0f082017-05-16 13:51:10 -0500245 zone['events'] = events
Matt Spinler78498c92017-04-11 13:59:46 -0500246 zones.append(zone)
247
248 zone_group['zones'] = zones
249 zone_groups.append(zone_group)
250
251 return zone_groups
252
253
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500254if __name__ == '__main__':
255 parser = ArgumentParser(
256 description="Phosphor fan zone definition parser")
257
258 parser.add_argument('-z', '--zone_yaml', dest='zone_yaml',
259 default="example/zones.yaml",
260 help='fan zone definitional yaml')
261 parser.add_argument('-f', '--fan_yaml', dest='fan_yaml',
262 default="example/fans.yaml",
263 help='fan definitional yaml')
Matthew Barthd4d0f082017-05-16 13:51:10 -0500264 parser.add_argument('-e', '--events_yaml', dest='events_yaml',
265 help='events to set speeds yaml')
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500266 parser.add_argument('-o', '--output_dir', dest='output_dir',
267 default=".",
268 help='output directory')
269 args = parser.parse_args()
270
271 if not args.zone_yaml or not args.fan_yaml:
272 parser.print_usage()
273 sys.exit(-1)
274
275 with open(args.zone_yaml, 'r') as zone_input:
276 zone_data = yaml.safe_load(zone_input) or {}
277
278 with open(args.fan_yaml, 'r') as fan_input:
279 fan_data = yaml.safe_load(fan_input) or {}
280
Matthew Barthd4d0f082017-05-16 13:51:10 -0500281 events_data = {}
282 if args.events_yaml:
283 with open(args.events_yaml, 'r') as events_input:
284 events_data = yaml.safe_load(events_input) or {}
285
Matt Spinleree7f6422017-05-09 11:03:14 -0500286 zone_config = buildZoneData(zone_data.get('zone_configuration', {}),
Matthew Barthd4d0f082017-05-16 13:51:10 -0500287 fan_data, events_data)
Matt Spinleree7f6422017-05-09 11:03:14 -0500288
289 manager_config = zone_data.get('manager_configuration', {})
290
291 if manager_config.get('power_on_delay') is None:
292 manager_config['power_on_delay'] = 0
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500293
294 output_file = os.path.join(args.output_dir, "fan_zone_defs.cpp")
295 with open(output_file, 'w') as output:
Matt Spinleree7f6422017-05-09 11:03:14 -0500296 output.write(Template(tmpl).render(zones=zone_config,
297 mgr_data=manager_config))