#!/usr/bin/env python3 import os import sys from argparse import ArgumentParser import yaml from mako.template import Template """ This script generates the data structures for the phosphor-fan-monitor application. A future improvement is to get the fan inventory names from a separate file, so just that file could be generated from the MRW. """ tmpl = """\ <%! def indent(str, depth): return ''.join(4*' '*depth+line for line in str.splitlines(True)) %>\ <%def name="getCondParams(cond)" buffered="True"> %if (cond['name'] == 'propertiesMatch'): std::vector{ %for i in cond['properties']: PropertyState{ { "${i['object']}", "${i['interface']}", "${i['property']['name']}" }, static_cast<${i['property']['type']}>(${str(i['property']['value']).lower()}) }, %endfor } %endif \ /* This is a generated file. */ #include "fan_defs.hpp" #include "types.hpp" #include "groups.hpp" #include "conditions.hpp" using namespace phosphor::fan::monitor; using namespace phosphor::fan::trust; const std::vector fanDefinitions { %for fan_data in data.get('fans', {}): FanDefinition{"${fan_data['inventory']}", ${fan_data.get('method', {})}, ${fan_data.get('functional_delay', 0)}, ${fan_data.get('allowed_out_of_range_time', {})}, ${fan_data['deviation']}, ${fan_data.get('upper_deviation', fan_data['deviation'])}, ${fan_data['num_sensors_nonfunc_for_fan_nonfunc']}, 0, // Monitor start delay - not used in YAML configs 0, // Count interval - not used in YAML configs std::nullopt, // nonfuncRotorErrorDelay - also not used here std::nullopt, // fanMissingErrorDelay - also not used here std::vector{ %for sensor in fan_data['sensors']: <% #has_target is a bool, and we need a true instead of True has_target = str(sensor['has_target']).lower() target_interface = sensor.get( 'target_interface', 'xyz.openbmc_project.Control.FanSpeed') target_path = sensor.get( 'target_path', '') factor = sensor.get('factor', 1) offset = sensor.get('offset', 0) threshold = sensor.get('threshold', 1) ignore_above_max = str(sensor.get( 'ignore_above_max', False)).lower() %> \ SensorDefinition{"${sensor['name']}", ${has_target}, "${target_interface}", "${target_path}", ${factor}, ${offset}, ${threshold}, ${ignore_above_max}}, %endfor }, %if ('condition' in fan_data) and \ (fan_data['condition'] is not None): make_condition(condition::${fan_data['condition']['name']}(\ ${indent(getCondParams(cond=fan_data['condition']), 5)}\ )), %else: {}, %endif false // set_func_on_present. Hardcoded to false. }, %endfor }; ##Function to generate the group creation lambda. ##If a group were to ever need a different constructor, ##it could be handled here. <%def name="get_lambda_contents(group)"> std::vector group{ %for member in group['group']: <% in_trust = str(member.get('in_trust', "true")).lower() %> GroupDefinition{"${member['name']}", ${in_trust}}, %endfor }; return std::make_unique<${group['class']}>(group); const std::vector trustGroups { %for group in data.get('sensor_trust_groups', {}): { []() {\ ${get_lambda_contents(group)}\ } }, %endfor }; """ if __name__ == "__main__": parser = ArgumentParser( description="Phosphor fan monitor definition parser" ) parser.add_argument( "-m", "--monitor_yaml", dest="monitor_yaml", default="example/monitor.yaml", help="fan monitor definitional yaml", ) parser.add_argument( "-o", "--output_dir", dest="output_dir", default=".", help="output directory", ) args = parser.parse_args() if not args.monitor_yaml: parser.print_usage() sys.exit(1) with open(args.monitor_yaml, "r") as monitor_input: monitor_data = yaml.safe_load(monitor_input) or {} # Do some minor input validation for fan in monitor_data.get("fans", {}): if (fan["deviation"] < 0) or (fan["deviation"] > 100): sys.exit("Invalid deviation value " + str(fan["deviation"])) output_file = os.path.join(args.output_dir, "fan_monitor_defs.cpp") with open(output_file, "w") as output: output.write(Template(tmpl).render(data=monitor_data))