1#!/usr/bin/env python3 2import requests 3import zipfile 4from io import BytesIO 5import os 6from collections import OrderedDict 7import shutil 8import json 9 10import xml.etree.ElementTree as ET 11 12VERSION = "DSP8010_2021.4" 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 'EthernetInterface', 45 'EthernetInterfaceCollection', 46 'Event', 47 'EventDestination', 48 'EventDestinationCollection', 49 'EventService', 50 'IPAddresses', 51 'JsonSchemaFile', 52 'JsonSchemaFileCollection', # redfish/v1/JsonSchemas 53 'LogEntry', 54 'LogEntryCollection', 55 'LogService', 56 'LogServiceCollection', 57 'Manager', 58 'ManagerAccount', 59 'ManagerAccountCollection', 60 'ManagerCollection', 61 'ManagerDiagnosticData', 62 'ManagerNetworkProtocol', 63 'Memory', 64 'MemoryCollection', 65 'Message', 66 'MessageRegistry', 67 'MessageRegistryCollection', 68 'MessageRegistryFile', 69 'MessageRegistryFileCollection', 70 'MetricDefinition', 71 'MetricDefinitionCollection', 72 'MetricReport', 73 'MetricReportCollection', 74 'MetricReportDefinition', 75 'MetricReportDefinitionCollection', 76 'OperatingConfig', 77 'OperatingConfigCollection', 78 'PCIeDevice', 79 'PCIeDeviceCollection', 80 'PCIeFunction', 81 'PCIeFunctionCollection', 82 'PhysicalContext', 83 'PCIeSlots', 84 'Power', 85 'Privileges', # Used in Role 86 'Processor', 87 'ProcessorCollection', 88 'RedfishError', 89 'RedfishExtensions', 90 'Redundancy', 91 'Resource', 92 'Role', 93 'RoleCollection', 94 'Sensor', 95 'SensorCollection', 96 'ServiceRoot', 97 'Session', 98 'SessionCollection', 99 'SessionService', 100 'Settings', 101 'SoftwareInventory', 102 'SoftwareInventoryCollection', 103 'Storage', 104 'StorageCollection', 105 'StorageController', 106 'StorageControllerCollection', 107 'Task', 108 'TaskCollection', 109 'TaskService', 110 'TelemetryService', 111 'Thermal', 112 'ThermalSubsystem', 113 'Triggers', 114 'TriggersCollection', 115 'UpdateService', 116 'VLanNetworkInterfaceCollection', 117 'VLanNetworkInterface', 118 'VirtualMedia', 119 'VirtualMediaCollection', 120 'odata', 121 'odata-v4', 122 'redfish-error', 123 'redfish-payload-annotations', 124 'redfish-schema', 125 'redfish-schema-v1', 126] 127 128SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) 129 130proxies = { 131 'https': os.environ.get("https_proxy", None) 132} 133 134r = requests.get( 135 'https://www.dmtf.org/sites/default/files/standards/documents/' + 136 VERSION + 137 '.zip', 138 proxies=proxies) 139 140r.raise_for_status() 141 142 143static_path = os.path.realpath(os.path.join(SCRIPT_DIR, "..", "static", 144 "redfish", "v1")) 145 146 147cpp_path = os.path.realpath(os.path.join(SCRIPT_DIR, "..", "redfish-core", 148 "include")) 149 150 151schema_path = os.path.join(static_path, "schema") 152json_schema_path = os.path.join(static_path, "JsonSchemas") 153metadata_index_path = os.path.join(static_path, "$metadata", "index.xml") 154 155zipBytesIO = BytesIO(r.content) 156zip_ref = zipfile.ZipFile(zipBytesIO) 157 158# Remove the old files 159skip_prefixes = ('Oem') 160if os.path.exists(schema_path): 161 files = [os.path.join(schema_path, f) for f in os.listdir(schema_path) 162 if not f.startswith(skip_prefixes)] 163 for f in files: 164 os.remove(f) 165if os.path.exists(json_schema_path): 166 files = [os.path.join(json_schema_path, f) for f in 167 os.listdir(json_schema_path) if not f.startswith(skip_prefixes)] 168 for f in files: 169 if (os.path.isfile(f)): 170 os.remove(f) 171 else: 172 shutil.rmtree(f) 173os.remove(metadata_index_path) 174 175if not os.path.exists(schema_path): 176 os.makedirs(schema_path) 177if not os.path.exists(json_schema_path): 178 os.makedirs(json_schema_path) 179 180with open(metadata_index_path, 'w') as metadata_index: 181 182 metadata_index.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n") 183 metadata_index.write( 184 "<edmx:Edmx xmlns:edmx=" 185 "\"http://docs.oasis-open.org/odata/ns/edmx\"" 186 " Version=\"4.0\">\n") 187 188 for zip_filepath in zip_ref.namelist(): 189 if zip_filepath.startswith('csdl/') and \ 190 (zip_filepath != VERSION + "/csdl/") and \ 191 (zip_filepath != "csdl/"): 192 filename = os.path.basename(zip_filepath) 193 194 # filename looks like Zone_v1.xml 195 filenamesplit = filename.split("_") 196 if filenamesplit[0] not in include_list: 197 print("excluding schema: " + filename) 198 continue 199 200 with open(os.path.join(schema_path, filename), 'wb') as schema_out: 201 202 metadata_index.write( 203 " <edmx:Reference Uri=\"/redfish/v1/schema/" + 204 filename + 205 "\">\n") 206 207 content = zip_ref.read(zip_filepath) 208 content = content.replace(b'\r\n', b'\n') 209 xml_root = ET.fromstring(content) 210 edmx = "{http://docs.oasis-open.org/odata/ns/edmx}" 211 edm = "{http://docs.oasis-open.org/odata/ns/edm}" 212 for edmx_child in xml_root: 213 if edmx_child.tag == edmx + "DataServices": 214 for data_child in edmx_child: 215 if data_child.tag == edm + "Schema": 216 namespace = data_child.attrib["Namespace"] 217 if namespace.startswith("RedfishExtensions"): 218 metadata_index.write( 219 " " 220 "<edmx:Include Namespace=\"" + 221 namespace + 222 "\" Alias=\"Redfish\"/>\n" 223 ) 224 225 else: 226 metadata_index.write( 227 " " 228 "<edmx:Include Namespace=\"" 229 + namespace + "\"/>\n" 230 ) 231 schema_out.write(content) 232 metadata_index.write(" </edmx:Reference>\n") 233 234 metadata_index.write(" <edmx:DataServices>\n" 235 " <Schema " 236 "xmlns=\"http://docs.oasis-open.org/odata/ns/edm\" " 237 "Namespace=\"Service\">\n" 238 " <EntityContainer Name=\"Service\" " 239 "Extends=\"ServiceRoot.v1_0_0.ServiceContainer\"/>\n" 240 " </Schema>\n" 241 " </edmx:DataServices>\n" 242 ) 243 # TODO:Issue#32 There's a bug in the script that currently deletes this 244 # schema (because it's an OEM schema). Because it's the only six, and we 245 # don't update schemas very often, we just manually fix it. Need a 246 # permanent fix to the script. 247 metadata_index.write( 248 " <edmx:Reference Uri=\"/redfish/v1/schema/OemManager_v1.xml\">\n") 249 metadata_index.write(" <edmx:Include Namespace=\"OemManager\"/>\n") 250 metadata_index.write(" </edmx:Reference>\n") 251 252 metadata_index.write( 253 " <edmx:Reference Uri=\"" 254 "/redfish/v1/schema/OemComputerSystem_v1.xml\">\n") 255 metadata_index.write( 256 " <edmx:Include Namespace=\"OemComputerSystem\"/>\n") 257 metadata_index.write(" </edmx:Reference>\n") 258 259 metadata_index.write( 260 " <edmx:Reference Uri=\"" 261 "/redfish/v1/schema/OemVirtualMedia_v1.xml\">\n") 262 metadata_index.write( 263 " <edmx:Include Namespace=\"OemVirtualMedia\"/>\n") 264 metadata_index.write( 265 " <edmx:Include Namespace=\"OemVirtualMedia.v1_0_0\"/>\n") 266 metadata_index.write(" </edmx:Reference>\n") 267 268 metadata_index.write( 269 " <edmx:Reference Uri=\"" 270 "/redfish/v1/schema/OemAccountService_v1.xml\">\n") 271 metadata_index.write( 272 " <edmx:Include Namespace=\"OemAccountService\"/>\n") 273 metadata_index.write( 274 " <edmx:Include Namespace=\"OemAccountService.v1_0_0\"/>\n") 275 metadata_index.write(" </edmx:Reference>\n") 276 277 metadata_index.write( 278 " <edmx:Reference Uri=\"/redfish/v1/schema/OemSession_v1.xml\">\n") 279 metadata_index.write(" <edmx:Include Namespace=\"OemSession\"/>\n") 280 metadata_index.write( 281 " <edmx:Include Namespace=\"OemSession.v1_0_0\"/>\n") 282 metadata_index.write(" </edmx:Reference>\n") 283 284 metadata_index.write("</edmx:Edmx>\n") 285 286schema_files = {} 287for zip_filepath in zip_ref.namelist(): 288 if zip_filepath.startswith(os.path.join('json-schema/')): 289 filename = os.path.basename(zip_filepath) 290 filenamesplit = filename.split(".") 291 292 # exclude schemas again to save flash space 293 if filenamesplit[0] not in include_list: 294 continue 295 296 if len(filenamesplit) == 3: 297 thisSchemaVersion = schema_files.get(filenamesplit[0], None) 298 if thisSchemaVersion is None: 299 schema_files[filenamesplit[0]] = filenamesplit[1] 300 else: 301 # need to see if we're a newer version. 302 if list(map(int, filenamesplit[1][1:].split("_"))) > list(map( 303 int, thisSchemaVersion[1:].split("_"))): 304 schema_files[filenamesplit[0]] = filenamesplit[1] 305 else: 306 # Unversioned schema include directly. Invent a version so it can 307 # still be sorted against 308 schema_files[filenamesplit[0]] = "v0_0_0" 309 310for schema, version in schema_files.items(): 311 basename = schema 312 if version != "v0_0_0": 313 basename += "." + version 314 basename += ".json" 315 316 zip_filepath = os.path.join("json-schema", basename) 317 schemadir = os.path.join(json_schema_path, schema) 318 os.makedirs(schemadir) 319 320 with open(os.path.join(schemadir, schema + ".json"), 'wb') as schema_file: 321 schema_file.write(zip_ref.read(zip_filepath).replace(b'\r\n', b'\n')) 322 323with open(os.path.join(cpp_path, "schemas.hpp"), 'w') as hpp_file: 324 hpp_file.write( 325 "#pragma once\n" 326 "{WARNING}\n" 327 "// clang-format off\n" 328 "\n" 329 "namespace redfish\n" 330 "{{\n" 331 " constexpr std::array schemas {{\n" 332 .format( 333 WARNING=WARNING)) 334 for schema_file in schema_files: 335 hpp_file.write(" \"{}\",\n".format(schema_file)) 336 337 hpp_file.write( 338 " };\n" 339 "}\n" 340 ) 341 342zip_ref.close() 343