blob: 229f5515ee3ddf24959275388740d6a792afd3ba [file] [log] [blame]
Matt Spinler446d2b92020-03-05 16:49:14 -06001#!/usr/bin/env python3
Matt Spinler5cb5deb2019-09-27 13:51:36 -05002
3import argparse
4import json
5import sys
6
7r"""
8Validates the PEL message registry JSON, which includes checking it against
9a JSON schema using the jsonschema module as well as doing some extra checks
10that can't be encoded in the schema.
11"""
12
13
14def check_duplicate_names(registry_json):
15 r"""
16 Check that there aren't any message registry entries with the same
17 'Name' field. There may be a use case for this in the future, but there
18 isn't right now.
19
20 registry_json: The message registry JSON
21 """
22
Matt Spinler446d2b92020-03-05 16:49:14 -060023 names = []
Patrick Williamse6555f52022-08-04 13:56:17 -050024 for entry in registry_json["PELs"]:
25 if entry["Name"] in names:
26 sys.exit("Found multiple uses of error {}".format(entry["Name"]))
Matt Spinler5cb5deb2019-09-27 13:51:36 -050027 else:
Patrick Williamse6555f52022-08-04 13:56:17 -050028 names.append(entry["Name"])
Matt Spinler5cb5deb2019-09-27 13:51:36 -050029
30
Matt Spinler55a69782019-10-25 13:49:30 -050031def check_duplicate_reason_codes(registry_json):
32 r"""
33 Check that there aren't any message registry entries with the same
34 'ReasonCode' field.
35
36 registry_json: The message registry JSON
37 """
38
Matt Spinler446d2b92020-03-05 16:49:14 -060039 reasonCodes = []
Patrick Williamse6555f52022-08-04 13:56:17 -050040 for entry in registry_json["PELs"]:
41 if entry["SRC"]["ReasonCode"] in reasonCodes:
42 sys.exit(
43 "Found duplicate SRC reason code {}".format(
44 entry["SRC"]["ReasonCode"]
45 )
46 )
Matt Spinler55a69782019-10-25 13:49:30 -050047 else:
Patrick Williamse6555f52022-08-04 13:56:17 -050048 reasonCodes.append(entry["SRC"]["ReasonCode"])
Matt Spinler55a69782019-10-25 13:49:30 -050049
50
Matt Spinler5cb5deb2019-09-27 13:51:36 -050051def check_component_id(registry_json):
52 r"""
53 Check that the upper byte of the ComponentID field matches the upper byte
54 of the ReasonCode field, but not on "11" type SRCs where they aren't
55 supposed to match.
56
57 registry_json: The message registry JSON
58 """
59
Patrick Williamse6555f52022-08-04 13:56:17 -050060 for entry in registry_json["PELs"]:
Matt Spinler5cb5deb2019-09-27 13:51:36 -050061
62 # Don't check on "11" SRCs as those reason codes aren't supposed to
63 # match the component ID.
Patrick Williamse6555f52022-08-04 13:56:17 -050064 if entry["SRC"].get("Type", "") == "11":
Matt Spinler5cb5deb2019-09-27 13:51:36 -050065 continue
66
Patrick Williamse6555f52022-08-04 13:56:17 -050067 if "ComponentID" in entry:
68 id = int(entry["ComponentID"], 16)
69 reason_code = int(entry["SRC"]["ReasonCode"], 16)
Matt Spinler5cb5deb2019-09-27 13:51:36 -050070
71 if (id & 0xFF00) != (reason_code & 0xFF00):
Patrick Williamse6555f52022-08-04 13:56:17 -050072 sys.exit(
73 "Found mismatching component ID {} vs reason "
74 "code {} for error {}".format(
75 entry["ComponentID"],
76 entry["SRC"]["ReasonCode"],
77 entry["Name"],
78 )
79 )
Matt Spinler5cb5deb2019-09-27 13:51:36 -050080
81
82def check_message_args(registry_json):
83 r"""
84 Check that if the Message field uses the '%' style placeholders that there
85 are that many entries in the MessageArgSources field. Also checks that
86 the MessageArgSources field is present but only if there are placeholders.
87
88 registry_json: The message registry JSON
89 """
90
Patrick Williamse6555f52022-08-04 13:56:17 -050091 for entry in registry_json["PELs"]:
92 num_placeholders = entry["Documentation"]["Message"].count("%")
Matt Spinler5cb5deb2019-09-27 13:51:36 -050093 if num_placeholders == 0:
94 continue
95
Patrick Williamse6555f52022-08-04 13:56:17 -050096 if "MessageArgSources" not in entry["Documentation"]:
97 sys.exit(
98 "Missing MessageArgSources property for error {}".format(
99 entry["Name"]
100 )
101 )
Matt Spinler5cb5deb2019-09-27 13:51:36 -0500102
Patrick Williamse6555f52022-08-04 13:56:17 -0500103 if num_placeholders != len(
104 entry["Documentation"]["MessageArgSources"]
105 ):
106 sys.exit(
107 "Different number of placeholders found in "
108 "Message vs MessageArgSources for error {}".format(
109 entry["Name"]
110 )
111 )
Matt Spinler5cb5deb2019-09-27 13:51:36 -0500112
113
114def validate_schema(registry, schema):
115 r"""
116 Validates the passed in JSON against the passed in schema JSON
117
118 registry: Path of the file containing the registry JSON
119 schema: Path of the file containing the schema JSON
120 Use None to skip the pure schema validation
121 """
122
123 with open(registry) as registry_handle:
124 registry_json = json.load(registry_handle)
125
126 if schema:
127
128 import jsonschema
129
130 with open(schema) as schema_handle:
131 schema_json = json.load(schema_handle)
132
133 try:
134 jsonschema.validate(registry_json, schema_json)
135 except jsonschema.ValidationError as e:
136 print(e)
137 sys.exit("Schema validation failed")
138
139 check_duplicate_names(registry_json)
140
Matt Spinler55a69782019-10-25 13:49:30 -0500141 check_duplicate_reason_codes(registry_json)
142
Matt Spinler5cb5deb2019-09-27 13:51:36 -0500143 check_component_id(registry_json)
144
145 check_message_args(registry_json)
146
147
Patrick Williamse6555f52022-08-04 13:56:17 -0500148if __name__ == "__main__":
Matt Spinler5cb5deb2019-09-27 13:51:36 -0500149
150 parser = argparse.ArgumentParser(
Patrick Williamse6555f52022-08-04 13:56:17 -0500151 description="PEL message registry processor"
152 )
Matt Spinler5cb5deb2019-09-27 13:51:36 -0500153
Patrick Williamse6555f52022-08-04 13:56:17 -0500154 parser.add_argument(
155 "-v",
156 "--validate",
157 action="store_true",
158 dest="validate",
159 help="Validate the JSON using the schema",
160 )
Matt Spinler5cb5deb2019-09-27 13:51:36 -0500161
Patrick Williamse6555f52022-08-04 13:56:17 -0500162 parser.add_argument(
163 "-s",
164 "--schema-file",
165 dest="schema_file",
166 help="The message registry JSON schema file",
167 )
Matt Spinler5cb5deb2019-09-27 13:51:36 -0500168
Patrick Williamse6555f52022-08-04 13:56:17 -0500169 parser.add_argument(
170 "-r",
171 "--registry-file",
172 dest="registry_file",
173 help="The message registry JSON file",
174 )
175 parser.add_argument(
176 "-k",
177 "--skip-schema-validation",
178 action="store_true",
179 dest="skip_schema",
180 help="Skip running schema validation. " "Only do the extra checks.",
181 )
Matt Spinler5cb5deb2019-09-27 13:51:36 -0500182
183 args = parser.parse_args()
184
185 if args.validate:
186 if not args.schema_file:
187 sys.exit("Schema file required")
188
189 if not args.registry_file:
190 sys.exit("Registry file required")
191
192 schema = args.schema_file
193 if args.skip_schema:
194 schema = None
195
196 validate_schema(args.registry_file, schema)