1#!/usr/bin/env python3 2import os 3import shutil 4import xml.etree.ElementTree as ET 5import zipfile 6from collections import OrderedDict, defaultdict 7from io import BytesIO 8 9import generate_schema_enums 10import requests 11from generate_schema_collections import generate_top_collections 12 13VERSION = "DSP8010_2023.3" 14 15WARNING = """/**************************************************************** 16 * READ THIS WARNING FIRST 17 * This is an auto-generated header which contains definitions 18 * for Redfish DMTF defined schemas. 19 * DO NOT modify this registry outside of running the 20 * update_schemas.py script. The definitions contained within 21 * this file are owned by DMTF. Any modifications to these files 22 * should be first pushed to the relevant registry in the DMTF 23 * github organization. 24 ***************************************************************/""" 25 26SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) 27 28proxies = {"https": os.environ.get("https_proxy", None)} 29 30r = requests.get( 31 "https://www.dmtf.org/sites/default/files/standards/documents/" 32 + VERSION 33 + ".zip", 34 proxies=proxies, 35) 36 37r.raise_for_status() 38 39 40static_path = os.path.realpath( 41 os.path.join(SCRIPT_DIR, "..", "static", "redfish", "v1") 42) 43 44 45cpp_path = os.path.realpath( 46 os.path.join(SCRIPT_DIR, "..", "redfish-core", "include") 47) 48 49 50schema_path = os.path.join( 51 SCRIPT_DIR, "..", "redfish-core", "schema", "dmtf", "csdl" 52) 53json_schema_path = os.path.join( 54 SCRIPT_DIR, "..", "redfish-core", "schema", "dmtf", "json-schema" 55) 56metadata_index_path = os.path.join(static_path, "$metadata", "index.xml") 57 58zipBytesIO = BytesIO(r.content) 59zip_ref = zipfile.ZipFile(zipBytesIO) 60 61 62class SchemaVersion: 63 """ 64 A Python class for sorting Redfish schema versions. Allows sorting Redfish 65 versions in the way humans expect, by comparing version strings as lists 66 (ie 0_2_0 comes before 0_10_0) in the way humans expect. It does case 67 insensitive schema name comparisons 68 """ 69 70 def __init__(self, key): 71 key = str.casefold(key) 72 73 split_tup = key.split(".") 74 self.version_pieces = [split_tup[0]] 75 if len(split_tup) < 2: 76 return 77 version = split_tup[1] 78 79 if version.startswith("v"): 80 version = version[1:] 81 if any(char.isdigit() for char in version): 82 self.version_pieces.extend([int(x) for x in version.split("_")]) 83 84 def __lt__(self, other): 85 return self.version_pieces < other.version_pieces 86 87 88# Remove the old files 89skip_prefixes = ["Oem", "OpenBMC"] 90if os.path.exists(schema_path): 91 files = [ 92 os.path.join(schema_path, f) 93 for f in os.listdir(schema_path) 94 if not any([f.startswith(prefix) for prefix in skip_prefixes]) 95 ] 96 for f in files: 97 os.remove(f) 98if os.path.exists(json_schema_path): 99 files = [ 100 os.path.join(json_schema_path, f) 101 for f in os.listdir(json_schema_path) 102 if not any([f.startswith(prefix) for prefix in skip_prefixes]) 103 ] 104 for f in files: 105 if os.path.isfile(f): 106 os.remove(f) 107 else: 108 shutil.rmtree(f) 109try: 110 os.remove(metadata_index_path) 111except FileNotFoundError: 112 pass 113 114if not os.path.exists(schema_path): 115 os.makedirs(schema_path) 116if not os.path.exists(json_schema_path): 117 os.makedirs(json_schema_path) 118 119csdl_filenames = [] 120json_schema_files = defaultdict(list) 121 122for zip_file in zip_ref.infolist(): 123 if zip_file.is_dir(): 124 continue 125 if zip_file.filename.startswith("csdl/"): 126 csdl_filenames.append(os.path.basename(zip_file.filename)) 127 elif zip_file.filename.startswith("json-schema/"): 128 filename = os.path.basename(zip_file.filename) 129 filenamesplit = filename.split(".") 130 json_schema_files[filenamesplit[0]].append(filename) 131 elif zip_file.filename.startswith("openapi/"): 132 pass 133 elif zip_file.filename.startswith("dictionaries/"): 134 pass 135 136# sort the json files by version 137for key, value in json_schema_files.items(): 138 value.sort(key=SchemaVersion, reverse=True) 139 140# Create a dictionary ordered by schema name 141json_schema_files = OrderedDict( 142 sorted(json_schema_files.items(), key=lambda x: SchemaVersion(x[0])) 143) 144for csdl_file in csdl_filenames: 145 with open(os.path.join(schema_path, csdl_file), "wb") as schema_out: 146 content = zip_ref.read(os.path.join("csdl", csdl_file)) 147 content = content.replace(b"\r\n", b"\n") 148 schema_out.write(content) 149 150with open(metadata_index_path, "w") as metadata_index: 151 metadata_index.write('<?xml version="1.0" encoding="UTF-8"?>\n') 152 metadata_index.write( 153 "<edmx:Edmx xmlns:edmx=" 154 '"http://docs.oasis-open.org/odata/ns/edmx"' 155 ' Version="4.0">\n' 156 ) 157 158 schema_static_dir = os.path.join( 159 SCRIPT_DIR, "..", "static", "redfish", "v1", "schema" 160 ) 161 for filename in sorted(os.listdir(schema_static_dir), key=SchemaVersion): 162 if not filename.endswith(".xml"): 163 continue 164 165 metadata_index.write( 166 ' <edmx:Reference Uri="/redfish/v1/schema/' + filename + '">\n' 167 ) 168 169 xml_root = ET.parse( 170 os.path.join(schema_static_dir, filename) 171 ).getroot() 172 edmx = "{http://docs.oasis-open.org/odata/ns/edmx}" 173 edm = "{http://docs.oasis-open.org/odata/ns/edm}" 174 for edmx_child in xml_root: 175 if edmx_child.tag == edmx + "DataServices": 176 for data_child in edmx_child: 177 if data_child.tag == edm + "Schema": 178 namespace = data_child.attrib["Namespace"] 179 if namespace.startswith("RedfishExtensions"): 180 metadata_index.write( 181 ' <edmx:Include Namespace="' 182 + namespace 183 + '" Alias="Redfish"/>\n' 184 ) 185 186 else: 187 metadata_index.write( 188 ' <edmx:Include Namespace="' 189 + namespace 190 + '"/>\n' 191 ) 192 metadata_index.write(" </edmx:Reference>\n") 193 194 metadata_index.write( 195 " <edmx:DataServices>\n" 196 " <Schema " 197 'xmlns="http://docs.oasis-open.org/odata/ns/edm" ' 198 'Namespace="Service">\n' 199 ' <EntityContainer Name="Service" ' 200 'Extends="ServiceRoot.v1_0_0.ServiceContainer"/>\n' 201 " </Schema>\n" 202 " </edmx:DataServices>\n" 203 ) 204 metadata_index.write("</edmx:Edmx>\n") 205 206 207for schema, version in json_schema_files.items(): 208 zip_filepath = os.path.join("json-schema", version[0]) 209 210 with open(os.path.join(json_schema_path, version[0]), "wb") as schema_file: 211 schema_file.write(zip_ref.read(zip_filepath).replace(b"\r\n", b"\n")) 212 213with open(os.path.join(cpp_path, "schemas.hpp"), "w") as hpp_file: 214 schemas = [] 215 for root, dirs, files in os.walk( 216 os.path.join(SCRIPT_DIR, "..", "static", "redfish", "v1", "schema") 217 ): 218 for csdl_file in sorted(files, key=SchemaVersion): 219 if csdl_file.endswith(".xml"): 220 schemas.append(csdl_file.replace("_v1.xml", "")) 221 hpp_file.write( 222 "#pragma once\n" 223 "{WARNING}\n" 224 "// clang-format off\n" 225 "#include <array>\n" 226 "#include <string_view>\n" 227 "\n" 228 "namespace redfish\n" 229 "{{\n" 230 " constexpr std::array<std::string_view,{SIZE}> schemas {{\n".format( 231 WARNING=WARNING, 232 SIZE=len(schemas), 233 ) 234 ) 235 for schema in schemas: 236 hpp_file.write(' "{}",\n'.format(schema)) 237 238 hpp_file.write(" };\n}\n") 239 240zip_ref.close() 241 242generate_schema_enums.main() 243generate_top_collections() 244