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