1#!/usr/bin/env python3 2 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 23 names = [] 24 for entry in registry_json["PELs"]: 25 if entry["Name"] in names: 26 sys.exit("Found multiple uses of error {}".format(entry["Name"])) 27 else: 28 names.append(entry["Name"]) 29 30 31def 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 39 reasonCodes = [] 40 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 ) 47 else: 48 reasonCodes.append(entry["SRC"]["ReasonCode"]) 49 50 51def 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 60 for entry in registry_json["PELs"]: 61 # Don't check on "11" SRCs as those reason codes aren't supposed to 62 # match the component ID. 63 if entry["SRC"].get("Type", "") == "11": 64 continue 65 66 if "ComponentID" in entry: 67 id = int(entry["ComponentID"], 16) 68 reason_code = int(entry["SRC"]["ReasonCode"], 16) 69 70 if (id & 0xFF00) != (reason_code & 0xFF00): 71 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 ) 79 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 90 for entry in registry_json["PELs"]: 91 num_placeholders = entry["Documentation"]["Message"].count("%") 92 if num_placeholders == 0: 93 continue 94 95 if "MessageArgSources" not in entry["Documentation"]: 96 sys.exit( 97 "Missing MessageArgSources property for error {}".format( 98 entry["Name"] 99 ) 100 ) 101 102 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 ) 111 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: 126 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 139 check_duplicate_reason_codes(registry_json) 140 141 check_component_id(registry_json) 142 143 check_message_args(registry_json) 144 145 146if __name__ == "__main__": 147 parser = argparse.ArgumentParser( 148 description="PEL message registry validator" 149 ) 150 151 parser.add_argument( 152 "-s", 153 "--schema-file", 154 dest="schema_file", 155 help="The message registry JSON schema file", 156 required=True, 157 ) 158 159 parser.add_argument( 160 "-r", 161 "--registry-file", 162 dest="registry_file", 163 help="The message registry JSON file", 164 required=True, 165 ) 166 167 parser.add_argument( 168 "-k", 169 "--skip-schema-validation", 170 action="store_true", 171 dest="skip_schema", 172 help="Skip running schema validation. Only do the extra checks.", 173 ) 174 175 args = parser.parse_args() 176 177 schema = args.schema_file 178 if args.skip_schema: 179 schema = None 180 181 validate_schema(args.registry_file, schema) 182