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