1*1ed1067cSMatt Spinler#!/usr/bin/env python3 2*1ed1067cSMatt Spinler 3*1ed1067cSMatt Spinlerimport argparse 4*1ed1067cSMatt Spinlerimport json 5*1ed1067cSMatt Spinlerimport sys 6*1ed1067cSMatt Spinler 7*1ed1067cSMatt Spinlerr""" 8*1ed1067cSMatt SpinlerValidates the PEL message registry JSON, which includes checking it against 9*1ed1067cSMatt Spinlera JSON schema using the jsonschema module as well as doing some extra checks 10*1ed1067cSMatt Spinlerthat can't be encoded in the schema. 11*1ed1067cSMatt Spinler""" 12*1ed1067cSMatt Spinler 13*1ed1067cSMatt Spinler 14*1ed1067cSMatt Spinlerdef check_duplicate_names(registry_json): 15*1ed1067cSMatt Spinler r""" 16*1ed1067cSMatt Spinler Check that there aren't any message registry entries with the same 17*1ed1067cSMatt Spinler 'Name' field. There may be a use case for this in the future, but there 18*1ed1067cSMatt Spinler isn't right now. 19*1ed1067cSMatt Spinler 20*1ed1067cSMatt Spinler registry_json: The message registry JSON 21*1ed1067cSMatt Spinler """ 22*1ed1067cSMatt Spinler 23*1ed1067cSMatt Spinler names = [] 24*1ed1067cSMatt Spinler for entry in registry_json["PELs"]: 25*1ed1067cSMatt Spinler if entry["Name"] in names: 26*1ed1067cSMatt Spinler sys.exit("Found multiple uses of error {}".format(entry["Name"])) 27*1ed1067cSMatt Spinler else: 28*1ed1067cSMatt Spinler names.append(entry["Name"]) 29*1ed1067cSMatt Spinler 30*1ed1067cSMatt Spinler 31*1ed1067cSMatt Spinlerdef check_duplicate_reason_codes(registry_json): 32*1ed1067cSMatt Spinler r""" 33*1ed1067cSMatt Spinler Check that there aren't any message registry entries with the same 34*1ed1067cSMatt Spinler 'ReasonCode' field. 35*1ed1067cSMatt Spinler 36*1ed1067cSMatt Spinler registry_json: The message registry JSON 37*1ed1067cSMatt Spinler """ 38*1ed1067cSMatt Spinler 39*1ed1067cSMatt Spinler reasonCodes = [] 40*1ed1067cSMatt Spinler for entry in registry_json["PELs"]: 41*1ed1067cSMatt Spinler if entry["SRC"]["ReasonCode"] in reasonCodes: 42*1ed1067cSMatt Spinler sys.exit( 43*1ed1067cSMatt Spinler "Found duplicate SRC reason code {}".format( 44*1ed1067cSMatt Spinler entry["SRC"]["ReasonCode"] 45*1ed1067cSMatt Spinler ) 46*1ed1067cSMatt Spinler ) 47*1ed1067cSMatt Spinler else: 48*1ed1067cSMatt Spinler reasonCodes.append(entry["SRC"]["ReasonCode"]) 49*1ed1067cSMatt Spinler 50*1ed1067cSMatt Spinler 51*1ed1067cSMatt Spinlerdef check_component_id(registry_json): 52*1ed1067cSMatt Spinler r""" 53*1ed1067cSMatt Spinler Check that the upper byte of the ComponentID field matches the upper byte 54*1ed1067cSMatt Spinler of the ReasonCode field, but not on "11" type SRCs where they aren't 55*1ed1067cSMatt Spinler supposed to match. 56*1ed1067cSMatt Spinler 57*1ed1067cSMatt Spinler registry_json: The message registry JSON 58*1ed1067cSMatt Spinler """ 59*1ed1067cSMatt Spinler 60*1ed1067cSMatt Spinler for entry in registry_json["PELs"]: 61*1ed1067cSMatt Spinler # Don't check on "11" SRCs as those reason codes aren't supposed to 62*1ed1067cSMatt Spinler # match the component ID. 63*1ed1067cSMatt Spinler if entry["SRC"].get("Type", "") == "11": 64*1ed1067cSMatt Spinler continue 65*1ed1067cSMatt Spinler 66*1ed1067cSMatt Spinler if "ComponentID" in entry: 67*1ed1067cSMatt Spinler id = int(entry["ComponentID"], 16) 68*1ed1067cSMatt Spinler reason_code = int(entry["SRC"]["ReasonCode"], 16) 69*1ed1067cSMatt Spinler 70*1ed1067cSMatt Spinler if (id & 0xFF00) != (reason_code & 0xFF00): 71*1ed1067cSMatt Spinler sys.exit( 72*1ed1067cSMatt Spinler "Found mismatching component ID {} vs reason " 73*1ed1067cSMatt Spinler "code {} for error {}".format( 74*1ed1067cSMatt Spinler entry["ComponentID"], 75*1ed1067cSMatt Spinler entry["SRC"]["ReasonCode"], 76*1ed1067cSMatt Spinler entry["Name"], 77*1ed1067cSMatt Spinler ) 78*1ed1067cSMatt Spinler ) 79*1ed1067cSMatt Spinler 80*1ed1067cSMatt Spinler 81*1ed1067cSMatt Spinlerdef check_message_args(registry_json): 82*1ed1067cSMatt Spinler r""" 83*1ed1067cSMatt Spinler Check that if the Message field uses the '%' style placeholders that there 84*1ed1067cSMatt Spinler are that many entries in the MessageArgSources field. Also checks that 85*1ed1067cSMatt Spinler the MessageArgSources field is present but only if there are placeholders. 86*1ed1067cSMatt Spinler 87*1ed1067cSMatt Spinler registry_json: The message registry JSON 88*1ed1067cSMatt Spinler """ 89*1ed1067cSMatt Spinler 90*1ed1067cSMatt Spinler for entry in registry_json["PELs"]: 91*1ed1067cSMatt Spinler num_placeholders = entry["Documentation"]["Message"].count("%") 92*1ed1067cSMatt Spinler if num_placeholders == 0: 93*1ed1067cSMatt Spinler continue 94*1ed1067cSMatt Spinler 95*1ed1067cSMatt Spinler if "MessageArgSources" not in entry["Documentation"]: 96*1ed1067cSMatt Spinler sys.exit( 97*1ed1067cSMatt Spinler "Missing MessageArgSources property for error {}".format( 98*1ed1067cSMatt Spinler entry["Name"] 99*1ed1067cSMatt Spinler ) 100*1ed1067cSMatt Spinler ) 101*1ed1067cSMatt Spinler 102*1ed1067cSMatt Spinler if num_placeholders != len( 103*1ed1067cSMatt Spinler entry["Documentation"]["MessageArgSources"] 104*1ed1067cSMatt Spinler ): 105*1ed1067cSMatt Spinler sys.exit( 106*1ed1067cSMatt Spinler "Different number of placeholders found in " 107*1ed1067cSMatt Spinler "Message vs MessageArgSources for error {}".format( 108*1ed1067cSMatt Spinler entry["Name"] 109*1ed1067cSMatt Spinler ) 110*1ed1067cSMatt Spinler ) 111*1ed1067cSMatt Spinler 112*1ed1067cSMatt Spinler 113*1ed1067cSMatt Spinlerdef validate_schema(registry, schema): 114*1ed1067cSMatt Spinler r""" 115*1ed1067cSMatt Spinler Validates the passed in JSON against the passed in schema JSON 116*1ed1067cSMatt Spinler 117*1ed1067cSMatt Spinler registry: Path of the file containing the registry JSON 118*1ed1067cSMatt Spinler schema: Path of the file containing the schema JSON 119*1ed1067cSMatt Spinler Use None to skip the pure schema validation 120*1ed1067cSMatt Spinler """ 121*1ed1067cSMatt Spinler 122*1ed1067cSMatt Spinler with open(registry) as registry_handle: 123*1ed1067cSMatt Spinler registry_json = json.load(registry_handle) 124*1ed1067cSMatt Spinler 125*1ed1067cSMatt Spinler if schema: 126*1ed1067cSMatt Spinler import jsonschema 127*1ed1067cSMatt Spinler 128*1ed1067cSMatt Spinler with open(schema) as schema_handle: 129*1ed1067cSMatt Spinler schema_json = json.load(schema_handle) 130*1ed1067cSMatt Spinler 131*1ed1067cSMatt Spinler try: 132*1ed1067cSMatt Spinler jsonschema.validate(registry_json, schema_json) 133*1ed1067cSMatt Spinler except jsonschema.ValidationError as e: 134*1ed1067cSMatt Spinler print(e) 135*1ed1067cSMatt Spinler sys.exit("Schema validation failed") 136*1ed1067cSMatt Spinler 137*1ed1067cSMatt Spinler check_duplicate_names(registry_json) 138*1ed1067cSMatt Spinler 139*1ed1067cSMatt Spinler check_duplicate_reason_codes(registry_json) 140*1ed1067cSMatt Spinler 141*1ed1067cSMatt Spinler check_component_id(registry_json) 142*1ed1067cSMatt Spinler 143*1ed1067cSMatt Spinler check_message_args(registry_json) 144*1ed1067cSMatt Spinler 145*1ed1067cSMatt Spinler 146*1ed1067cSMatt Spinlerif __name__ == "__main__": 147*1ed1067cSMatt Spinler parser = argparse.ArgumentParser( 148*1ed1067cSMatt Spinler description="PEL message registry validator" 149*1ed1067cSMatt Spinler ) 150*1ed1067cSMatt Spinler 151*1ed1067cSMatt Spinler parser.add_argument( 152*1ed1067cSMatt Spinler "-s", 153*1ed1067cSMatt Spinler "--schema-file", 154*1ed1067cSMatt Spinler dest="schema_file", 155*1ed1067cSMatt Spinler help="The message registry JSON schema file", 156*1ed1067cSMatt Spinler required=True, 157*1ed1067cSMatt Spinler ) 158*1ed1067cSMatt Spinler 159*1ed1067cSMatt Spinler parser.add_argument( 160*1ed1067cSMatt Spinler "-r", 161*1ed1067cSMatt Spinler "--registry-file", 162*1ed1067cSMatt Spinler dest="registry_file", 163*1ed1067cSMatt Spinler help="The message registry JSON file", 164*1ed1067cSMatt Spinler required=True, 165*1ed1067cSMatt Spinler ) 166*1ed1067cSMatt Spinler 167*1ed1067cSMatt Spinler parser.add_argument( 168*1ed1067cSMatt Spinler "-k", 169*1ed1067cSMatt Spinler "--skip-schema-validation", 170*1ed1067cSMatt Spinler action="store_true", 171*1ed1067cSMatt Spinler dest="skip_schema", 172*1ed1067cSMatt Spinler help="Skip running schema validation. Only do the extra checks.", 173*1ed1067cSMatt Spinler ) 174*1ed1067cSMatt Spinler 175*1ed1067cSMatt Spinler args = parser.parse_args() 176*1ed1067cSMatt Spinler 177*1ed1067cSMatt Spinler schema = args.schema_file 178*1ed1067cSMatt Spinler if args.skip_schema: 179*1ed1067cSMatt Spinler schema = None 180*1ed1067cSMatt Spinler 181*1ed1067cSMatt Spinler validate_schema(args.registry_file, schema) 182