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.get('upper_deviation', fan_data['deviation'])}, 59 ${fan_data['num_sensors_nonfunc_for_fan_nonfunc']}, 60 0, // Monitor start delay - not used in YAML configs 61 0, // Count interval - not used in YAML configs 62 std::nullopt, // nonfuncRotorErrorDelay - also not used here 63 std::nullopt, // fanMissingErrorDelay - also not used here 64 std::vector<SensorDefinition>{ 65 %for sensor in fan_data['sensors']: 66 <% 67 #has_target is a bool, and we need a true instead of True 68 has_target = str(sensor['has_target']).lower() 69 target_interface = sensor.get( 70 'target_interface', 71 'xyz.openbmc_project.Control.FanSpeed') 72 target_path = sensor.get( 73 'target_path', 74 '') 75 factor = sensor.get('factor', 1) 76 offset = sensor.get('offset', 0) 77 threshold = sensor.get('threshold', 1) 78 ignore_above_max = str(sensor.get( 79 'ignore_above_max', False)).lower() 80 %> \ 81 SensorDefinition{"${sensor['name']}", 82 ${has_target}, 83 "${target_interface}", 84 "${target_path}", 85 ${factor}, 86 ${offset}, 87 ${threshold}, 88 ${ignore_above_max}}, 89 %endfor 90 }, 91 %if ('condition' in fan_data) and \ 92 (fan_data['condition'] is not None): 93 make_condition(condition::${fan_data['condition']['name']}(\ 94 ${indent(getCondParams(cond=fan_data['condition']), 5)}\ 95 )), 96 %else: 97 {}, 98 %endif 99 false // set_func_on_present. Hardcoded to false. 100 }, 101%endfor 102}; 103 104##Function to generate the group creation lambda. 105##If a group were to ever need a different constructor, 106##it could be handled here. 107<%def name="get_lambda_contents(group)"> 108 std::vector<GroupDefinition> group{ 109 %for member in group['group']: 110 <% 111 in_trust = str(member.get('in_trust', "true")).lower() 112 %> 113 GroupDefinition{"${member['name']}", ${in_trust}}, 114 %endfor 115 }; 116 return std::make_unique<${group['class']}>(group); 117</%def> 118const std::vector<CreateGroupFunction> trustGroups 119{ 120%for group in data.get('sensor_trust_groups', {}): 121 { 122 []() 123 {\ 124${get_lambda_contents(group)}\ 125 } 126 }, 127%endfor 128}; 129""" 130 131 132if __name__ == "__main__": 133 parser = ArgumentParser( 134 description="Phosphor fan monitor definition parser" 135 ) 136 137 parser.add_argument( 138 "-m", 139 "--monitor_yaml", 140 dest="monitor_yaml", 141 default="example/monitor.yaml", 142 help="fan monitor definitional yaml", 143 ) 144 parser.add_argument( 145 "-o", 146 "--output_dir", 147 dest="output_dir", 148 default=".", 149 help="output directory", 150 ) 151 args = parser.parse_args() 152 153 if not args.monitor_yaml: 154 parser.print_usage() 155 sys.exit(1) 156 157 with open(args.monitor_yaml, "r") as monitor_input: 158 monitor_data = yaml.safe_load(monitor_input) or {} 159 160 # Do some minor input validation 161 for fan in monitor_data.get("fans", {}): 162 if (fan["deviation"] < 0) or (fan["deviation"] > 100): 163 sys.exit("Invalid deviation value " + str(fan["deviation"])) 164 165 output_file = os.path.join(args.output_dir, "fan_monitor_defs.cpp") 166 with open(output_file, "w") as output: 167 output.write(Template(tmpl).render(data=monitor_data)) 168