1#!/usr/bin/env python3 2 3import os 4import sys 5from argparse import ArgumentParser 6 7import yaml 8from mako.template import Template 9 10""" 11This script generates the data structures for the 12phosphor-fan-monitor application. 13 14A future improvement is to get the fan inventory names 15from a separate file, so just that file could be generated 16from the MRW. 17""" 18 19 20tmpl = """\ 21<%! 22def indent(str, depth): 23 return ''.join(4*' '*depth+line for line in str.splitlines(True)) 24%>\ 25<%def name="getCondParams(cond)" buffered="True"> 26%if (cond['name'] == 'propertiesMatch'): 27std::vector<PropertyState>{ 28 %for i in cond['properties']: 29 PropertyState{ 30 { 31 "${i['object']}", 32 "${i['interface']}", 33 "${i['property']['name']}" 34 }, 35 static_cast<${i['property']['type']}>(${str(i['property']['value']).lower()}) 36 }, 37 %endfor 38} 39%endif 40</%def>\ 41/* This is a generated file. */ 42#include "fan_defs.hpp" 43#include "types.hpp" 44#include "groups.hpp" 45#include "conditions.hpp" 46 47using namespace phosphor::fan::monitor; 48using namespace phosphor::fan::trust; 49 50const std::vector<FanDefinition> fanDefinitions 51{ 52%for fan_data in data.get('fans', {}): 53 FanDefinition{"${fan_data['inventory']}", 54 ${fan_data.get('method', {})}, 55 ${fan_data.get('functional_delay', 0)}, 56 ${fan_data.get('allowed_out_of_range_time', {})}, 57 ${fan_data['deviation']}, 58 ${fan_data['num_sensors_nonfunc_for_fan_nonfunc']}, 59 0, // Monitor start delay - not used in YAML configs 60 0, // Count interval - not used in YAML configs 61 std::nullopt, // nonfuncRotorErrorDelay - also not used here 62 std::nullopt, // fanMissingErrorDelay - also not used here 63 std::vector<SensorDefinition>{ 64 %for sensor in fan_data['sensors']: 65 <% 66 #has_target is a bool, and we need a true instead of True 67 has_target = str(sensor['has_target']).lower() 68 target_interface = sensor.get( 69 'target_interface', 70 'xyz.openbmc_project.Control.FanSpeed') 71 target_path = sensor.get( 72 'target_path', 73 '') 74 factor = sensor.get('factor', 1) 75 offset = sensor.get('offset', 0) 76 threshold = sensor.get('threshold', 1) 77 ignore_above_max = str(sensor.get( 78 'ignore_above_max', False)).lower() 79 %> \ 80 SensorDefinition{"${sensor['name']}", 81 ${has_target}, 82 "${target_interface}", 83 "${target_path}", 84 ${factor}, 85 ${offset}, 86 ${threshold}, 87 ${ignore_above_max}}, 88 %endfor 89 }, 90 %if ('condition' in fan_data) and \ 91 (fan_data['condition'] is not None): 92 make_condition(condition::${fan_data['condition']['name']}(\ 93 ${indent(getCondParams(cond=fan_data['condition']), 5)}\ 94 )), 95 %else: 96 {}, 97 %endif 98 false // set_func_on_present. Hardcoded to false. 99 }, 100%endfor 101}; 102 103##Function to generate the group creation lambda. 104##If a group were to ever need a different constructor, 105##it could be handled here. 106<%def name="get_lambda_contents(group)"> 107 std::vector<GroupDefinition> group{ 108 %for member in group['group']: 109 <% 110 in_trust = str(member.get('in_trust', "true")).lower() 111 %> 112 GroupDefinition{"${member['name']}", ${in_trust}}, 113 %endfor 114 }; 115 return std::make_unique<${group['class']}>(group); 116</%def> 117const std::vector<CreateGroupFunction> trustGroups 118{ 119%for group in data.get('sensor_trust_groups', {}): 120 { 121 []() 122 {\ 123${get_lambda_contents(group)}\ 124 } 125 }, 126%endfor 127}; 128""" 129 130 131if __name__ == "__main__": 132 parser = ArgumentParser( 133 description="Phosphor fan monitor definition parser" 134 ) 135 136 parser.add_argument( 137 "-m", 138 "--monitor_yaml", 139 dest="monitor_yaml", 140 default="example/monitor.yaml", 141 help="fan monitor definitional yaml", 142 ) 143 parser.add_argument( 144 "-o", 145 "--output_dir", 146 dest="output_dir", 147 default=".", 148 help="output directory", 149 ) 150 args = parser.parse_args() 151 152 if not args.monitor_yaml: 153 parser.print_usage() 154 sys.exit(1) 155 156 with open(args.monitor_yaml, "r") as monitor_input: 157 monitor_data = yaml.safe_load(monitor_input) or {} 158 159 # Do some minor input validation 160 for fan in monitor_data.get("fans", {}): 161 if (fan["deviation"] < 0) or (fan["deviation"] > 100): 162 sys.exit("Invalid deviation value " + str(fan["deviation"])) 163 164 output_file = os.path.join(args.output_dir, "fan_monitor_defs.cpp") 165 with open(output_file, "w") as output: 166 output.write(Template(tmpl).render(data=monitor_data)) 167