blob: d8264e168cbe9983a8c8fb1d99499020d45d5811 [file] [log] [blame]
#!/usr/bin/env python3
'''Generates 2 reports about OpenBMC error logs:
1) Dumps every error defined in the errors.yaml files passed in
into a single JSON file that looks like:
{
"desc":"Callout IIC device",
"error":"xyz.openbmc_project.Common.Callout.Error.IIC",
"file":"xyz/openbmc_project/Common/Callout.errors.yaml",
"metadata":[
"CALLOUT_IIC_BUS",
"CALLOUT_IIC_ADDR",
"Inherits xyz.openbmc_project.Common.Callout.Error.Device"
]
}
2) Crosschecks this generated JSON with the IBM error policy table,
showing if any errors are in one file but not the other.
'''
import argparse
import os
import json
import yaml
def get_errors(yaml_dirs):
'''Finds all of the errors in all of the error YAML files in
the directories passed in.'''
all_errors = []
for yaml_dir in yaml_dirs:
error_data = []
yaml_files = get_yaml(yaml_dir)
for yaml_file in yaml_files:
all_errors += read_error_yaml(yaml_dir, yaml_file)
return all_errors
def read_error_yaml(yaml_dir, yaml_file):
'''Returns a list of dictionary objects reflecting the error YAML.'''
all_errors = []
#xyz/openbmc_project/x.errors.yaml -> xyz.openbmc_project.x.Error
error_base = yaml_file.replace(os.sep, '.')
error_base = error_base.replace('.errors.yaml', '')
error_base += '.Error.'
#Also needs to look up the metadata from the .metadata.yaml files
metadata_file = yaml_file.replace('errors.yaml', 'metadata.yaml')
metadata = []
if os.path.exists(os.path.join(yaml_dir, metadata_file)):
with open(os.path.join(yaml_dir, metadata_file)) as mfd:
metadata = yaml.safe_load(mfd.read())
with open(os.path.join(yaml_dir, yaml_file)) as fd:
data = yaml.safe_load(fd.read())
for e in data:
error = {}
error['error'] = error_base + e['name']
error['desc'] = e['description']
error['metadata'] = get_metadata(e['name'], metadata)
error['file'] = yaml_file
all_errors.append(error)
return all_errors
def add_error(val):
'''Adds the '.Error' before the last segment of an error string.'''
dot = val.rfind('.')
return val[:dot] + '.Error' + val[dot:]
def get_metadata(name, metadata):
'''Finds metadata entries for the error in the metadata
dictionary parsed out of the *.metadata.yaml files.
The metadata YAML looks something like:
- name: SlaveDetectionFailure
meta:
- str: "ERRNO=%d"
type: int32
inherits:
- xyz.openbmc_project.Callout
'''
data = []
for m in metadata:
if m['name'] == name:
if 'meta' in m:
for entry in m['meta']:
#Get the name from name=value
n = entry['str'].split('=')[0]
data.append(n)
#inherits is a list, return it comma separated
if 'inherits' in m:
vals = list(map(add_error, m['inherits']))
i = ','.join(vals)
data.append("Inherits %s" % i)
return data
def get_yaml(yaml_dir):
'''Finds all of the *.errors.yaml files in the directory passed in.
Returns a list of entries like xyz/openbmc_project/Common.Errors.yaml.
'''
err_files = []
metadata_files = []
if os.path.exists(yaml_dir):
for directory, _, files in os.walk(yaml_dir):
if not files:
continue
err_files += [os.path.relpath(
os.path.join(directory, f), yaml_dir)
for f in [f for f in files if f.endswith('.errors.yaml')]]
return err_files
def crosscheck(errors, policy, outfile):
'''Crosschecks that the errors found in the YAML are in the
policy file, and vice versa.
'''
policy_errors = [x['err'] for x in policy]
yaml_errors = [x['error'] for x in errors]
out = open(outfile, 'w')
out.write("YAML errors not in policy table:\n\n")
for e in yaml_errors:
if e not in policy_errors:
out.write(" %s\n" % e)
out.write("\n%d total errors in the YAML\n\n" % len(yaml_errors))
out.write("Policy errors not in YAML:\n\n")
for e in policy_errors:
if e not in yaml_errors:
out.write(" %s\n" % e)
num_details = 0
for e in policy:
for d in e['dtls']:
num_details += 1
out.write("\n%d total errors (with %d total details blocks) in the "
"policy table\n\n" % (len(policy_errors), num_details))
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Error log policy reports')
parser.add_argument('-y', '--yaml_dirs',
dest='yaml_dirs',
default='.',
help='Comma separated list of error YAML dirs')
parser.add_argument('-e', '--error_file',
dest='error_file',
default='obmc-errors.json',
help='Output Error report file')
parser.add_argument('-p', '--policy',
dest='policy_file',
default='condensed.json',
help='Condensed policy in JSON')
parser.add_argument('-x', '--crosscheck',
dest='crosscheck_file',
help='YAML vs policy table crosscheck output file')
args = parser.parse_args()
dirs = args.yaml_dirs.split(',')
errors = get_errors(dirs)
with open(args.error_file, 'w') as outfile:
json.dump(errors, outfile, sort_keys=True,
indent=2, separators=(',', ':'))
if args.crosscheck_file:
with open(args.policy_file) as pf:
policy = yaml.safe_load(pf.read())
crosscheck(errors, policy, args.crosscheck_file)