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 metadata_process = dict() # metadata that have the 'process' keyword set 124 125 error_yamls = get_error_yaml_files(i_yaml_dir, i_test_dir) 126 127 for error_yaml in error_yamls: 128 # Verify the error yaml file 129 if (not (os.path.isfile(error_yaml))): 130 print("Can not find input yaml file " + error_yaml) 131 exit(1) 132 133 # Verify the metadata yaml file 134 meta_yaml = get_meta_yaml_file(error_yaml) 135 if (not (os.path.isfile(meta_yaml))): 136 print("Can not find meta yaml file " + meta_yaml) 137 exit(1) 138 139 # Verify the input mako file 140 template_path = "/".join((i_template_dir, i_elog_mako)) 141 if (not (os.path.isfile(template_path))): 142 print("Can not find input template file " + template_path) 143 exit(1) 144 145 get_elog_data(error_yaml, 146 meta_yaml, 147 error_yamls[error_yaml], 148 # Last arg is a tuple 149 (errors, 150 error_msg, 151 error_lvl, 152 meta, 153 meta_data, 154 parents, 155 metadata_process)) 156 157 if(not check_error_inheritance(errors, parents)): 158 print("Error - failed to validate error inheritance") 159 exit(1) 160 161 errors = order_inherited_errors(errors, parents) 162 163 # Load the mako template and call it with the required data 164 yaml_dir = i_yaml_dir.strip("./") 165 yaml_dir = yaml_dir.strip("../") 166 template = Template(filename=template_path) 167 f = open(i_output_hpp, 'w') 168 f.write(template.render( 169 errors=errors, 170 error_msg=error_msg, 171 error_lvl=error_lvl, 172 meta=meta, 173 meta_data=meta_data, 174 parents=parents, 175 metadata_process=metadata_process)) 176 f.close() 177 178 179def get_elog_data(i_elog_yaml, 180 i_elog_meta_yaml, 181 i_namespace, 182 o_elog_data): 183 r""" 184 Parse the error and metadata yaml files in order to pull out 185 error metadata. 186 187 Description of arguments: 188 i_elog_yaml error yaml file 189 i_elog_meta_yaml metadata yaml file 190 i_namespace namespace data 191 o_elog_data error metadata 192 """ 193 (errors, error_msg, error_lvl, meta, 194 meta_data, parents, metadata_process) = o_elog_data 195 ifile = yaml.safe_load(open(i_elog_yaml)) 196 mfile = yaml.safe_load(open(i_elog_meta_yaml)) 197 for i in mfile: 198 match = None 199 # Find the corresponding meta data for this entry 200 for j in ifile: 201 if j['name'] == i['name']: 202 match = j 203 break 204 if (match is None): 205 print("Error - Did not find meta data for " + i['name']) 206 exit(1) 207 # Grab the main error and it's info 208 fullname = i_namespace.replace('/', '.') + ('.') + i['name'] 209 errors.append(fullname) 210 parent = None 211 if('inherits' in i): 212 # Get 0th inherited error (current support - single inheritance) 213 parent = i['inherits'][0] 214 parents[fullname] = parent 215 error_msg[fullname] = match['description'].strip() 216 try: 217 error_lvl[fullname] = i['level'] 218 except: 219 print ("No level found for: " + i['name'] + ", using INFO") 220 error_lvl[fullname] = "INFO" 221 tmp_meta = [] 222 # grab all the meta data fields and info 223 if('meta' in i): 224 for j in i['meta']: 225 str_short = j['str'].split('=')[0] 226 tmp_meta.append(str_short) 227 meta_data[str_short] = {} 228 meta_data[str_short]['str'] = j['str'] 229 meta_data[str_short]['str_short'] = str_short 230 meta_data[str_short]['type'] = get_cpp_type(j['type']) 231 if(('process' in j) and (True == j['process'])): 232 metadata_process[str_short] = fullname + "." + str_short 233 meta[fullname] = tmp_meta 234 235 # Debug 236 # for i in errors: 237 # print "ERROR: " + errors[i] 238 # print " MSG: " + error_msg[errors[i]] 239 # print " LVL: " + error_lvl[errors[i]] 240 # print " META: " 241 # print meta[i] 242 243 244def main(i_args): 245 parser = OptionParser() 246 247 parser.add_option("-m", "--mako", dest="elog_mako", 248 default="elog-gen-template.mako.hpp", 249 help="input mako template file to use") 250 251 parser.add_option("-o", "--output", dest="output_hpp", 252 default="elog-errors.hpp", 253 help="output hpp to generate, elog-errors.hpp default") 254 255 parser.add_option("-y", "--yamldir", dest="yamldir", 256 default="None", 257 help="Base directory of yaml files to process") 258 259 parser.add_option("-u", "--testdir", dest="testdir", 260 default="./tools/example/", 261 help="Unit test directory of yaml files to process") 262 263 parser.add_option("-t", "--templatedir", dest="templatedir", 264 default="phosphor-logging/templates/", 265 help="Base directory of files to process") 266 267 (options, args) = parser.parse_args(i_args) 268 269 gen_elog_hpp(options.yamldir, 270 options.testdir, 271 options.output_hpp, 272 options.templatedir, 273 options.elog_mako) 274 275# Only run if it's a script 276if __name__ == '__main__': 277 main(sys.argv[1:]) 278