1#!/usr/bin/env python 2 3r""" 4This script will parse error log yaml file(s) and generate 5a header file which will then be used by the error logging client and 6server to collect and validate the error information generated by the 7openbmc software components. 8 9This code uses a mako template to provide the basic template of the header 10file we're going to generate. We then call it with information from the 11yaml to generate the header file. 12""" 13 14from mako.template import Template 15from optparse import OptionParser 16import yaml 17import sys 18import os 19 20 21def order_inherited_errors(i_errors, i_parents): 22 # the ordered list of errors 23 errors = list() 24 has_inheritance = False 25 for error in i_errors: 26 if(i_parents[error] is not None): 27 has_inheritance = True 28 break 29 30 if(has_inheritance): 31 # Order the error codes list such that an error is never placed 32 # before it's parent. This way generated code can ensure parent 33 # definitions preceed child error definitions. 34 while(len(errors) < len(i_errors)): 35 for error in i_errors: 36 if(error in errors): 37 # already ordererd 38 continue 39 if((not i_parents[error]) or (i_parents[error] in errors)): 40 # parent present, or has no parent, either way this error 41 # can be added 42 errors.append(error) 43 else: 44 # no inherited errors 45 errors = i_errors 46 47 return errors 48 49 50def check_error_inheritance(i_errors, i_parents): 51 for error in i_errors: 52 if(i_parents[error] and (i_parents[error] not in i_errors)): 53 print(error + " inherits " + i_parents[error] + 54 " but the latter is not defined") 55 return False 56 return True 57 58 59def get_error_yaml_files(i_yaml_dir): 60 yaml_files = filter( 61 lambda file: file.endswith('.errors.yaml'), 62 os.listdir(i_yaml_dir)) 63 return yaml_files 64 65 66def get_meta_yaml_file(i_error_yaml_file): 67 # the meta data will be defined in file name where we replace 68 # <Interface>.errors.yaml with <Interface>.metadata.yaml 69 meta_yaml = i_error_yaml_file.replace("errors", "metadata") 70 return meta_yaml 71 72 73def get_cpp_type(i_type): 74 typeMap = { 75 'int16': 'int16_t', 76 'int32': 'int32_t', 77 'int64': 'int64_t', 78 'uint16': 'uint16_t', 79 'uint32': 'uint32_t', 80 'uint64': 'uint64_t', 81 'double': 'double', 82 # const char* aids usage of constexpr 83 'string': 'const char*'} 84 85 return typeMap[i_type] 86 87 88def gen_elog_hpp(i_yaml_dir, i_output_hpp, 89 i_template_dir, i_elog_mako, i_error_namespace): 90 r""" 91 Read yaml file(s) under input yaml dir, grab the relevant data and call 92 the mako template to generate the output header file. 93 94 Description of arguments: 95 i_yaml_dir directory containing error yaml files 96 i_output_hpp name of the to be generated output hpp 97 i_template_dir directory containing error mako templates 98 i_elog_mako error mako template to render 99 """ 100 101 # Input parameters to mako template 102 errors = list() # Main error codes 103 error_msg = dict() # Error msg that corresponds to error code 104 error_lvl = dict() # Error code log level (debug, info, error, ...) 105 meta = dict() # The meta data names associated (ERRNO, FILE_NAME, ...) 106 meta_data = dict() # The meta data info (type, format) 107 parents = dict() 108 109 error_yamls = get_error_yaml_files(i_yaml_dir) 110 111 for error_yaml in error_yamls: 112 # Verify the error yaml file 113 error_yaml = "/".join((i_yaml_dir, error_yaml)) 114 if (not (os.path.isfile(error_yaml))): 115 print("Can not find input yaml file " + error_yaml) 116 exit(1) 117 118 # Verify the metadata yaml file 119 meta_yaml = get_meta_yaml_file(error_yaml) 120 if (not (os.path.isfile(meta_yaml))): 121 print("Can not find meta yaml file " + meta_yaml) 122 exit(1) 123 124 # Verify the input mako file 125 template_path = "/".join((i_template_dir, i_elog_mako)) 126 if (not (os.path.isfile(template_path))): 127 print("Can not find input template file " + template_path) 128 exit(1) 129 130 get_elog_data(error_yaml, 131 meta_yaml, 132 # 3rd arg is a tuple 133 (errors, 134 error_msg, 135 error_lvl, 136 meta, 137 meta_data, 138 parents)) 139 140 if(not check_error_inheritance(errors, parents)): 141 print("Error - failed to validate error inheritance") 142 exit(1) 143 144 errors = order_inherited_errors(errors, parents) 145 146 # Load the mako template and call it with the required data 147 yaml_dir = i_yaml_dir.strip("./") 148 yaml_dir = yaml_dir.strip("../") 149 template = Template(filename=template_path) 150 f = open(i_output_hpp, 'w') 151 f.write(template.render( 152 errors=errors, error_msg=error_msg, 153 error_lvl=error_lvl, meta=meta, 154 meta_data=meta_data, error_namespace=i_error_namespace, 155 parents=parents)) 156 f.close() 157 158 159def get_elog_data(i_elog_yaml, 160 i_elog_meta_yaml, 161 o_elog_data): 162 r""" 163 Parse the error and metadata yaml files in order to pull out 164 error metadata. 165 166 Description of arguments: 167 i_elog_yaml error yaml file 168 i_elog_meta_yaml metadata yaml file 169 o_elog_data error metadata 170 """ 171 errors, error_msg, error_lvl, meta, meta_data, parents = o_elog_data 172 ifile = yaml.safe_load(open(i_elog_yaml)) 173 mfile = yaml.safe_load(open(i_elog_meta_yaml)) 174 for i in mfile: 175 match = None 176 # Find the corresponding meta data for this entry 177 for j in ifile: 178 if j['name'] == i['name']: 179 match = j 180 break 181 if (match is None): 182 print("Error - Did not find meta data for " + i['name']) 183 exit(1) 184 # Grab the main error and it's info 185 errors.append(i['name']) 186 parent = None 187 if('inherits' in i): 188 # xyz.openbmc.Foo, we need Foo 189 # Get 0th inherited error (current support - single inheritance) 190 parent = i['inherits'][0].split(".").pop() 191 parents[i['name']] = parent 192 error_msg[i['name']] = match['description'] 193 error_lvl[i['name']] = i['level'] 194 tmp_meta = [] 195 # grab all the meta data fields and info 196 for j in i['meta']: 197 str_short = j['str'].split('=')[0] 198 tmp_meta.append(str_short) 199 meta_data[str_short] = {} 200 meta_data[str_short]['str'] = j['str'] 201 meta_data[str_short]['str_short'] = str_short 202 meta_data[str_short]['type'] = get_cpp_type(j['type']) 203 meta[i['name']] = tmp_meta 204 205 # Debug 206 # for i in errors: 207 # print "ERROR: " + errors[i] 208 # print " MSG: " + error_msg[errors[i]] 209 # print " LVL: " + error_lvl[errors[i]] 210 # print " META: " 211 # print meta[i] 212 213 214def main(i_args): 215 parser = OptionParser() 216 217 parser.add_option("-m", "--mako", dest="elog_mako", 218 default="elog-gen-template.mako.hpp", 219 help="input mako template file to use") 220 221 parser.add_option("-o", "--output", dest="output_hpp", 222 default="elog-gen.hpp", 223 help="output hpp to generate, elog-gen.hpp is default") 224 225 parser.add_option("-y", "--yamldir", dest="yamldir", 226 default="./example/xyz/openbmc_project/Example", 227 help="Base directory of yaml files to process") 228 229 parser.add_option("-t", "--templatedir", dest="templatedir", 230 default="phosphor-logging/templates/", 231 help="Base directory of files to process") 232 233 parser.add_option("-n", "--namespace", dest="error_namespace", 234 default="example/xyz/openbmc_project/Example", 235 help="Error d-bus namespace") 236 237 (options, args) = parser.parse_args(i_args) 238 239 gen_elog_hpp(options.yamldir, 240 options.output_hpp, 241 options.templatedir, 242 options.elog_mako, 243 options.error_namespace) 244 245# Only run if it's a script 246if __name__ == '__main__': 247 main(sys.argv[1:]) 248