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