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_2021.1" 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/OemComputerSystem_v1.xml\">\n") 217 metadata_index.write(" <edmx:Include Namespace=\"OemComputerSystem\"/>\n") 218 metadata_index.write(" </edmx:Reference>\n") 219 220 metadata_index.write( 221 " <edmx:Reference Uri=\"/redfish/v1/schema/OemVirtualMedia_v1.xml\">\n") 222 metadata_index.write(" <edmx:Include Namespace=\"OemVirtualMedia\"/>\n") 223 metadata_index.write(" <edmx:Include Namespace=\"OemVirtualMedia.v1_0_0\"/>\n") 224 metadata_index.write(" </edmx:Reference>\n") 225 226 metadata_index.write( 227 " <edmx:Reference Uri=\"/redfish/v1/schema/OemAccountService_v1.xml\">\n") 228 metadata_index.write(" <edmx:Include Namespace=\"OemAccountService\"/>\n") 229 metadata_index.write(" <edmx:Include Namespace=\"OemAccountService.v1_0_0\"/>\n") 230 metadata_index.write(" </edmx:Reference>\n") 231 232 metadata_index.write( 233 " <edmx:Reference Uri=\"/redfish/v1/schema/OemSession_v1.xml\">\n") 234 metadata_index.write(" <edmx:Include Namespace=\"OemSession\"/>\n") 235 metadata_index.write(" <edmx:Include Namespace=\"OemSession.v1_0_0\"/>\n") 236 metadata_index.write(" </edmx:Reference>\n") 237 238 metadata_index.write("</edmx:Edmx>\n") 239 240schema_files = {} 241for zip_filepath in zip_ref.namelist(): 242 if zip_filepath.startswith(os.path.join(VERSION, 'json-schema/')): 243 filename = os.path.basename(zip_filepath) 244 filenamesplit = filename.split(".") 245 246 # exclude schemas again to save flash space 247 if filenamesplit[0] not in include_list: 248 continue 249 250 if len(filenamesplit) == 3: 251 thisSchemaVersion = schema_files.get(filenamesplit[0], None) 252 if thisSchemaVersion is None: 253 schema_files[filenamesplit[0]] = filenamesplit[1] 254 else: 255 # need to see if we're a newer version. 256 if list(map(int, filenamesplit[1][1:].split("_"))) > list(map( 257 int, thisSchemaVersion[1:].split("_"))): 258 schema_files[filenamesplit[0]] = filenamesplit[1] 259 260 261for schema, version in schema_files.items(): 262 basename = schema + "." + version + ".json" 263 zip_filepath = os.path.join(VERSION, "json-schema", basename) 264 schemadir = os.path.join(json_schema_path, schema) 265 os.makedirs(schemadir) 266 location_json = OrderedDict() 267 location_json["Language"] = "en" 268 location_json["PublicationUri"] = ( 269 "http://redfish.dmtf.org/schemas/v1/" + schema + ".json") 270 location_json["Uri"] = ( 271 "/redfish/v1/JsonSchemas/" + schema + "/" + schema + ".json") 272 273 index_json = OrderedDict() 274 index_json["@odata.context"] = "/redfish/v1/$metadata#JsonSchemaFile.JsonSchemaFile" 275 index_json["@odata.id"] = "/redfish/v1/JsonSchemas/" + schema 276 index_json["@odata.type"] = "#JsonSchemaFile.v1_0_2.JsonSchemaFile" 277 index_json["Name"] = schema + " Schema File" 278 index_json["Schema"] = "#" + schema + "." + schema 279 index_json["Description"] = schema + " Schema File Location" 280 index_json["Id"] = schema 281 index_json["Languages"] = ["en"] 282 index_json["Languages@odata.count"] = 1 283 index_json["Location"] = [location_json] 284 index_json["Location@odata.count"] = 1 285 286 with open(os.path.join(schemadir, "index.json"), 'w') as schema_file: 287 json.dump(index_json, schema_file, indent=4) 288 with open(os.path.join(schemadir, schema + ".json"), 'wb') as schema_file: 289 schema_file.write(zip_ref.read(zip_filepath).replace(b'\r\n', b'\n')) 290 291with open(os.path.join(json_schema_path, "index.json"), 'w') as index_file: 292 members = [{"@odata.id": "/redfish/v1/JsonSchemas/" + schema} 293 for schema in schema_files] 294 295 members.sort(key=lambda x: x["@odata.id"]) 296 297 indexData = OrderedDict() 298 299 indexData["@odata.id"] = "/redfish/v1/JsonSchemas" 300 indexData["@odata.context"] = ("/redfish/v1/$metadata" 301 "#JsonSchemaFileCollection." 302 "JsonSchemaFileCollection") 303 indexData["@odata.type"] = ("#JsonSchemaFileCollection." 304 "JsonSchemaFileCollection") 305 indexData["Name"] = "JsonSchemaFile Collection" 306 indexData["Description"] = "Collection of JsonSchemaFiles" 307 indexData["Members@odata.count"] = len(schema_files) 308 indexData["Members"] = members 309 310 json.dump(indexData, index_file, indent=2) 311 312zip_ref.close() 313