blob: c6a86f85d962c668abb457642ef2821d2d12ce8a [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 # Don't check on "11" SRCs as those reason codes aren't supposed to
62 # match the component ID.
Patrick Williamse6555f52022-08-04 13:56:17 -050063 if entry["SRC"].get("Type", "") == "11":
Matt Spinler5cb5deb2019-09-27 13:51:36 -050064 continue
65
Patrick Williamse6555f52022-08-04 13:56:17 -050066 if "ComponentID" in entry:
67 id = int(entry["ComponentID"], 16)
68 reason_code = int(entry["SRC"]["ReasonCode"], 16)
Matt Spinler5cb5deb2019-09-27 13:51:36 -050069
70 if (id & 0xFF00) != (reason_code & 0xFF00):
Patrick Williamse6555f52022-08-04 13:56:17 -050071 sys.exit(
72 "Found mismatching component ID {} vs reason "
73 "code {} for error {}".format(
74 entry["ComponentID"],
75 entry["SRC"]["ReasonCode"],
76 entry["Name"],
77 )
78 )
Matt Spinler5cb5deb2019-09-27 13:51:36 -050079
80
81def check_message_args(registry_json):
82 r"""
83 Check that if the Message field uses the '%' style placeholders that there
84 are that many entries in the MessageArgSources field. Also checks that
85 the MessageArgSources field is present but only if there are placeholders.
86
87 registry_json: The message registry JSON
88 """
89
Patrick Williamse6555f52022-08-04 13:56:17 -050090 for entry in registry_json["PELs"]:
91 num_placeholders = entry["Documentation"]["Message"].count("%")
Matt Spinler5cb5deb2019-09-27 13:51:36 -050092 if num_placeholders == 0:
93 continue
94
Patrick Williamse6555f52022-08-04 13:56:17 -050095 if "MessageArgSources" not in entry["Documentation"]:
96 sys.exit(
97 "Missing MessageArgSources property for error {}".format(
98 entry["Name"]
99 )
100 )
Matt Spinler5cb5deb2019-09-27 13:51:36 -0500101
Patrick Williamse6555f52022-08-04 13:56:17 -0500102 if num_placeholders != len(
103 entry["Documentation"]["MessageArgSources"]
104 ):
105 sys.exit(
106 "Different number of placeholders found in "
107 "Message vs MessageArgSources for error {}".format(
108 entry["Name"]
109 )
110 )
Matt Spinler5cb5deb2019-09-27 13:51:36 -0500111
112
113def validate_schema(registry, schema):
114 r"""
115 Validates the passed in JSON against the passed in schema JSON
116
117 registry: Path of the file containing the registry JSON
118 schema: Path of the file containing the schema JSON
119 Use None to skip the pure schema validation
120 """
121
122 with open(registry) as registry_handle:
123 registry_json = json.load(registry_handle)
124
125 if schema:
Matt Spinler5cb5deb2019-09-27 13:51:36 -0500126 import jsonschema
127
128 with open(schema) as schema_handle:
129 schema_json = json.load(schema_handle)
130
131 try:
132 jsonschema.validate(registry_json, schema_json)
133 except jsonschema.ValidationError as e:
134 print(e)
135 sys.exit("Schema validation failed")
136
137 check_duplicate_names(registry_json)
138
Matt Spinler55a69782019-10-25 13:49:30 -0500139 check_duplicate_reason_codes(registry_json)
140
Matt Spinler5cb5deb2019-09-27 13:51:36 -0500141 check_component_id(registry_json)
142
143 check_message_args(registry_json)
144
145
Patrick Williamse6555f52022-08-04 13:56:17 -0500146if __name__ == "__main__":
Matt Spinler5cb5deb2019-09-27 13:51:36 -0500147 parser = argparse.ArgumentParser(
Patrick Williamse6555f52022-08-04 13:56:17 -0500148 description="PEL message registry processor"
149 )
Matt Spinler5cb5deb2019-09-27 13:51:36 -0500150
Patrick Williamse6555f52022-08-04 13:56:17 -0500151 parser.add_argument(
152 "-v",
153 "--validate",
154 action="store_true",
155 dest="validate",
156 help="Validate the JSON using the schema",
157 )
Matt Spinler5cb5deb2019-09-27 13:51:36 -0500158
Patrick Williamse6555f52022-08-04 13:56:17 -0500159 parser.add_argument(
160 "-s",
161 "--schema-file",
162 dest="schema_file",
163 help="The message registry JSON schema file",
164 )
Matt Spinler5cb5deb2019-09-27 13:51:36 -0500165
Patrick Williamse6555f52022-08-04 13:56:17 -0500166 parser.add_argument(
167 "-r",
168 "--registry-file",
169 dest="registry_file",
170 help="The message registry JSON file",
171 )
172 parser.add_argument(
173 "-k",
174 "--skip-schema-validation",
175 action="store_true",
176 dest="skip_schema",
Patrick Williamsd27675d2022-12-08 06:18:00 -0600177 help="Skip running schema validation. Only do the extra checks.",
Patrick Williamse6555f52022-08-04 13:56:17 -0500178 )
Matt Spinler5cb5deb2019-09-27 13:51:36 -0500179
180 args = parser.parse_args()
181
182 if args.validate:
183 if not args.schema_file:
184 sys.exit("Schema file required")
185
186 if not args.registry_file:
187 sys.exit("Registry file required")
188
189 schema = args.schema_file
190 if args.skip_schema:
191 schema = None
192
193 validate_schema(args.registry_file, schema)