1#!/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 14#Note: Condition is a TODO (openbmc/openbmc#1500) 15tmpl = '''/* This is a generated file. */ 16#include "manager.hpp" 17 18using namespace phosphor::fan::control; 19 20const unsigned int Manager::_powerOnDelay{${mgr_data['power_on_delay']}}; 21 22const std::vector<ZoneGroup> Manager::_zoneLayouts 23{ 24%for zone_group in zones: 25 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 40 } 41 }, 42 %endfor 43 } 44 }, 45%endfor 46}; 47''' 48 49 50def getFansInZone(zone_num, profiles, fan_data): 51 """ 52 Parses the fan definition YAML files to find the fans 53 that match both the zone passed in and one of the 54 cooling profiles. 55 """ 56 57 fans = [] 58 59 for f in fan_data['fans']: 60 61 if zone_num != f['cooling_zone']: 62 continue 63 64 #'cooling_profile' is optional (use 'all' instead) 65 if f.get('cooling_profile') is None: 66 profile = "all" 67 else: 68 profile = f['cooling_profile'] 69 70 if profile not in profiles: 71 continue 72 73 fan = {} 74 fan['name'] = f['inventory'] 75 fan['sensors'] = f['sensors'] 76 fans.append(fan) 77 78 return fans 79 80 81def buildZoneData(zone_data, fan_data): 82 """ 83 Combines the zone definition YAML and fan 84 definition YAML to create a data structure defining 85 the fan cooling zones. 86 """ 87 88 zone_groups = [] 89 90 for group in zone_data: 91 conditions = [] 92 for c in group['zone_conditions']: 93 conditions.append(c['name']) 94 95 zone_group = {} 96 zone_group['conditions'] = conditions 97 98 zones = [] 99 for z in group['zones']: 100 zone = {} 101 102 #'zone' is required 103 if (not 'zone' in z) or (z['zone'] is None): 104 sys.exit("Missing fan zone number in " + zone_yaml) 105 106 zone['num'] = z['zone'] 107 108 zone['full_speed'] = z['full_speed'] 109 110 #'cooling_profiles' is optional (use 'all' instead) 111 if (not 'cooling_profiles' in z) or \ 112 (z['cooling_profiles'] is None): 113 profiles = ["all"] 114 else: 115 profiles = z['cooling_profiles'] 116 117 fans = getFansInZone(z['zone'], profiles, fan_data) 118 119 if len(fans) == 0: 120 sys.exit("Didn't find any fans in zone " + str(zone['num'])) 121 122 zone['fans'] = fans 123 zones.append(zone) 124 125 zone_group['zones'] = zones 126 zone_groups.append(zone_group) 127 128 return zone_groups 129 130 131if __name__ == '__main__': 132 parser = ArgumentParser( 133 description="Phosphor fan zone definition parser") 134 135 parser.add_argument('-z', '--zone_yaml', dest='zone_yaml', 136 default="example/zones.yaml", 137 help='fan zone definitional yaml') 138 parser.add_argument('-f', '--fan_yaml', dest='fan_yaml', 139 default="example/fans.yaml", 140 help='fan definitional yaml') 141 parser.add_argument('-o', '--output_dir', dest='output_dir', 142 default=".", 143 help='output directory') 144 args = parser.parse_args() 145 146 if not args.zone_yaml or not args.fan_yaml: 147 parser.print_usage() 148 sys.exit(-1) 149 150 with open(args.zone_yaml, 'r') as zone_input: 151 zone_data = yaml.safe_load(zone_input) or {} 152 153 with open(args.fan_yaml, 'r') as fan_input: 154 fan_data = yaml.safe_load(fan_input) or {} 155 156 zone_config = buildZoneData(zone_data.get('zone_configuration', {}), 157 fan_data) 158 159 manager_config = zone_data.get('manager_configuration', {}) 160 161 if manager_config.get('power_on_delay') is None: 162 manager_config['power_on_delay'] = 0 163 164 output_file = os.path.join(args.output_dir, "fan_zone_defs.cpp") 165 with open(output_file, 'w') as output: 166 output.write(Template(tmpl).render(zones=zone_config, 167 mgr_data=manager_config)) 168