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