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 f.close() 156 157 158def get_elog_data(i_elog_yaml, 159 i_elog_meta_yaml, 160 o_elog_data): 161 r""" 162 Parse the error and metadata yaml files in order to pull out 163 error metadata. 164 165 Description of arguments: 166 i_elog_yaml error yaml file 167 i_elog_meta_yaml metadata yaml file 168 o_elog_data error metadata 169 """ 170 errors, error_msg, error_lvl, meta, meta_data, parents = o_elog_data 171 ifile = yaml.safe_load(open(i_elog_yaml)) 172 mfile = yaml.safe_load(open(i_elog_meta_yaml)) 173 for i in ifile: 174 match = None 175 # Find the corresponding meta data for this entry 176 for m in mfile: 177 if m['name'] == i['name']: 178 match = m 179 break 180 if (match is None): 181 print "Error - Did not find meta data for " + i['name'] 182 exit(1) 183 # Grab the main error and it's info 184 errors.append(i['name']) 185 parent = None 186 if('inherits' in i): 187 # xyz.openbmc.Foo, we need Foo 188 # Get 0th inherited error (current support - single inheritance) 189 parent = i['inherits'][0].split(".").pop() 190 parents[i['name']] = parent 191 error_msg[i['name']] = i['description'] 192 error_lvl[i['name']] = match['level'] 193 tmp_meta = [] 194 # grab all the meta data fields and info 195 for j in match['meta']: 196 str_short = j['str'].split('=')[0] 197 tmp_meta.append(str_short) 198 meta_data[str_short] = {} 199 meta_data[str_short]['str'] = j['str'] 200 meta_data[str_short]['str_short'] = str_short 201 meta_data[str_short]['type'] = get_cpp_type(j['type']) 202 meta[i['name']] = tmp_meta 203 204 # Debug 205 # for i in errors: 206 # print "ERROR: " + errors[i] 207 # print " MSG: " + error_msg[errors[i]] 208 # print " LVL: " + error_lvl[errors[i]] 209 # print " META: " 210 # print meta[i] 211 212 213def main(i_args): 214 parser = OptionParser() 215 216 parser.add_option("-m", "--mako", dest="elog_mako", 217 default="elog-gen-template.mako.hpp", 218 help="input mako template file to use") 219 220 parser.add_option("-o", "--output", dest="output_hpp", 221 default="elog-gen.hpp", 222 help="output hpp to generate, elog-gen.hpp is default") 223 224 parser.add_option("-y", "--yamldir", dest="yamldir", 225 default="./example/xyz/openbmc_project/Example", 226 help="Base directory of yaml files to process") 227 228 parser.add_option("-t", "--templatedir", dest="templatedir", 229 default="phosphor-logging/templates/", 230 help="Base directory of files to process") 231 232 parser.add_option("-n", "--namespace", dest="error_namespace", 233 default="example/xyz/openbmc_project/Example", 234 help="Error d-bus namespace") 235 236 (options, args) = parser.parse_args(i_args) 237 238 gen_elog_hpp(options.yamldir, 239 options.output_hpp, 240 options.templatedir, 241 options.elog_mako, 242 options.error_namespace) 243 244# Only run if it's a script 245if __name__ == '__main__': 246 main(sys.argv[1:]) 247