xref: /openbmc/bmcweb/scripts/update_schemas.py (revision f2a8e57e)
1#!/usr/bin/env python3
2import os
3import shutil
4import zipfile
5from collections import OrderedDict, defaultdict
6from io import BytesIO
7
8import generate_schema_enums
9import requests
10from generate_schema_collections import generate_top_collections
11
12VERSION = "DSP8010_2024.1"
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
25SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
26
27proxies = {"https": os.environ.get("https_proxy", None)}
28
29r = requests.get(
30    "https://www.dmtf.org/sites/default/files/standards/documents/"
31    + VERSION
32    + ".zip",
33    proxies=proxies,
34)
35
36r.raise_for_status()
37
38
39static_path = os.path.realpath(
40    os.path.join(SCRIPT_DIR, "..", "static", "redfish", "v1")
41)
42
43
44cpp_path = os.path.realpath(
45    os.path.join(SCRIPT_DIR, "..", "redfish-core", "include")
46)
47
48
49schema_path = os.path.join(
50    SCRIPT_DIR, "..", "redfish-core", "schema", "dmtf", "csdl"
51)
52json_schema_path = os.path.join(
53    SCRIPT_DIR, "..", "redfish-core", "schema", "dmtf", "json-schema"
54)
55
56zipBytesIO = BytesIO(r.content)
57zip_ref = zipfile.ZipFile(zipBytesIO)
58
59
60class SchemaVersion:
61    """
62    A Python class for sorting Redfish schema versions.  Allows sorting Redfish
63    versions in the way humans expect, by comparing version strings as lists
64    (ie 0_2_0 comes before 0_10_0) in the way humans expect.  It does case
65    insensitive schema name comparisons
66    """
67
68    def __init__(self, key):
69        key = str.casefold(key)
70
71        split_tup = key.split(".")
72        self.version_pieces = [split_tup[0]]
73        if len(split_tup) < 2:
74            return
75        version = split_tup[1]
76
77        if version.startswith("v"):
78            version = version[1:]
79        if any(char.isdigit() for char in version):
80            self.version_pieces.extend([int(x) for x in version.split("_")])
81
82    def __lt__(self, other):
83        return self.version_pieces < other.version_pieces
84
85
86# Remove the old files
87skip_prefixes = ["Oem", "OpenBMC"]
88if os.path.exists(schema_path):
89    files = [
90        os.path.join(schema_path, f)
91        for f in os.listdir(schema_path)
92        if not any([f.startswith(prefix) for prefix in skip_prefixes])
93    ]
94    for f in files:
95        os.remove(f)
96if os.path.exists(json_schema_path):
97    files = [
98        os.path.join(json_schema_path, f)
99        for f in os.listdir(json_schema_path)
100        if not any([f.startswith(prefix) for prefix in skip_prefixes])
101    ]
102    for f in files:
103        if os.path.isfile(f):
104            os.remove(f)
105        else:
106            shutil.rmtree(f)
107
108if not os.path.exists(schema_path):
109    os.makedirs(schema_path)
110if not os.path.exists(json_schema_path):
111    os.makedirs(json_schema_path)
112
113csdl_filenames = []
114json_schema_files = defaultdict(list)
115
116for zip_file in zip_ref.infolist():
117    if zip_file.is_dir():
118        continue
119    if zip_file.filename.startswith("csdl/"):
120        csdl_filenames.append(os.path.basename(zip_file.filename))
121    elif zip_file.filename.startswith("json-schema/"):
122        filename = os.path.basename(zip_file.filename)
123        filenamesplit = filename.split(".")
124        json_schema_files[filenamesplit[0]].append(filename)
125    elif zip_file.filename.startswith("openapi/"):
126        pass
127    elif zip_file.filename.startswith("dictionaries/"):
128        pass
129
130# sort the json files by version
131for key, value in json_schema_files.items():
132    value.sort(key=SchemaVersion, reverse=True)
133
134# Create a dictionary ordered by schema name
135json_schema_files = OrderedDict(
136    sorted(json_schema_files.items(), key=lambda x: SchemaVersion(x[0]))
137)
138for csdl_file in csdl_filenames:
139    with open(os.path.join(schema_path, csdl_file), "wb") as schema_out:
140        content = zip_ref.read(os.path.join("csdl", csdl_file))
141        content = content.replace(b"\r\n", b"\n")
142        schema_out.write(content)
143
144for schema, version in json_schema_files.items():
145    zip_filepath = os.path.join("json-schema", version[0])
146
147    with open(os.path.join(json_schema_path, version[0]), "wb") as schema_file:
148        schema_file.write(zip_ref.read(zip_filepath).replace(b"\r\n", b"\n"))
149
150with open(os.path.join(cpp_path, "schemas.hpp"), "w") as hpp_file:
151    schemas = []
152    for root, dirs, files in os.walk(
153        os.path.join(SCRIPT_DIR, "..", "static", "redfish", "v1", "schema")
154    ):
155        for csdl_file in sorted(files, key=SchemaVersion):
156            if csdl_file.endswith(".xml"):
157                schemas.append(csdl_file.replace("_v1.xml", ""))
158    hpp_file.write(
159        "#pragma once\n"
160        "{WARNING}\n"
161        "// clang-format off\n"
162        "#include <array>\n"
163        "#include <string_view>\n"
164        "\n"
165        "namespace redfish\n"
166        "{{\n"
167        "    constexpr std::array<std::string_view,{SIZE}> schemas {{\n".format(
168            WARNING=WARNING,
169            SIZE=len(schemas),
170        )
171    )
172    for schema in schemas:
173        hpp_file.write('        "{}",\n'.format(schema))
174
175    hpp_file.write("    };\n}\n")
176
177zip_ref.close()
178
179generate_schema_enums.main()
180generate_top_collections()
181