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 59# Return the yaml files with their directory structure plus the file name 60# without the yaml extension, which will be used to set the namespaces. 61# Ex: file xyz/openbmc_project/Error/Callout/Device.errors.yaml 62# will have namespce xyz/openbmc_project/Error/Callout/Device 63def get_error_yaml_files(i_yaml_dir, i_test_dir): 64 yaml_files = dict() 65 if i_yaml_dir != "None": 66 for root, dirs, files in os.walk(i_yaml_dir): 67 for files in filter(lambda file: 68 file.endswith('.errors.yaml'), files): 69 splitdir = root.split(i_yaml_dir)[1] + "/" + files[:-12] 70 if splitdir.startswith("/"): 71 splitdir = splitdir[1:] 72 yaml_files[(os.path.join(root, files))] = splitdir 73 for root, dirs, files in os.walk(i_test_dir): 74 for files in filter(lambda file: file.endswith('.errors.yaml'), files): 75 splitdir = root.split(i_test_dir)[1] + "/" + files[:-12] 76 yaml_files[(os.path.join(root, files))] = splitdir 77 return yaml_files 78 79 80def get_meta_yaml_file(i_error_yaml_file): 81 # the meta data will be defined in file name where we replace 82 # <Interface>.errors.yaml with <Interface>.metadata.yaml 83 meta_yaml = i_error_yaml_file.replace("errors", "metadata") 84 return meta_yaml 85 86 87def get_cpp_type(i_type): 88 typeMap = { 89 'int16': 'int16_t', 90 'int32': 'int32_t', 91 'int64': 'int64_t', 92 'uint16': 'uint16_t', 93 'uint32': 'uint32_t', 94 'uint64': 'uint64_t', 95 'double': 'double', 96 # const char* aids usage of constexpr 97 'string': 'const char*'} 98 99 return typeMap[i_type] 100 101 102def gen_elog_hpp(i_yaml_dir, i_test_dir, i_output_hpp, 103 i_template_dir, i_elog_mako): 104 r""" 105 Read yaml file(s) under input yaml dir, grab the relevant data and call 106 the mako template to generate the output header file. 107 108 Description of arguments: 109 i_yaml_dir directory containing base error yaml files 110 i_test_dir directory containing test error yaml files 111 i_output_hpp name of the to be generated output hpp 112 i_template_dir directory containing error mako templates 113 i_elog_mako error mako template to render 114 """ 115 116 # Input parameters to mako template 117 errors = list() # Main error codes 118 error_msg = dict() # Error msg that corresponds to error code 119 error_lvl = dict() # Error code log level (debug, info, error, ...) 120 meta = dict() # The meta data names associated (ERRNO, FILE_NAME, ...) 121 meta_data = dict() # The meta data info (type, format) 122 parents = dict() 123 124 error_yamls = get_error_yaml_files(i_yaml_dir, i_test_dir) 125 126 for error_yaml in error_yamls: 127 # Verify the error yaml file 128 if (not (os.path.isfile(error_yaml))): 129 print("Can not find input yaml file " + error_yaml) 130 exit(1) 131 132 # Verify the metadata yaml file 133 meta_yaml = get_meta_yaml_file(error_yaml) 134 if (not (os.path.isfile(meta_yaml))): 135 print("Can not find meta yaml file " + meta_yaml) 136 exit(1) 137 138 # Verify the input mako file 139 template_path = "/".join((i_template_dir, i_elog_mako)) 140 if (not (os.path.isfile(template_path))): 141 print("Can not find input template file " + template_path) 142 exit(1) 143 144 get_elog_data(error_yaml, 145 meta_yaml, 146 error_yamls[error_yaml], 147 # Last arg is a tuple 148 (errors, 149 error_msg, 150 error_lvl, 151 meta, 152 meta_data, 153 parents)) 154 155 if(not check_error_inheritance(errors, parents)): 156 print("Error - failed to validate error inheritance") 157 exit(1) 158 159 errors = order_inherited_errors(errors, parents) 160 161 # Load the mako template and call it with the required data 162 yaml_dir = i_yaml_dir.strip("./") 163 yaml_dir = yaml_dir.strip("../") 164 template = Template(filename=template_path) 165 f = open(i_output_hpp, 'w') 166 f.write(template.render( 167 errors=errors, error_msg=error_msg, 168 error_lvl=error_lvl, meta=meta, 169 meta_data=meta_data, 170 parents=parents)) 171 f.close() 172 173 174def get_elog_data(i_elog_yaml, 175 i_elog_meta_yaml, 176 i_namespace, 177 o_elog_data): 178 r""" 179 Parse the error and metadata yaml files in order to pull out 180 error metadata. 181 182 Description of arguments: 183 i_elog_yaml error yaml file 184 i_elog_meta_yaml metadata yaml file 185 i_namespace namespace data 186 o_elog_data error metadata 187 """ 188 errors, error_msg, error_lvl, meta, meta_data, parents = o_elog_data 189 ifile = yaml.safe_load(open(i_elog_yaml)) 190 mfile = yaml.safe_load(open(i_elog_meta_yaml)) 191 for i in mfile: 192 match = None 193 # Find the corresponding meta data for this entry 194 for j in ifile: 195 if j['name'] == i['name']: 196 match = j 197 break 198 if (match is None): 199 print("Error - Did not find meta data for " + i['name']) 200 exit(1) 201 # Grab the main error and it's info 202 fullname = i_namespace.replace('/', '.') + ('.') + i['name'] 203 errors.append(fullname) 204 parent = None 205 if('inherits' in i): 206 # Get 0th inherited error (current support - single inheritance) 207 parent = i['inherits'][0] 208 parents[fullname] = parent 209 error_msg[fullname] = match['description'] 210 try: 211 error_lvl[fullname] = i['level'] 212 except: 213 print ("No level found for: " + i['name'] + ", using INFO") 214 error_lvl[fullname] = "INFO" 215 tmp_meta = [] 216 # grab all the meta data fields and info 217 for j in i['meta']: 218 str_short = j['str'].split('=')[0] 219 tmp_meta.append(str_short) 220 meta_data[str_short] = {} 221 meta_data[str_short]['str'] = j['str'] 222 meta_data[str_short]['str_short'] = str_short 223 meta_data[str_short]['type'] = get_cpp_type(j['type']) 224 meta[fullname] = tmp_meta 225 226 # Debug 227 # for i in errors: 228 # print "ERROR: " + errors[i] 229 # print " MSG: " + error_msg[errors[i]] 230 # print " LVL: " + error_lvl[errors[i]] 231 # print " META: " 232 # print meta[i] 233 234 235def main(i_args): 236 parser = OptionParser() 237 238 parser.add_option("-m", "--mako", dest="elog_mako", 239 default="elog-gen-template.mako.hpp", 240 help="input mako template file to use") 241 242 parser.add_option("-o", "--output", dest="output_hpp", 243 default="elog-errors.hpp", 244 help="output hpp to generate, elog-errors.hpp default") 245 246 parser.add_option("-y", "--yamldir", dest="yamldir", 247 default="None", 248 help="Base directory of yaml files to process") 249 250 parser.add_option("-u", "--testdir", dest="testdir", 251 default="./tools/example/", 252 help="Unit test directory of yaml files to process") 253 254 parser.add_option("-t", "--templatedir", dest="templatedir", 255 default="phosphor-logging/templates/", 256 help="Base directory of files to process") 257 258 (options, args) = parser.parse_args(i_args) 259 260 gen_elog_hpp(options.yamldir, 261 options.testdir, 262 options.output_hpp, 263 options.templatedir, 264 options.elog_mako) 265 266# Only run if it's a script 267if __name__ == '__main__': 268 main(sys.argv[1:]) 269