blob: 221b3224d8dd866ca9060331dfac8fbd2c47eddb [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"
17
18using namespace phosphor::fan::control;
19
Matt Spinleree7f6422017-05-09 11:03:14 -050020const unsigned int Manager::_powerOnDelay{${mgr_data['power_on_delay']}};
21
Matt Spinlerd08dbe22017-04-11 13:52:54 -050022const std::vector<ZoneGroup> Manager::_zoneLayouts
23{
24%for zone_group in zones:
Matt Spinler78498c92017-04-11 13:59:46 -050025 ZoneGroup{std::vector<Condition>{},
26 std::vector<ZoneDefinition>{
27 %for zone in zone_group['zones']:
28 ZoneDefinition{${zone['num']},
29 ${zone['full_speed']},
30 std::vector<FanDefinition>{
31 %for fan in zone['fans']:
32 FanDefinition{"${fan['name']}",
33 std::vector<std::string>{
34 %for sensor in fan['sensors']:
35 "${sensor}",
36 %endfor
37 }
38 },
39 %endfor
Matthew Barthd4d0f082017-05-16 13:51:10 -050040 },
41 std::vector<SetSpeedEvent>{
42 %for event in zone['events']:
43 SetSpeedEvent{
44 Group{
45 %for member in event['group']:
46 {
47 "${member['name']}",
48 {"${member['interface']}",
49 "${member['property']}"}
50 },
51 %endfor
52 },
53 %endfor
Matt Spinler78498c92017-04-11 13:59:46 -050054 }
55 },
56 %endfor
57 }
58 },
Matt Spinlerd08dbe22017-04-11 13:52:54 -050059%endfor
60};
61'''
62
Matt Spinler78498c92017-04-11 13:59:46 -050063
Matthew Barthd4d0f082017-05-16 13:51:10 -050064def getEventsInZone(zone_num, events_data):
65 """
66 Constructs the event entries defined for each zone using the events yaml
67 provided.
68 """
69 events = []
70 if 'events' in events_data:
71 for e in events_data['events']:
72 for z in e['zone_conditions']:
73 if zone_num not in z['zones']:
74 continue
75
76 event = {}
77 # Add set speed event group
78 group = []
79 groups = next(g for g in events_data['groups']
80 if g['name'] == e['group'])
81 for member in groups['members']:
82 members = {}
83 members['name'] = ("/xyz/openbmc_project/" +
84 groups['type'] +
85 member)
86 members['interface'] = e['interface']
87 members['property'] = e['property']['name']
88 group.append(members)
89 event['group'] = group
90 events.append(event)
91
92 return events
93
94
Matt Spinler78498c92017-04-11 13:59:46 -050095def getFansInZone(zone_num, profiles, fan_data):
96 """
97 Parses the fan definition YAML files to find the fans
98 that match both the zone passed in and one of the
99 cooling profiles.
100 """
101
102 fans = []
103
104 for f in fan_data['fans']:
105
106 if zone_num != f['cooling_zone']:
107 continue
108
109 #'cooling_profile' is optional (use 'all' instead)
110 if f.get('cooling_profile') is None:
111 profile = "all"
112 else:
113 profile = f['cooling_profile']
114
115 if profile not in profiles:
116 continue
117
118 fan = {}
119 fan['name'] = f['inventory']
120 fan['sensors'] = f['sensors']
121 fans.append(fan)
122
123 return fans
124
125
Matthew Barthd4d0f082017-05-16 13:51:10 -0500126def buildZoneData(zone_data, fan_data, events_data):
Matt Spinler78498c92017-04-11 13:59:46 -0500127 """
128 Combines the zone definition YAML and fan
129 definition YAML to create a data structure defining
130 the fan cooling zones.
131 """
132
133 zone_groups = []
134
135 for group in zone_data:
136 conditions = []
137 for c in group['zone_conditions']:
138 conditions.append(c['name'])
139
140 zone_group = {}
141 zone_group['conditions'] = conditions
142
143 zones = []
144 for z in group['zones']:
145 zone = {}
146
147 #'zone' is required
148 if (not 'zone' in z) or (z['zone'] is None):
149 sys.exit("Missing fan zone number in " + zone_yaml)
150
151 zone['num'] = z['zone']
152
153 zone['full_speed'] = z['full_speed']
154
155 #'cooling_profiles' is optional (use 'all' instead)
156 if (not 'cooling_profiles' in z) or \
157 (z['cooling_profiles'] is None):
158 profiles = ["all"]
159 else:
160 profiles = z['cooling_profiles']
161
162 fans = getFansInZone(z['zone'], profiles, fan_data)
Matthew Barthd4d0f082017-05-16 13:51:10 -0500163 events = getEventsInZone(z['zone'], events_data)
Matt Spinler78498c92017-04-11 13:59:46 -0500164
165 if len(fans) == 0:
166 sys.exit("Didn't find any fans in zone " + str(zone['num']))
167
168 zone['fans'] = fans
Matthew Barthd4d0f082017-05-16 13:51:10 -0500169 zone['events'] = events
Matt Spinler78498c92017-04-11 13:59:46 -0500170 zones.append(zone)
171
172 zone_group['zones'] = zones
173 zone_groups.append(zone_group)
174
175 return zone_groups
176
177
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500178if __name__ == '__main__':
179 parser = ArgumentParser(
180 description="Phosphor fan zone definition parser")
181
182 parser.add_argument('-z', '--zone_yaml', dest='zone_yaml',
183 default="example/zones.yaml",
184 help='fan zone definitional yaml')
185 parser.add_argument('-f', '--fan_yaml', dest='fan_yaml',
186 default="example/fans.yaml",
187 help='fan definitional yaml')
Matthew Barthd4d0f082017-05-16 13:51:10 -0500188 parser.add_argument('-e', '--events_yaml', dest='events_yaml',
189 help='events to set speeds yaml')
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500190 parser.add_argument('-o', '--output_dir', dest='output_dir',
191 default=".",
192 help='output directory')
193 args = parser.parse_args()
194
195 if not args.zone_yaml or not args.fan_yaml:
196 parser.print_usage()
197 sys.exit(-1)
198
199 with open(args.zone_yaml, 'r') as zone_input:
200 zone_data = yaml.safe_load(zone_input) or {}
201
202 with open(args.fan_yaml, 'r') as fan_input:
203 fan_data = yaml.safe_load(fan_input) or {}
204
Matthew Barthd4d0f082017-05-16 13:51:10 -0500205 events_data = {}
206 if args.events_yaml:
207 with open(args.events_yaml, 'r') as events_input:
208 events_data = yaml.safe_load(events_input) or {}
209
Matt Spinleree7f6422017-05-09 11:03:14 -0500210 zone_config = buildZoneData(zone_data.get('zone_configuration', {}),
Matthew Barthd4d0f082017-05-16 13:51:10 -0500211 fan_data, events_data)
Matt Spinleree7f6422017-05-09 11:03:14 -0500212
213 manager_config = zone_data.get('manager_configuration', {})
214
215 if manager_config.get('power_on_delay') is None:
216 manager_config['power_on_delay'] = 0
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500217
218 output_file = os.path.join(args.output_dir, "fan_zone_defs.cpp")
219 with open(output_file, 'w') as output:
Matt Spinleree7f6422017-05-09 11:03:14 -0500220 output.write(Template(tmpl).render(zones=zone_config,
221 mgr_data=manager_config))