xref: /openbmc/bmcweb/scripts/update_schemas.py (revision 30aacdd8)
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    'ThermalSubsystem',
113    'Triggers',
114    'TriggersCollection',
115    'UpdateService',
116    'VLanNetworkInterfaceCollection',
117    'VLanNetworkInterface',
118    'VirtualMedia',
119    'VirtualMediaCollection',
120    'odata',
121    'odata-v4',
122    'redfish-error',
123    'redfish-payload-annotations',
124    'redfish-schema',
125    'redfish-schema-v1',
126]
127
128SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
129
130proxies = {
131    'https': os.environ.get("https_proxy", None)
132}
133
134r = requests.get(
135    'https://www.dmtf.org/sites/default/files/standards/documents/' +
136    VERSION +
137    '.zip',
138    proxies=proxies)
139
140r.raise_for_status()
141
142
143static_path = os.path.realpath(os.path.join(SCRIPT_DIR, "..", "static",
144                                            "redfish", "v1"))
145
146
147cpp_path = os.path.realpath(os.path.join(SCRIPT_DIR, "..", "redfish-core",
148                                         "include"))
149
150
151schema_path = os.path.join(static_path, "schema")
152json_schema_path = os.path.join(static_path, "JsonSchemas")
153metadata_index_path = os.path.join(static_path, "$metadata", "index.xml")
154
155zipBytesIO = BytesIO(r.content)
156zip_ref = zipfile.ZipFile(zipBytesIO)
157
158# Remove the old files
159skip_prefixes = ('Oem')
160if os.path.exists(schema_path):
161    files = [os.path.join(schema_path, f) for f in os.listdir(schema_path)
162             if not f.startswith(skip_prefixes)]
163    for f in files:
164        os.remove(f)
165if os.path.exists(json_schema_path):
166    files = [os.path.join(json_schema_path, f) for f in
167             os.listdir(json_schema_path) if not f.startswith(skip_prefixes)]
168    for f in files:
169        if (os.path.isfile(f)):
170            os.remove(f)
171        else:
172            shutil.rmtree(f)
173os.remove(metadata_index_path)
174
175if not os.path.exists(schema_path):
176    os.makedirs(schema_path)
177if not os.path.exists(json_schema_path):
178    os.makedirs(json_schema_path)
179
180with open(metadata_index_path, 'w') as metadata_index:
181
182    metadata_index.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
183    metadata_index.write(
184        "<edmx:Edmx xmlns:edmx="
185        "\"http://docs.oasis-open.org/odata/ns/edmx\""
186        " Version=\"4.0\">\n")
187
188    for zip_filepath in zip_ref.namelist():
189        if zip_filepath.startswith('csdl/') and \
190            (zip_filepath != VERSION + "/csdl/") and \
191                (zip_filepath != "csdl/"):
192            filename = os.path.basename(zip_filepath)
193
194            # filename looks like Zone_v1.xml
195            filenamesplit = filename.split("_")
196            if filenamesplit[0] not in include_list:
197                print("excluding schema: " + filename)
198                continue
199
200            with open(os.path.join(schema_path, filename), 'wb') as schema_out:
201
202                metadata_index.write(
203                    "    <edmx:Reference Uri=\"/redfish/v1/schema/" +
204                    filename +
205                    "\">\n")
206
207                content = zip_ref.read(zip_filepath)
208                content = content.replace(b'\r\n', b'\n')
209                xml_root = ET.fromstring(content)
210                edmx = "{http://docs.oasis-open.org/odata/ns/edmx}"
211                edm = "{http://docs.oasis-open.org/odata/ns/edm}"
212                for edmx_child in xml_root:
213                    if edmx_child.tag == edmx + "DataServices":
214                        for data_child in edmx_child:
215                            if data_child.tag == edm + "Schema":
216                                namespace = data_child.attrib["Namespace"]
217                                if namespace.startswith("RedfishExtensions"):
218                                    metadata_index.write(
219                                        "        "
220                                        "<edmx:Include Namespace=\"" +
221                                        namespace +
222                                        "\"  Alias=\"Redfish\"/>\n"
223                                    )
224
225                                else:
226                                    metadata_index.write(
227                                        "        "
228                                        "<edmx:Include Namespace=\""
229                                        + namespace + "\"/>\n"
230                                    )
231                schema_out.write(content)
232                metadata_index.write("    </edmx:Reference>\n")
233
234    metadata_index.write("    <edmx:DataServices>\n"
235                         "        <Schema "
236                         "xmlns=\"http://docs.oasis-open.org/odata/ns/edm\" "
237                         "Namespace=\"Service\">\n"
238                         "            <EntityContainer Name=\"Service\" "
239                         "Extends=\"ServiceRoot.v1_0_0.ServiceContainer\"/>\n"
240                         "        </Schema>\n"
241                         "    </edmx:DataServices>\n"
242                         )
243    # TODO:Issue#32 There's a bug in the script that currently deletes this
244    # schema (because it's an OEM schema). Because it's the only six, and we
245    # don't update schemas very often, we just manually fix it. Need a
246    # permanent fix to the script.
247    metadata_index.write(
248        "    <edmx:Reference Uri=\"/redfish/v1/schema/OemManager_v1.xml\">\n")
249    metadata_index.write("        <edmx:Include Namespace=\"OemManager\"/>\n")
250    metadata_index.write("    </edmx:Reference>\n")
251
252    metadata_index.write(
253        "    <edmx:Reference Uri=\""
254        "/redfish/v1/schema/OemComputerSystem_v1.xml\">\n")
255    metadata_index.write(
256        "        <edmx:Include Namespace=\"OemComputerSystem\"/>\n")
257    metadata_index.write("    </edmx:Reference>\n")
258
259    metadata_index.write(
260        "    <edmx:Reference Uri=\""
261        "/redfish/v1/schema/OemVirtualMedia_v1.xml\">\n")
262    metadata_index.write(
263        "        <edmx:Include Namespace=\"OemVirtualMedia\"/>\n")
264    metadata_index.write(
265        "        <edmx:Include Namespace=\"OemVirtualMedia.v1_0_0\"/>\n")
266    metadata_index.write("    </edmx:Reference>\n")
267
268    metadata_index.write(
269        "    <edmx:Reference Uri=\""
270        "/redfish/v1/schema/OemAccountService_v1.xml\">\n")
271    metadata_index.write(
272        "        <edmx:Include Namespace=\"OemAccountService\"/>\n")
273    metadata_index.write(
274        "        <edmx:Include Namespace=\"OemAccountService.v1_0_0\"/>\n")
275    metadata_index.write("    </edmx:Reference>\n")
276
277    metadata_index.write(
278        "    <edmx:Reference Uri=\"/redfish/v1/schema/OemSession_v1.xml\">\n")
279    metadata_index.write("        <edmx:Include Namespace=\"OemSession\"/>\n")
280    metadata_index.write(
281        "        <edmx:Include Namespace=\"OemSession.v1_0_0\"/>\n")
282    metadata_index.write("    </edmx:Reference>\n")
283
284    metadata_index.write("</edmx:Edmx>\n")
285
286schema_files = {}
287for zip_filepath in zip_ref.namelist():
288    if zip_filepath.startswith(os.path.join('json-schema/')):
289        filename = os.path.basename(zip_filepath)
290        filenamesplit = filename.split(".")
291
292        # exclude schemas again to save flash space
293        if filenamesplit[0] not in include_list:
294            continue
295
296        if len(filenamesplit) == 3:
297            thisSchemaVersion = schema_files.get(filenamesplit[0], None)
298            if thisSchemaVersion is None:
299                schema_files[filenamesplit[0]] = filenamesplit[1]
300            else:
301                # need to see if we're a newer version.
302                if list(map(int, filenamesplit[1][1:].split("_"))) > list(map(
303                        int, thisSchemaVersion[1:].split("_"))):
304                    schema_files[filenamesplit[0]] = filenamesplit[1]
305
306
307for schema, version in schema_files.items():
308    basename = schema + "." + version + ".json"
309    zip_filepath = os.path.join("json-schema", basename)
310    schemadir = os.path.join(json_schema_path, schema)
311    os.makedirs(schemadir)
312
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(cpp_path, "schemas.hpp"), 'w') as hpp_file:
317    hpp_file.write(
318        "#pragma once\n"
319        "{WARNING}\n"
320        "// clang-format off\n"
321        "\n"
322        "namespace redfish\n"
323        "{{\n"
324        "    constexpr std::array schemas {{\n"
325        .format(
326            WARNING=WARNING))
327    for schema_file in schema_files:
328        hpp_file.write("        \"{}\",\n".format(schema_file))
329
330    hpp_file.write(
331        "    };\n"
332        "}\n"
333    )
334
335zip_ref.close()
336