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 'Triggers', 113 'TriggersCollection', 114 'UpdateService', 115 'VLanNetworkInterfaceCollection', 116 'VLanNetworkInterface', 117 'VirtualMedia', 118 'VirtualMediaCollection', 119 'odata', 120 'odata-v4', 121 'redfish-error', 122 'redfish-payload-annotations', 123 'redfish-schema', 124 'redfish-schema-v1', 125] 126 127SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) 128 129proxies = { 130 'https': os.environ.get("https_proxy", None) 131} 132 133r = requests.get( 134 'https://www.dmtf.org/sites/default/files/standards/documents/' + 135 VERSION + 136 '.zip', 137 proxies=proxies) 138 139r.raise_for_status() 140 141 142static_path = os.path.realpath(os.path.join(SCRIPT_DIR, "..", "static", 143 "redfish", "v1")) 144 145 146cpp_path = os.path.realpath(os.path.join(SCRIPT_DIR, "..", "redfish-core", 147 "include")) 148 149 150schema_path = os.path.join(static_path, "schema") 151json_schema_path = os.path.join(static_path, "JsonSchemas") 152metadata_index_path = os.path.join(static_path, "$metadata", "index.xml") 153 154zipBytesIO = BytesIO(r.content) 155zip_ref = zipfile.ZipFile(zipBytesIO) 156 157# Remove the old files 158skip_prefixes = ('Oem') 159if os.path.exists(schema_path): 160 files = [os.path.join(schema_path, f) for f in os.listdir(schema_path) 161 if not f.startswith(skip_prefixes)] 162 for f in files: 163 os.remove(f) 164if os.path.exists(json_schema_path): 165 files = [os.path.join(json_schema_path, f) for f in 166 os.listdir(json_schema_path) if not f.startswith(skip_prefixes)] 167 for f in files: 168 if (os.path.isfile(f)): 169 os.remove(f) 170 else: 171 shutil.rmtree(f) 172os.remove(metadata_index_path) 173 174if not os.path.exists(schema_path): 175 os.makedirs(schema_path) 176if not os.path.exists(json_schema_path): 177 os.makedirs(json_schema_path) 178 179with open(metadata_index_path, 'w') as metadata_index: 180 181 metadata_index.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n") 182 metadata_index.write( 183 "<edmx:Edmx xmlns:edmx=" 184 "\"http://docs.oasis-open.org/odata/ns/edmx\"" 185 " Version=\"4.0\">\n") 186 187 for zip_filepath in zip_ref.namelist(): 188 if zip_filepath.startswith('csdl/') and \ 189 (zip_filepath != VERSION + "/csdl/") and \ 190 (zip_filepath != "csdl/"): 191 filename = os.path.basename(zip_filepath) 192 193 # filename looks like Zone_v1.xml 194 filenamesplit = filename.split("_") 195 if filenamesplit[0] not in include_list: 196 print("excluding schema: " + filename) 197 continue 198 199 with open(os.path.join(schema_path, filename), 'wb') as schema_out: 200 201 metadata_index.write( 202 " <edmx:Reference Uri=\"/redfish/v1/schema/" + 203 filename + 204 "\">\n") 205 206 content = zip_ref.read(zip_filepath) 207 content = content.replace(b'\r\n', b'\n') 208 xml_root = ET.fromstring(content) 209 edmx = "{http://docs.oasis-open.org/odata/ns/edmx}" 210 edm = "{http://docs.oasis-open.org/odata/ns/edm}" 211 for edmx_child in xml_root: 212 if edmx_child.tag == edmx + "DataServices": 213 for data_child in edmx_child: 214 if data_child.tag == edm + "Schema": 215 namespace = data_child.attrib["Namespace"] 216 if namespace.startswith("RedfishExtensions"): 217 metadata_index.write( 218 " " 219 "<edmx:Include Namespace=\"" + 220 namespace + 221 "\" Alias=\"Redfish\"/>\n" 222 ) 223 224 else: 225 metadata_index.write( 226 " " 227 "<edmx:Include Namespace=\"" 228 + namespace + "\"/>\n" 229 ) 230 schema_out.write(content) 231 metadata_index.write(" </edmx:Reference>\n") 232 233 metadata_index.write(" <edmx:DataServices>\n" 234 " <Schema " 235 "xmlns=\"http://docs.oasis-open.org/odata/ns/edm\" " 236 "Namespace=\"Service\">\n" 237 " <EntityContainer Name=\"Service\" " 238 "Extends=\"ServiceRoot.v1_0_0.ServiceContainer\"/>\n" 239 " </Schema>\n" 240 " </edmx:DataServices>\n" 241 ) 242 # TODO:Issue#32 There's a bug in the script that currently deletes this 243 # schema (because it's an OEM schema). Because it's the only six, and we 244 # don't update schemas very often, we just manually fix it. Need a 245 # permanent fix to the script. 246 metadata_index.write( 247 " <edmx:Reference Uri=\"/redfish/v1/schema/OemManager_v1.xml\">\n") 248 metadata_index.write(" <edmx:Include Namespace=\"OemManager\"/>\n") 249 metadata_index.write(" </edmx:Reference>\n") 250 251 metadata_index.write( 252 " <edmx:Reference Uri=\"" 253 "/redfish/v1/schema/OemComputerSystem_v1.xml\">\n") 254 metadata_index.write( 255 " <edmx:Include Namespace=\"OemComputerSystem\"/>\n") 256 metadata_index.write(" </edmx:Reference>\n") 257 258 metadata_index.write( 259 " <edmx:Reference Uri=\"" 260 "/redfish/v1/schema/OemVirtualMedia_v1.xml\">\n") 261 metadata_index.write( 262 " <edmx:Include Namespace=\"OemVirtualMedia\"/>\n") 263 metadata_index.write( 264 " <edmx:Include Namespace=\"OemVirtualMedia.v1_0_0\"/>\n") 265 metadata_index.write(" </edmx:Reference>\n") 266 267 metadata_index.write( 268 " <edmx:Reference Uri=\"" 269 "/redfish/v1/schema/OemAccountService_v1.xml\">\n") 270 metadata_index.write( 271 " <edmx:Include Namespace=\"OemAccountService\"/>\n") 272 metadata_index.write( 273 " <edmx:Include Namespace=\"OemAccountService.v1_0_0\"/>\n") 274 metadata_index.write(" </edmx:Reference>\n") 275 276 metadata_index.write( 277 " <edmx:Reference Uri=\"/redfish/v1/schema/OemSession_v1.xml\">\n") 278 metadata_index.write(" <edmx:Include Namespace=\"OemSession\"/>\n") 279 metadata_index.write( 280 " <edmx:Include Namespace=\"OemSession.v1_0_0\"/>\n") 281 metadata_index.write(" </edmx:Reference>\n") 282 283 metadata_index.write("</edmx:Edmx>\n") 284 285schema_files = {} 286for zip_filepath in zip_ref.namelist(): 287 if zip_filepath.startswith(os.path.join('json-schema/')): 288 filename = os.path.basename(zip_filepath) 289 filenamesplit = filename.split(".") 290 291 # exclude schemas again to save flash space 292 if filenamesplit[0] not in include_list: 293 continue 294 295 if len(filenamesplit) == 3: 296 thisSchemaVersion = schema_files.get(filenamesplit[0], None) 297 if thisSchemaVersion is None: 298 schema_files[filenamesplit[0]] = filenamesplit[1] 299 else: 300 # need to see if we're a newer version. 301 if list(map(int, filenamesplit[1][1:].split("_"))) > list(map( 302 int, thisSchemaVersion[1:].split("_"))): 303 schema_files[filenamesplit[0]] = filenamesplit[1] 304 305 306for schema, version in schema_files.items(): 307 basename = schema + "." + version + ".json" 308 zip_filepath = os.path.join("json-schema", basename) 309 schemadir = os.path.join(json_schema_path, schema) 310 os.makedirs(schemadir) 311 312 with open(os.path.join(schemadir, schema + ".json"), 'wb') as schema_file: 313 schema_file.write(zip_ref.read(zip_filepath).replace(b'\r\n', b'\n')) 314 315with open(os.path.join(cpp_path, "schemas.hpp"), 'w') as hpp_file: 316 hpp_file.write( 317 "#pragma once\n" 318 "{WARNING}\n" 319 "// clang-format off\n" 320 "\n" 321 "namespace redfish\n" 322 "{{\n" 323 " constexpr std::array schemas {{\n" 324 .format( 325 WARNING=WARNING)) 326 for schema_file in schema_files: 327 hpp_file.write(" \"{}\",\n".format(schema_file)) 328 329 hpp_file.write( 330 " };\n" 331 "}\n" 332 ) 333 334zip_ref.close() 335