blob: 8471827693afe1096b977817ff4be2cca50c7bff [file] [log] [blame]
Matt Spinler3048ecc2018-03-28 10:15:12 -05001#!/usr/bin/env python
2
3'''Generates 2 reports about OpenBMC error logs:
4
5 1) Dumps every error defined in the errors.yaml files passed in
6 into a single JSON file that looks like:
7
8 {
9 "desc":"Callout IIC device",
10 "error":"xyz.openbmc_project.Common.Callout.Error.IIC",
11 "file":"xyz/openbmc_project/Common/Callout.errors.yaml",
12 "metadata":[
13 "CALLOUT_IIC_BUS",
14 "CALLOUT_IIC_ADDR",
15 "Inherits xyz.openbmc_project.Common.Callout.Error.Device"
16 ]
17 }
18
19 2) Crosschecks this generated JSON with the IBM error policy table,
20 showing if any errors are in one file but not the other.
21
22'''
23
24import argparse
25import os
26import json
27import yaml
28
29
30def get_errors(yaml_dirs):
31 '''Finds all of the errors in all of the error YAML files in
32 the directories passed in.'''
33
34 all_errors = []
35 for yaml_dir in yaml_dirs:
36 error_data = []
37 yaml_files = get_yaml(yaml_dir)
38
39 for yaml_file in yaml_files:
40 all_errors += read_error_yaml(yaml_dir, yaml_file)
41
42 return all_errors
43
44
45def read_error_yaml(yaml_dir, yaml_file):
46 '''Returns a list of dictionary objects reflecting the error YAML.'''
47
48 all_errors = []
49
50 #xyz/openbmc_project/x.errors.yaml -> xyz.openbmc_project.x.Error
51 error_base = yaml_file.replace(os.sep, '.')
52 error_base = error_base.replace('.errors.yaml', '')
53 error_base += '.Error.'
54
55 #Also needs to look up the metadata from the .metadata.yaml files
56 metadata_file = yaml_file.replace('errors.yaml', 'metadata.yaml')
57 metadata = []
58
59 if os.path.exists(os.path.join(yaml_dir, metadata_file)):
60 with open(os.path.join(yaml_dir, metadata_file)) as mfd:
61 metadata = yaml.safe_load(mfd.read())
62
63 with open(os.path.join(yaml_dir, yaml_file)) as fd:
64 data = yaml.safe_load(fd.read())
65
66 for e in data:
67 error = {}
68 error['error'] = error_base + e['name']
69 error['desc'] = e['description']
70 error['metadata'] = get_metadata(e['name'], metadata)
71 error['file'] = yaml_file
72 all_errors.append(error)
73
74 return all_errors
75
76
Matt Spinlera5ae51c2018-03-28 10:22:18 -050077def add_error(val):
78 '''Adds the '.Error' before the last segment of an error string.'''
79 dot = val.rfind('.')
80 return val[:dot] + '.Error' + val[dot:]
81
82
Matt Spinler3048ecc2018-03-28 10:15:12 -050083def get_metadata(name, metadata):
Matt Spinlera5ae51c2018-03-28 10:22:18 -050084 '''Finds metadata entries for the error in the metadata
85 dictionary parsed out of the *.metadata.yaml files.
86
87 The metadata YAML looks something like:
88 - name: SlaveDetectionFailure
89 meta:
90 - str: "ERRNO=%d"
91 type: int32
92 inherits:
93 - xyz.openbmc_project.Callout
94 '''
95
Matt Spinler3048ecc2018-03-28 10:15:12 -050096 data = []
Matt Spinlera5ae51c2018-03-28 10:22:18 -050097 for m in metadata:
98 if m['name'] == name:
99 if 'meta' in m:
100 for entry in m['meta']:
101 #Get the name from name=value
102 n = entry['str'].split('=')[0]
103 data.append(n)
104
105 #inherits is a list, return it comma separated
106 if 'inherits' in m:
107 vals = list(map(add_error, m['inherits']))
108 i = ','.join(vals)
109 data.append("Inherits %s" % i)
110
Matt Spinler3048ecc2018-03-28 10:15:12 -0500111 return data
112
113
114def get_yaml(yaml_dir):
115 '''Finds all of the *.errors.yaml files in the directory passed in.
116 Returns a list of entries like xyz/openbmc_project/Common.Errors.yaml.
117 '''
118
119 err_files = []
120 metadata_files = []
121 if os.path.exists(yaml_dir):
122 for directory, _, files in os.walk(yaml_dir):
123 if not files:
124 continue
125
126 err_files += map(
127 lambda f: os.path.relpath(
128 os.path.join(directory, f),
129 yaml_dir),
130 filter(lambda f: f.endswith('.errors.yaml'), files))
131
132 return err_files
133
134
Matt Spinler3911b052018-03-28 10:23:58 -0500135def crosscheck(errors, policy, outfile):
136 '''Crosschecks that the errors found in the YAML are in the
137 policy file, and vice versa.
138 '''
139
140 policy_errors = [x['err'] for x in policy]
141 yaml_errors = [x['error'] for x in errors]
142
143 out = open(outfile, 'w')
144 out.write("YAML errors not in policy table:\n\n")
145
146 for e in yaml_errors:
147 if e not in policy_errors:
148 out.write(" %s\n" % e)
149
150 out.write("\n%d total errors in the YAML\n\n" % len(yaml_errors))
151 out.write("Policy errors not in YAML:\n\n")
152
153 for e in policy_errors:
154 if e not in yaml_errors:
155 out.write(" %s\n" % e)
156
157 num_details = 0
158 for e in policy:
159 for d in e['dtls']:
160 num_details += 1
161
162 out.write("\n%d total errors (with %d total details blocks) in the "
163 "policy table\n\n" % (len(policy_errors), num_details))
164
Matt Spinler3048ecc2018-03-28 10:15:12 -0500165if __name__ == '__main__':
166
167 parser = argparse.ArgumentParser(description='Error log policy reports')
168
169 parser.add_argument('-y', '--yaml_dirs',
170 dest='yaml_dirs',
171 default='.',
172 help='Comma separated list of error YAML dirs')
173 parser.add_argument('-e', '--error_file',
174 dest='error_file',
175 default='obmc-errors.json',
176 help='Output Error report file')
177 parser.add_argument('-p', '--policy',
178 dest='policy_file',
179 default='condensed.json',
180 help='Condensed policy in JSON')
Matt Spinler3911b052018-03-28 10:23:58 -0500181 parser.add_argument('-x', '--crosscheck',
182 dest='crosscheck_file',
183 help='YAML vs policy table crosscheck output file')
Matt Spinler3048ecc2018-03-28 10:15:12 -0500184
185 args = parser.parse_args()
186
187 dirs = args.yaml_dirs.split(',')
188 errors = get_errors(dirs)
189
190 with open(args.error_file, 'w') as outfile:
191 json.dump(errors, outfile, sort_keys=True,
192 indent=2, separators=(',', ':'))
193
Matt Spinler3911b052018-03-28 10:23:58 -0500194 if args.crosscheck_file:
195 with open(args.policy_file) as pf:
196 policy = yaml.safe_load(pf.read())
197
198 crosscheck(errors, policy, args.crosscheck_file)