xref: /openbmc/bmcweb/scripts/update_schemas.py (revision 81d523a7)
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
14WARNING = '''/****************************************************************
15 *                 READ THIS WARNING FIRST
16 * This is an auto-generated header which contains definitions
17 * for Redfish DMTF defined schemas.
18 * DO NOT modify this registry outside of running the
19 * update_schemas.py script.  The definitions contained within
20 * this file are owned by DMTF.  Any modifications to these files
21 * should be first pushed to the relevant registry in the DMTF
22 * github organization.
23 ***************************************************************/'''
24
25# To use a new schema, add to list and rerun tool
26include_list = [
27    'AccountService',
28    'ActionInfo',
29    'Assembly',
30    'AttributeRegistry',
31    'Bios',
32    'Cable',
33    'CableCollection',
34    'Certificate',
35    'CertificateCollection',
36    'CertificateLocations',
37    'CertificateService',
38    'Chassis',
39    'ChassisCollection',
40    'ComputerSystem',
41    'ComputerSystemCollection',
42    'Drive',
43    'DriveCollection',
44    'EthernetInterface',
45    'EthernetInterfaceCollection',
46    'Event',
47    'EventDestination',
48    'EventDestinationCollection',
49    'EventService',
50    'IPAddresses',
51    'JsonSchemaFile',
52    'JsonSchemaFileCollection',  # redfish/v1/JsonSchemas
53    'LogEntry',
54    'LogEntryCollection',
55    'LogService',
56    'LogServiceCollection',
57    'Manager',
58    'ManagerAccount',
59    'ManagerAccountCollection',
60    'ManagerCollection',
61    'ManagerDiagnosticData',
62    'ManagerNetworkProtocol',
63    'Memory',
64    'MemoryCollection',
65    'Message',
66    'MessageRegistry',
67    'MessageRegistryCollection',
68    'MessageRegistryFile',
69    'MessageRegistryFileCollection',
70    'MetricDefinition',
71    'MetricDefinitionCollection',
72    'MetricReport',
73    'MetricReportCollection',
74    'MetricReportDefinition',
75    'MetricReportDefinitionCollection',
76    'OperatingConfig',
77    'OperatingConfigCollection',
78    'PCIeDevice',
79    'PCIeDeviceCollection',
80    'PCIeFunction',
81    'PCIeFunctionCollection',
82    'PhysicalContext',
83    'PCIeSlots',
84    'Power',
85    'Privileges',  # Used in Role
86    'Processor',
87    'ProcessorCollection',
88    'RedfishError',
89    'RedfishExtensions',
90    'Redundancy',
91    'Resource',
92    'Role',
93    'RoleCollection',
94    'Sensor',
95    'SensorCollection',
96    'ServiceRoot',
97    'Session',
98    'SessionCollection',
99    'SessionService',
100    'Settings',
101    'SoftwareInventory',
102    'SoftwareInventoryCollection',
103    'Storage',
104    'StorageCollection',
105    'StorageController',
106    'StorageControllerCollection',
107    'Task',
108    'TaskCollection',
109    'TaskService',
110    'TelemetryService',
111    'Thermal',
112    'Triggers',
113    'TriggersCollection',
114    'UpdateService',
115    'VLanNetworkInterfaceCollection',
116    'VLanNetworkInterface',
117    'VirtualMedia',
118    'VirtualMediaCollection',
119    'odata',
120    'odata-v4',
121    'redfish-error',
122    'redfish-payload-annotations',
123    'redfish-schema',
124    'redfish-schema-v1',
125]
126
127SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
128
129proxies = {
130    'https': os.environ.get("https_proxy", None)
131}
132
133r = requests.get(
134    'https://www.dmtf.org/sites/default/files/standards/documents/' +
135    VERSION +
136    '.zip',
137    proxies=proxies)
138
139r.raise_for_status()
140
141
142static_path = os.path.realpath(os.path.join(SCRIPT_DIR, "..", "static",
143                                            "redfish", "v1"))
144
145
146cpp_path = os.path.realpath(os.path.join(SCRIPT_DIR, "..", "redfish-core",
147                                         "include"))
148
149
150schema_path = os.path.join(static_path, "schema")
151json_schema_path = os.path.join(static_path, "JsonSchemas")
152metadata_index_path = os.path.join(static_path, "$metadata", "index.xml")
153
154zipBytesIO = BytesIO(r.content)
155zip_ref = zipfile.ZipFile(zipBytesIO)
156
157# Remove the old files
158skip_prefixes = ('Oem')
159if os.path.exists(schema_path):
160    files = [os.path.join(schema_path, f) for f in os.listdir(schema_path)
161             if not f.startswith(skip_prefixes)]
162    for f in files:
163        os.remove(f)
164if os.path.exists(json_schema_path):
165    files = [os.path.join(json_schema_path, f) for f in
166             os.listdir(json_schema_path) if not f.startswith(skip_prefixes)]
167    for f in files:
168        if (os.path.isfile(f)):
169            os.remove(f)
170        else:
171            shutil.rmtree(f)
172os.remove(metadata_index_path)
173
174if not os.path.exists(schema_path):
175    os.makedirs(schema_path)
176if not os.path.exists(json_schema_path):
177    os.makedirs(json_schema_path)
178
179with open(metadata_index_path, 'w') as metadata_index:
180
181    metadata_index.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
182    metadata_index.write(
183        "<edmx:Edmx xmlns:edmx="
184        "\"http://docs.oasis-open.org/odata/ns/edmx\""
185        " Version=\"4.0\">\n")
186
187    for zip_filepath in zip_ref.namelist():
188        if zip_filepath.startswith('csdl/') and \
189            (zip_filepath != VERSION + "/csdl/") and \
190                (zip_filepath != "csdl/"):
191            filename = os.path.basename(zip_filepath)
192
193            # filename looks like Zone_v1.xml
194            filenamesplit = filename.split("_")
195            if filenamesplit[0] not in include_list:
196                print("excluding schema: " + filename)
197                continue
198
199            with open(os.path.join(schema_path, filename), 'wb') as schema_out:
200
201                metadata_index.write(
202                    "    <edmx:Reference Uri=\"/redfish/v1/schema/" +
203                    filename +
204                    "\">\n")
205
206                content = zip_ref.read(zip_filepath)
207                content = content.replace(b'\r\n', b'\n')
208                xml_root = ET.fromstring(content)
209                edmx = "{http://docs.oasis-open.org/odata/ns/edmx}"
210                edm = "{http://docs.oasis-open.org/odata/ns/edm}"
211                for edmx_child in xml_root:
212                    if edmx_child.tag == edmx + "DataServices":
213                        for data_child in edmx_child:
214                            if data_child.tag == edm + "Schema":
215                                namespace = data_child.attrib["Namespace"]
216                                if namespace.startswith("RedfishExtensions"):
217                                    metadata_index.write(
218                                        "        "
219                                        "<edmx:Include Namespace=\"" +
220                                        namespace +
221                                        "\"  Alias=\"Redfish\"/>\n"
222                                    )
223
224                                else:
225                                    metadata_index.write(
226                                        "        "
227                                        "<edmx:Include Namespace=\""
228                                        + namespace + "\"/>\n"
229                                    )
230                schema_out.write(content)
231                metadata_index.write("    </edmx:Reference>\n")
232
233    metadata_index.write("    <edmx:DataServices>\n"
234                         "        <Schema "
235                         "xmlns=\"http://docs.oasis-open.org/odata/ns/edm\" "
236                         "Namespace=\"Service\">\n"
237                         "            <EntityContainer Name=\"Service\" "
238                         "Extends=\"ServiceRoot.v1_0_0.ServiceContainer\"/>\n"
239                         "        </Schema>\n"
240                         "    </edmx:DataServices>\n"
241                         )
242    # TODO:Issue#32 There's a bug in the script that currently deletes this
243    # schema (because it's an OEM schema). Because it's the only six, and we
244    # don't update schemas very often, we just manually fix it. Need a
245    # permanent fix to the script.
246    metadata_index.write(
247        "    <edmx:Reference Uri=\"/redfish/v1/schema/OemManager_v1.xml\">\n")
248    metadata_index.write("        <edmx:Include Namespace=\"OemManager\"/>\n")
249    metadata_index.write("    </edmx:Reference>\n")
250
251    metadata_index.write(
252        "    <edmx:Reference Uri=\""
253        "/redfish/v1/schema/OemComputerSystem_v1.xml\">\n")
254    metadata_index.write(
255        "        <edmx:Include Namespace=\"OemComputerSystem\"/>\n")
256    metadata_index.write("    </edmx:Reference>\n")
257
258    metadata_index.write(
259        "    <edmx:Reference Uri=\""
260        "/redfish/v1/schema/OemVirtualMedia_v1.xml\">\n")
261    metadata_index.write(
262        "        <edmx:Include Namespace=\"OemVirtualMedia\"/>\n")
263    metadata_index.write(
264        "        <edmx:Include Namespace=\"OemVirtualMedia.v1_0_0\"/>\n")
265    metadata_index.write("    </edmx:Reference>\n")
266
267    metadata_index.write(
268        "    <edmx:Reference Uri=\""
269        "/redfish/v1/schema/OemAccountService_v1.xml\">\n")
270    metadata_index.write(
271        "        <edmx:Include Namespace=\"OemAccountService\"/>\n")
272    metadata_index.write(
273        "        <edmx:Include Namespace=\"OemAccountService.v1_0_0\"/>\n")
274    metadata_index.write("    </edmx:Reference>\n")
275
276    metadata_index.write(
277        "    <edmx:Reference Uri=\"/redfish/v1/schema/OemSession_v1.xml\">\n")
278    metadata_index.write("        <edmx:Include Namespace=\"OemSession\"/>\n")
279    metadata_index.write(
280        "        <edmx:Include Namespace=\"OemSession.v1_0_0\"/>\n")
281    metadata_index.write("    </edmx:Reference>\n")
282
283    metadata_index.write("</edmx:Edmx>\n")
284
285schema_files = {}
286for zip_filepath in zip_ref.namelist():
287    if zip_filepath.startswith(os.path.join('json-schema/')):
288        filename = os.path.basename(zip_filepath)
289        filenamesplit = filename.split(".")
290
291        # exclude schemas again to save flash space
292        if filenamesplit[0] not in include_list:
293            continue
294
295        if len(filenamesplit) == 3:
296            thisSchemaVersion = schema_files.get(filenamesplit[0], None)
297            if thisSchemaVersion is None:
298                schema_files[filenamesplit[0]] = filenamesplit[1]
299            else:
300                # need to see if we're a newer version.
301                if list(map(int, filenamesplit[1][1:].split("_"))) > list(map(
302                        int, thisSchemaVersion[1:].split("_"))):
303                    schema_files[filenamesplit[0]] = filenamesplit[1]
304
305
306for schema, version in schema_files.items():
307    basename = schema + "." + version + ".json"
308    zip_filepath = os.path.join("json-schema", basename)
309    schemadir = os.path.join(json_schema_path, schema)
310    os.makedirs(schemadir)
311
312    with open(os.path.join(schemadir, schema + ".json"), 'wb') as schema_file:
313        schema_file.write(zip_ref.read(zip_filepath).replace(b'\r\n', b'\n'))
314
315with open(os.path.join(cpp_path, "schemas.hpp"), 'w') as hpp_file:
316    hpp_file.write(
317        "#pragma once\n"
318        "{WARNING}\n"
319        "// clang-format off\n"
320        "\n"
321        "namespace redfish\n"
322        "{{\n"
323        "    constexpr std::array schemas {{\n"
324        .format(
325            WARNING=WARNING))
326    for schema_file in schema_files:
327        hpp_file.write("        \"{}\",\n".format(schema_file))
328
329    hpp_file.write(
330        "    };\n"
331        "}\n"
332    )
333
334zip_ref.close()
335