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