1#!/usr/bin/env python3 2import os 3import shutil 4import xml.etree.ElementTree as ET 5import zipfile 6from collections import OrderedDict, defaultdict 7from io import BytesIO 8 9import generate_schema_enums 10import requests 11 12VERSION = "DSP8010_2022.2" 13 14WARNING = """/**************************************************************** 15 * READ THIS WARNING FIRST 16 * This is an auto-generated header which contains definitions 17 * for Redfish DMTF defined schemas. 18 * DO NOT modify this registry outside of running the 19 * update_schemas.py script. The definitions contained within 20 * this file are owned by DMTF. Any modifications to these files 21 * should be first pushed to the relevant registry in the DMTF 22 * github organization. 23 ***************************************************************/""" 24 25# To use a new schema, add to list and rerun tool 26include_list = [ 27 "AccountService", 28 "ActionInfo", 29 "Assembly", 30 "AttributeRegistry", 31 "Bios", 32 "Cable", 33 "CableCollection", 34 "Certificate", 35 "CertificateCollection", 36 "CertificateLocations", 37 "CertificateService", 38 "Chassis", 39 "ChassisCollection", 40 "ComputerSystem", 41 "ComputerSystemCollection", 42 "Drive", 43 "DriveCollection", 44 "EnvironmentMetrics", 45 "EthernetInterface", 46 "EthernetInterfaceCollection", 47 "Event", 48 "EventDestination", 49 "EventDestinationCollection", 50 "EventService", 51 "Fan", 52 "FanCollection", 53 "IPAddresses", 54 "JsonSchemaFile", 55 "JsonSchemaFileCollection", # redfish/v1/JsonSchemas 56 "LogEntry", 57 "LogEntryCollection", 58 "LogService", 59 "LogServiceCollection", 60 "Manager", 61 "ManagerAccount", 62 "ManagerAccountCollection", 63 "ManagerCollection", 64 "ManagerDiagnosticData", 65 "ManagerNetworkProtocol", 66 "Memory", 67 "MemoryCollection", 68 "Message", 69 "MessageRegistry", 70 "MessageRegistryCollection", 71 "MessageRegistryFile", 72 "MessageRegistryFileCollection", 73 "MetricDefinition", 74 "MetricDefinitionCollection", 75 "MetricReport", 76 "MetricReportCollection", 77 "MetricReportDefinition", 78 "MetricReportDefinitionCollection", 79 "OperatingConfig", 80 "OperatingConfigCollection", 81 "PCIeDevice", 82 "PCIeDeviceCollection", 83 "PCIeFunction", 84 "PCIeFunctionCollection", 85 "PhysicalContext", 86 "PCIeSlots", 87 "Power", 88 "PowerSubsystem", 89 "PowerSupply", 90 "PowerSupplyCollection", 91 "Privileges", # Used in Role 92 "Processor", 93 "ProcessorCollection", 94 "RedfishError", 95 "RedfishExtensions", 96 "Redundancy", 97 "Resource", 98 "Role", 99 "RoleCollection", 100 "Sensor", 101 "SensorCollection", 102 "ServiceRoot", 103 "Session", 104 "SessionCollection", 105 "SessionService", 106 "Settings", 107 "SoftwareInventory", 108 "SoftwareInventoryCollection", 109 "Storage", 110 "StorageCollection", 111 "StorageController", 112 "StorageControllerCollection", 113 "Task", 114 "TaskCollection", 115 "TaskService", 116 "TelemetryService", 117 "Thermal", 118 "ThermalMetrics", 119 "ThermalSubsystem", 120 "Triggers", 121 "TriggersCollection", 122 "UpdateService", 123 "VLanNetworkInterfaceCollection", 124 "VLanNetworkInterface", 125 "VirtualMedia", 126 "VirtualMediaCollection", 127 "odata", 128 "odata-v4", 129 "redfish-error", 130 "redfish-payload-annotations", 131 "redfish-schema", 132 "redfish-schema-v1", 133] 134 135SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) 136 137proxies = {"https": os.environ.get("https_proxy", None)} 138 139r = requests.get( 140 "https://www.dmtf.org/sites/default/files/standards/documents/" 141 + VERSION 142 + ".zip", 143 proxies=proxies, 144) 145 146r.raise_for_status() 147 148 149static_path = os.path.realpath( 150 os.path.join(SCRIPT_DIR, "..", "static", "redfish", "v1") 151) 152 153 154cpp_path = os.path.realpath( 155 os.path.join(SCRIPT_DIR, "..", "redfish-core", "include") 156) 157 158 159schema_path = os.path.join(static_path, "schema") 160json_schema_path = os.path.join(static_path, "JsonSchemas") 161metadata_index_path = os.path.join(static_path, "$metadata", "index.xml") 162 163zipBytesIO = BytesIO(r.content) 164zip_ref = zipfile.ZipFile(zipBytesIO) 165 166 167class SchemaVersion: 168 """ 169 A Python class for sorting Redfish schema versions. Allows sorting Redfish 170 versions in the way humans expect, by comparing version strings as lists 171 (ie 0_2_0 comes before 0_10_0) in the way humans expect. It does case 172 insensitive schema name comparisons 173 """ 174 175 def __init__(self, key): 176 key = str.casefold(key) 177 178 split_tup = key.split(".") 179 self.version_pieces = [split_tup[0]] 180 if len(split_tup) < 2: 181 return 182 version = split_tup[1] 183 184 if version.startswith("v"): 185 version = version[1:] 186 if any(char.isdigit() for char in version): 187 self.version_pieces.extend([int(x) for x in version.split("_")]) 188 189 def __lt__(self, other): 190 return self.version_pieces < other.version_pieces 191 192 193# Remove the old files 194skip_prefixes = "Oem" 195if os.path.exists(schema_path): 196 files = [ 197 os.path.join(schema_path, f) 198 for f in os.listdir(schema_path) 199 if not f.startswith(skip_prefixes) 200 ] 201 for f in files: 202 os.remove(f) 203if os.path.exists(json_schema_path): 204 files = [ 205 os.path.join(json_schema_path, f) 206 for f in os.listdir(json_schema_path) 207 if not f.startswith(skip_prefixes) 208 ] 209 for f in files: 210 if os.path.isfile(f): 211 os.remove(f) 212 else: 213 shutil.rmtree(f) 214try: 215 os.remove(metadata_index_path) 216except FileNotFoundError: 217 pass 218 219if not os.path.exists(schema_path): 220 os.makedirs(schema_path) 221if not os.path.exists(json_schema_path): 222 os.makedirs(json_schema_path) 223 224csdl_filenames = [] 225json_schema_files = defaultdict(list) 226 227for zip_file in zip_ref.infolist(): 228 if zip_file.is_dir(): 229 continue 230 if zip_file.filename.startswith("csdl/"): 231 csdl_filenames.append(os.path.basename(zip_file.filename)) 232 elif zip_file.filename.startswith("json-schema/"): 233 filename = os.path.basename(zip_file.filename) 234 filenamesplit = filename.split(".") 235 # exclude schemas again to save flash space 236 if filenamesplit[0] not in include_list: 237 continue 238 json_schema_files[filenamesplit[0]].append(filename) 239 elif zip_file.filename.startswith("openapi/"): 240 pass 241 elif zip_file.filename.startswith("dictionaries/"): 242 pass 243 244# sort the json files by version 245for key, value in json_schema_files.items(): 246 value.sort(key=SchemaVersion, reverse=True) 247 248# Create a dictionary ordered by schema name 249json_schema_files = OrderedDict( 250 sorted(json_schema_files.items(), key=lambda x: SchemaVersion(x[0])) 251) 252 253csdl_filenames.sort(key=SchemaVersion) 254with open(metadata_index_path, "w") as metadata_index: 255 metadata_index.write('<?xml version="1.0" encoding="UTF-8"?>\n') 256 metadata_index.write( 257 "<edmx:Edmx xmlns:edmx=" 258 '"http://docs.oasis-open.org/odata/ns/edmx"' 259 ' Version="4.0">\n' 260 ) 261 262 for filename in csdl_filenames: 263 # filename looks like Zone_v1.xml 264 filenamesplit = filename.split("_") 265 if filenamesplit[0] not in include_list: 266 print("excluding schema: " + filename) 267 continue 268 269 with open(os.path.join(schema_path, filename), "wb") as schema_out: 270 metadata_index.write( 271 ' <edmx:Reference Uri="/redfish/v1/schema/' 272 + filename 273 + '">\n' 274 ) 275 276 content = zip_ref.read(os.path.join("csdl", filename)) 277 content = content.replace(b"\r\n", b"\n") 278 xml_root = ET.fromstring(content) 279 edmx = "{http://docs.oasis-open.org/odata/ns/edmx}" 280 edm = "{http://docs.oasis-open.org/odata/ns/edm}" 281 for edmx_child in xml_root: 282 if edmx_child.tag == edmx + "DataServices": 283 for data_child in edmx_child: 284 if data_child.tag == edm + "Schema": 285 namespace = data_child.attrib["Namespace"] 286 if namespace.startswith("RedfishExtensions"): 287 metadata_index.write( 288 ' <edmx:Include Namespace="' 289 + namespace 290 + '" Alias="Redfish"/>\n' 291 ) 292 293 else: 294 metadata_index.write( 295 ' <edmx:Include Namespace="' 296 + namespace 297 + '"/>\n' 298 ) 299 schema_out.write(content) 300 metadata_index.write(" </edmx:Reference>\n") 301 302 metadata_index.write( 303 " <edmx:DataServices>\n" 304 " <Schema " 305 'xmlns="http://docs.oasis-open.org/odata/ns/edm" ' 306 'Namespace="Service">\n' 307 ' <EntityContainer Name="Service" ' 308 'Extends="ServiceRoot.v1_0_0.ServiceContainer"/>\n' 309 " </Schema>\n" 310 " </edmx:DataServices>\n" 311 ) 312 # TODO:Issue#32 There's a bug in the script that currently deletes this 313 # schema (because it's an OEM schema). Because it's the only six, and we 314 # don't update schemas very often, we just manually fix it. Need a 315 # permanent fix to the script. 316 metadata_index.write( 317 ' <edmx:Reference Uri="/redfish/v1/schema/OemManager_v1.xml">\n' 318 ) 319 metadata_index.write(' <edmx:Include Namespace="OemManager"/>\n') 320 metadata_index.write(" </edmx:Reference>\n") 321 322 metadata_index.write( 323 ' <edmx:Reference Uri="' 324 '/redfish/v1/schema/OemComputerSystem_v1.xml">\n' 325 ) 326 metadata_index.write( 327 ' <edmx:Include Namespace="OemComputerSystem"/>\n' 328 ) 329 metadata_index.write(" </edmx:Reference>\n") 330 331 metadata_index.write( 332 ' <edmx:Reference Uri="' 333 '/redfish/v1/schema/OemVirtualMedia_v1.xml">\n' 334 ) 335 metadata_index.write( 336 ' <edmx:Include Namespace="OemVirtualMedia"/>\n' 337 ) 338 metadata_index.write( 339 ' <edmx:Include Namespace="OemVirtualMedia.v1_0_0"/>\n' 340 ) 341 metadata_index.write(" </edmx:Reference>\n") 342 343 metadata_index.write( 344 ' <edmx:Reference Uri="' 345 '/redfish/v1/schema/OemAccountService_v1.xml">\n' 346 ) 347 metadata_index.write( 348 ' <edmx:Include Namespace="OemAccountService"/>\n' 349 ) 350 metadata_index.write( 351 ' <edmx:Include Namespace="OemAccountService.v1_0_0"/>\n' 352 ) 353 metadata_index.write(" </edmx:Reference>\n") 354 355 metadata_index.write( 356 ' <edmx:Reference Uri="/redfish/v1/schema/OemSession_v1.xml">\n' 357 ) 358 metadata_index.write(' <edmx:Include Namespace="OemSession"/>\n') 359 metadata_index.write( 360 ' <edmx:Include Namespace="OemSession.v1_0_0"/>\n' 361 ) 362 metadata_index.write(" </edmx:Reference>\n") 363 364 metadata_index.write("</edmx:Edmx>\n") 365 366 367for schema, version in json_schema_files.items(): 368 zip_filepath = os.path.join("json-schema", version[0]) 369 schemadir = os.path.join(json_schema_path, schema) 370 os.makedirs(schemadir) 371 372 with open(os.path.join(schemadir, schema + ".json"), "wb") as schema_file: 373 schema_file.write(zip_ref.read(zip_filepath).replace(b"\r\n", b"\n")) 374 375with open(os.path.join(cpp_path, "schemas.hpp"), "w") as hpp_file: 376 hpp_file.write( 377 "#pragma once\n" 378 "{WARNING}\n" 379 "// clang-format off\n" 380 "#include <array>\n" 381 "\n" 382 "namespace redfish\n" 383 "{{\n" 384 " constexpr std::array schemas {{\n".format(WARNING=WARNING) 385 ) 386 for schema_file in json_schema_files: 387 hpp_file.write(' "{}",\n'.format(schema_file)) 388 389 hpp_file.write(" };\n}\n") 390 391zip_ref.close() 392 393generate_schema_enums.main() 394