blob: c3e10daddb3be5de05c0d4aef5151924689ea1ff [file] [log] [blame]
Matt Spinlerc4cd4dc2020-02-20 13:02:34 -06001#!/usr/bin/env python3
Matt Spinler3048ecc2018-03-28 10:15:12 -05002
Patrick Williams7f2b6ae2022-12-05 09:55:35 -06003"""Generates 2 reports about OpenBMC error logs:
Matt Spinler3048ecc2018-03-28 10:15:12 -05004
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
Patrick Williams7f2b6ae2022-12-05 09:55:35 -060022"""
Matt Spinler3048ecc2018-03-28 10:15:12 -050023
24import argparse
Matt Spinler3048ecc2018-03-28 10:15:12 -050025import json
Patrick Williams7f2b6ae2022-12-05 09:55:35 -060026import os
27
Matt Spinler3048ecc2018-03-28 10:15:12 -050028import yaml
29
30
31def get_errors(yaml_dirs):
Patrick Williams7f2b6ae2022-12-05 09:55:35 -060032 """Finds all of the errors in all of the error YAML files in
33 the directories passed in."""
Matt Spinler3048ecc2018-03-28 10:15:12 -050034
35 all_errors = []
36 for yaml_dir in yaml_dirs:
Matt Spinler3048ecc2018-03-28 10:15:12 -050037 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):
Patrick Williams7f2b6ae2022-12-05 09:55:35 -060046 """Returns a list of dictionary objects reflecting the error YAML."""
Matt Spinler3048ecc2018-03-28 10:15:12 -050047
48 all_errors = []
49
Patrick Williams7f2b6ae2022-12-05 09:55:35 -060050 # 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."
Matt Spinler3048ecc2018-03-28 10:15:12 -050054
Patrick Williams7f2b6ae2022-12-05 09:55:35 -060055 # Also needs to look up the metadata from the .metadata.yaml files
56 metadata_file = yaml_file.replace("errors.yaml", "metadata.yaml")
Matt Spinler3048ecc2018-03-28 10:15:12 -050057 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 = {}
Patrick Williams7f2b6ae2022-12-05 09:55:35 -060068 error["error"] = error_base + e["name"]
69 error["desc"] = e["description"]
70 error["metadata"] = get_metadata(e["name"], metadata)
71 error["file"] = yaml_file
Matt Spinler3048ecc2018-03-28 10:15:12 -050072 all_errors.append(error)
73
74 return all_errors
75
76
Matt Spinlera5ae51c2018-03-28 10:22:18 -050077def add_error(val):
Patrick Williams7f2b6ae2022-12-05 09:55:35 -060078 """Adds the '.Error' before the last segment of an error string."""
79 dot = val.rfind(".")
80 return val[:dot] + ".Error" + val[dot:]
Matt Spinlera5ae51c2018-03-28 10:22:18 -050081
82
Matt Spinler3048ecc2018-03-28 10:15:12 -050083def get_metadata(name, metadata):
Patrick Williams7f2b6ae2022-12-05 09:55:35 -060084 """Finds metadata entries for the error in the metadata
85 dictionary parsed out of the *.metadata.yaml files.
Matt Spinlera5ae51c2018-03-28 10:22:18 -050086
Patrick Williams7f2b6ae2022-12-05 09:55:35 -060087 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 """
Matt Spinlera5ae51c2018-03-28 10:22:18 -050095
Matt Spinler3048ecc2018-03-28 10:15:12 -050096 data = []
Matt Spinlera5ae51c2018-03-28 10:22:18 -050097 for m in metadata:
Patrick Williams7f2b6ae2022-12-05 09:55:35 -060098 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]
Matt Spinlera5ae51c2018-03-28 10:22:18 -0500103 data.append(n)
104
Patrick Williams7f2b6ae2022-12-05 09:55:35 -0600105 # 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)
Matt Spinlera5ae51c2018-03-28 10:22:18 -0500109 data.append("Inherits %s" % i)
110
Matt Spinler3048ecc2018-03-28 10:15:12 -0500111 return data
112
113
114def get_yaml(yaml_dir):
Patrick Williams7f2b6ae2022-12-05 09:55:35 -0600115 """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 """
Matt Spinler3048ecc2018-03-28 10:15:12 -0500118
119 err_files = []
Matt Spinler3048ecc2018-03-28 10:15:12 -0500120 if os.path.exists(yaml_dir):
121 for directory, _, files in os.walk(yaml_dir):
122 if not files:
123 continue
124
Patrick Williams7f2b6ae2022-12-05 09:55:35 -0600125 err_files += [
126 os.path.relpath(os.path.join(directory, f), yaml_dir)
127 for f in [f for f in files if f.endswith(".errors.yaml")]
128 ]
Matt Spinler3048ecc2018-03-28 10:15:12 -0500129
130 return err_files
131
132
Matt Spinler3911b052018-03-28 10:23:58 -0500133def crosscheck(errors, policy, outfile):
Patrick Williams7f2b6ae2022-12-05 09:55:35 -0600134 """Crosschecks that the errors found in the YAML are in the
135 policy file, and vice versa.
136 """
Matt Spinler3911b052018-03-28 10:23:58 -0500137
Patrick Williams7f2b6ae2022-12-05 09:55:35 -0600138 policy_errors = [x["err"] for x in policy]
139 yaml_errors = [x["error"] for x in errors]
Matt Spinler3911b052018-03-28 10:23:58 -0500140
Patrick Williams7f2b6ae2022-12-05 09:55:35 -0600141 out = open(outfile, "w")
Matt Spinler3911b052018-03-28 10:23:58 -0500142 out.write("YAML errors not in policy table:\n\n")
143
144 for e in yaml_errors:
145 if e not in policy_errors:
146 out.write(" %s\n" % e)
147
148 out.write("\n%d total errors in the YAML\n\n" % len(yaml_errors))
149 out.write("Policy errors not in YAML:\n\n")
150
151 for e in policy_errors:
152 if e not in yaml_errors:
153 out.write(" %s\n" % e)
154
155 num_details = 0
156 for e in policy:
Patrick Williams7f2b6ae2022-12-05 09:55:35 -0600157 for d in e["dtls"]:
Matt Spinler3911b052018-03-28 10:23:58 -0500158 num_details += 1
159
Patrick Williams7f2b6ae2022-12-05 09:55:35 -0600160 out.write(
161 "\n%d total errors (with %d total details blocks) in the "
162 "policy table\n\n" % (len(policy_errors), num_details)
163 )
Matt Spinler3911b052018-03-28 10:23:58 -0500164
Matt Spinler3048ecc2018-03-28 10:15:12 -0500165
Patrick Williams7f2b6ae2022-12-05 09:55:35 -0600166if __name__ == "__main__":
167 parser = argparse.ArgumentParser(description="Error log policy reports")
Matt Spinler3048ecc2018-03-28 10:15:12 -0500168
Patrick Williams7f2b6ae2022-12-05 09:55:35 -0600169 parser.add_argument(
170 "-y",
171 "--yaml_dirs",
172 dest="yaml_dirs",
173 default=".",
174 help="Comma separated list of error YAML dirs",
175 )
176 parser.add_argument(
177 "-e",
178 "--error_file",
179 dest="error_file",
180 default="obmc-errors.json",
181 help="Output Error report file",
182 )
183 parser.add_argument(
184 "-p",
185 "--policy",
186 dest="policy_file",
187 default="condensed.json",
188 help="Condensed policy in JSON",
189 )
190 parser.add_argument(
191 "-x",
192 "--crosscheck",
193 dest="crosscheck_file",
194 help="YAML vs policy table crosscheck output file",
195 )
Matt Spinler3048ecc2018-03-28 10:15:12 -0500196
197 args = parser.parse_args()
198
Patrick Williams7f2b6ae2022-12-05 09:55:35 -0600199 dirs = args.yaml_dirs.split(",")
Matt Spinler3048ecc2018-03-28 10:15:12 -0500200 errors = get_errors(dirs)
201
Patrick Williams7f2b6ae2022-12-05 09:55:35 -0600202 with open(args.error_file, "w") as outfile:
203 json.dump(
204 errors, outfile, sort_keys=True, indent=2, separators=(",", ":")
205 )
Matt Spinler3048ecc2018-03-28 10:15:12 -0500206
Matt Spinler3911b052018-03-28 10:23:58 -0500207 if args.crosscheck_file:
208 with open(args.policy_file) as pf:
209 policy = yaml.safe_load(pf.read())
210
211 crosscheck(errors, policy, args.crosscheck_file)