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