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