xref: /openbmc/bmcweb/scripts/update_schemas.py (revision d8ef9915)
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