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