xref: /openbmc/bmcweb/scripts/update_schemas.py (revision 735ef6d8)
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    'Privileges',  # Used in Role
73    'Processor',
74    'ProcessorCollection',
75    'RedfishError',
76    'RedfishExtensions',
77    'Redundancy',
78    'Resource',
79    'Role',
80    'RoleCollection',
81    'Sensor',
82    'SensorCollection',
83    'ServiceRoot',
84    'Session',
85    'SessionCollection',
86    'SessionService',
87    'Settings',
88    'SoftwareInventory',
89    'SoftwareInventoryCollection',
90    'Storage',
91    'StorageCollection',
92    'StorageController',
93    'StorageControllerCollection',
94    'Task',
95    'TaskCollection',
96    'TaskService',
97    'TelemetryService',
98    'Thermal',
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
137if os.path.exists(schema_path):
138    files = glob.glob(os.path.join(schema_path, '[!Oem]*'))
139    for f in files:
140        os.remove(f)
141if os.path.exists(json_schema_path):
142    files = glob.glob(os.path.join(json_schema_path, '[!Oem]*'))
143    for f in files:
144        if (os.path.isfile(f)):
145            os.remove(f)
146        else:
147            shutil.rmtree(f)
148os.remove(metadata_index_path)
149
150if not os.path.exists(schema_path):
151    os.makedirs(schema_path)
152if not os.path.exists(json_schema_path):
153    os.makedirs(json_schema_path)
154
155with open(metadata_index_path, 'w') as metadata_index:
156
157    metadata_index.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
158    metadata_index.write(
159        "<edmx:Edmx xmlns:edmx="
160        "\"http://docs.oasis-open.org/odata/ns/edmx\""
161        " Version=\"4.0\">\n")
162
163    for zip_filepath in zip_ref.namelist():
164        if zip_filepath.startswith(VERSION + '/csdl/') and \
165            (zip_filepath != VERSION + "/csdl/") and \
166                (zip_filepath != VERSION + "/csdl/"):
167            filename = os.path.basename(zip_filepath)
168
169            # filename looks like Zone_v1.xml
170            filenamesplit = filename.split("_")
171            if filenamesplit[0] not in include_list:
172                print("excluding schema: " + filename)
173                continue
174
175            with open(os.path.join(schema_path, filename), 'wb') as schema_out:
176
177                metadata_index.write(
178                    "    <edmx:Reference Uri=\"/redfish/v1/schema/" +
179                    filename +
180                    "\">\n")
181
182                content = zip_ref.read(zip_filepath)
183                content = content.replace(b'\r\n', b'\n')
184                xml_root = ET.fromstring(content)
185                edmx = "{http://docs.oasis-open.org/odata/ns/edmx}"
186                for edmx_child in xml_root:
187                    if edmx_child.tag == edmx + "DataServices":
188                        for data_child in edmx_child:
189                            if data_child.tag == edmx + "Schema":
190                                namespace = data_child.attrib["Namespace"]
191                                if namespace.startswith("RedfishExtensions"):
192                                    metadata_index.write(
193                                        "        "
194                                        "<edmx:Include Namespace=\"" +
195                                        namespace +
196                                        "\"  Alias=\"Redfish\"/>\n"
197                                    )
198
199                                else:
200                                    metadata_index.write(
201                                        "        "
202                                        "<edmx:Include Namespace=\""
203                                        + namespace + "\"/>\n"
204                                    )
205                schema_out.write(content)
206                metadata_index.write("    </edmx:Reference>\n")
207
208    metadata_index.write("    <edmx:DataServices>\n"
209                         "        <Schema "
210                         "xmlns=\"http://docs.oasis-open.org/odata/ns/edm\" "
211                         "Namespace=\"Service\">\n"
212                         "            <EntityContainer Name=\"Service\" "
213                         "Extends=\"ServiceRoot.v1_0_0.ServiceContainer\"/>\n"
214                         "        </Schema>\n"
215                         "    </edmx:DataServices>\n"
216                         )
217    # TODO:Issue#32 There's a bug in the script that currently deletes this
218    # schema (because it's an OEM schema). Because it's the only six, and we
219    # don't update schemas very often, we just manually fix it. Need a
220    # permanent fix to the script.
221    metadata_index.write(
222        "    <edmx:Reference Uri=\"/redfish/v1/schema/OemManager_v1.xml\">\n")
223    metadata_index.write("        <edmx:Include Namespace=\"OemManager\"/>\n")
224    metadata_index.write("    </edmx:Reference>\n")
225
226    metadata_index.write(
227        "    <edmx:Reference Uri=\""
228        "/redfish/v1/schema/OemComputerSystem_v1.xml\">\n")
229    metadata_index.write(
230        "        <edmx:Include Namespace=\"OemComputerSystem\"/>\n")
231    metadata_index.write("    </edmx:Reference>\n")
232
233    metadata_index.write(
234        "    <edmx:Reference Uri=\""
235        "/redfish/v1/schema/OemVirtualMedia_v1.xml\">\n")
236    metadata_index.write(
237        "        <edmx:Include Namespace=\"OemVirtualMedia\"/>\n")
238    metadata_index.write(
239        "        <edmx:Include Namespace=\"OemVirtualMedia.v1_0_0\"/>\n")
240    metadata_index.write("    </edmx:Reference>\n")
241
242    metadata_index.write(
243        "    <edmx:Reference Uri=\""
244        "/redfish/v1/schema/OemAccountService_v1.xml\">\n")
245    metadata_index.write(
246        "        <edmx:Include Namespace=\"OemAccountService\"/>\n")
247    metadata_index.write(
248        "        <edmx:Include Namespace=\"OemAccountService.v1_0_0\"/>\n")
249    metadata_index.write("    </edmx:Reference>\n")
250
251    metadata_index.write(
252        "    <edmx:Reference Uri=\"/redfish/v1/schema/OemSession_v1.xml\">\n")
253    metadata_index.write("        <edmx:Include Namespace=\"OemSession\"/>\n")
254    metadata_index.write(
255        "        <edmx:Include Namespace=\"OemSession.v1_0_0\"/>\n")
256    metadata_index.write("    </edmx:Reference>\n")
257
258    metadata_index.write("</edmx:Edmx>\n")
259
260schema_files = {}
261for zip_filepath in zip_ref.namelist():
262    if zip_filepath.startswith(os.path.join(VERSION, 'json-schema/')):
263        filename = os.path.basename(zip_filepath)
264        filenamesplit = filename.split(".")
265
266        # exclude schemas again to save flash space
267        if filenamesplit[0] not in include_list:
268            continue
269
270        if len(filenamesplit) == 3:
271            thisSchemaVersion = schema_files.get(filenamesplit[0], None)
272            if thisSchemaVersion is None:
273                schema_files[filenamesplit[0]] = filenamesplit[1]
274            else:
275                # need to see if we're a newer version.
276                if list(map(int, filenamesplit[1][1:].split("_"))) > list(map(
277                        int, thisSchemaVersion[1:].split("_"))):
278                    schema_files[filenamesplit[0]] = filenamesplit[1]
279
280
281for schema, version in schema_files.items():
282    basename = schema + "." + version + ".json"
283    zip_filepath = os.path.join(VERSION, "json-schema", basename)
284    schemadir = os.path.join(json_schema_path, schema)
285    os.makedirs(schemadir)
286    location_json = OrderedDict()
287    location_json["Language"] = "en"
288    location_json["PublicationUri"] = (
289        "http://redfish.dmtf.org/schemas/v1/" + schema + ".json")
290    location_json["Uri"] = (
291        "/redfish/v1/JsonSchemas/" + schema + "/" + schema + ".json")
292
293    index_json = OrderedDict()
294    index_json["@odata.context"] = \
295        "/redfish/v1/$metadata#JsonSchemaFile.JsonSchemaFile"
296    index_json["@odata.id"] = "/redfish/v1/JsonSchemas/" + schema
297    index_json["@odata.type"] = "#JsonSchemaFile.v1_0_2.JsonSchemaFile"
298    index_json["Name"] = schema + " Schema File"
299    index_json["Schema"] = "#" + schema + "." + schema
300    index_json["Description"] = schema + " Schema File Location"
301    index_json["Id"] = schema
302    index_json["Languages"] = ["en"]
303    index_json["Languages@odata.count"] = 1
304    index_json["Location"] = [location_json]
305    index_json["Location@odata.count"] = 1
306
307    with open(os.path.join(schemadir, "index.json"), 'w') as schema_file:
308        json.dump(index_json, schema_file, indent=4)
309    with open(os.path.join(schemadir, schema + ".json"), 'wb') as schema_file:
310        schema_file.write(zip_ref.read(zip_filepath).replace(b'\r\n', b'\n'))
311
312with open(os.path.join(json_schema_path, "index.json"), 'w') as index_file:
313    members = [{"@odata.id": "/redfish/v1/JsonSchemas/" + schema}
314               for schema in schema_files]
315
316    members.sort(key=lambda x: x["@odata.id"])
317
318    indexData = OrderedDict()
319
320    indexData["@odata.id"] = "/redfish/v1/JsonSchemas"
321    indexData["@odata.context"] = ("/redfish/v1/$metadata"
322                                   "#JsonSchemaFileCollection."
323                                   "JsonSchemaFileCollection")
324    indexData["@odata.type"] = ("#JsonSchemaFileCollection."
325                                "JsonSchemaFileCollection")
326    indexData["Name"] = "JsonSchemaFile Collection"
327    indexData["Description"] = "Collection of JsonSchemaFiles"
328    indexData["Members@odata.count"] = len(schema_files)
329    indexData["Members"] = members
330
331    json.dump(indexData, index_file, indent=2)
332
333zip_ref.close()
334