From 86166fcf83d1dd96742c33d8e30f7ce3161d3ae5 Mon Sep 17 00:00:00 2001 From: Sergey Gulyaev Date: Wed, 22 Feb 2023 18:05:31 +0300 Subject: [PATCH] redfish-gen: Create RefishBase class with common methods Create common Redfish class with common functionality to share same Redfish related functionaly in the 'redfish-gen' tool. End-User-Impact: None: (Code optimization without change functionality) Signed-off-by: Sergey Gulyaev --- tools/redfish-gen/redfish_gen/redfish_base.py | 107 +++++++++++++ tools/redfish-gen/redfish_gen/redfish_node.py | 46 ++---- .../redfish-gen/redfish_gen/redfish_schema.py | 144 +++++------------- 3 files changed, 158 insertions(+), 139 deletions(-) create mode 100644 tools/redfish-gen/redfish_gen/redfish_base.py diff --git a/tools/redfish-gen/redfish_gen/redfish_base.py b/tools/redfish-gen/redfish_gen/redfish_base.py new file mode 100644 index 00000000..74b936bb --- /dev/null +++ b/tools/redfish-gen/redfish_gen/redfish_base.py @@ -0,0 +1,107 @@ +## SPDX-License-Identifier: Apache-2.0 +## Copyright (C) 2023, KNS Group LLC (YADRO) + +import json +from shutil import copy +from os import path, makedirs, walk +from xml.dom import minidom + +from .globals import __RFG_PATH__ +from .globals import __WWW_PATH__ + +class RedfishBase: + """ + Redfish base class. + Contains all common methods for RedfishNode and RedfishSchema classes. + """ + + @staticmethod + def redfish_version(): + return "v1" + + @staticmethod + def _json_schemas_relative_dir(): + return "JsonSchemas" + + @staticmethod + def _load_file_fullpath(fullname): + with open(fullname) as f: + data = f.read() + return data + + @staticmethod + def _load_file(fullname, schema_path=__RFG_PATH__): + file_path = path.join(schema_path, fullname) + with open(file_path) as f: + data = f.read() + return data + + @staticmethod + def _load_bundle_file(fullname): + file_path = path.join("assets", "schemas", "bundle", fullname) + return RedfishBase._load_file(file_path) + + @staticmethod + def _load_csdl_file(fullname): + data = RedfishBase._load_bundle_file(fullname) + return minidom.parseString(data) + + + @staticmethod + def _version_from_filename(filename): + parts = filename.split(".") + if len(parts) > 1: + return parts[1] + return None + + @staticmethod + def schema_and_version_from(filename): + filename = path.basename(filename) + filename = path.splitext(filename)[0] + parts = filename.split(".") + if len(parts) > 1: + return parts[0], parts[1] + return parts[0], None + + @staticmethod + def write_file(destdir, filename, content): + if not path.isdir(destdir): + makedirs(destdir, exist_ok=True) + filename = path.join(destdir, filename) + if isinstance(content, str): + with open(filename, "w") as file: + file.writelines(content) + else: + with open(filename, "wb") as file: + file.write(content) + + def __init__(self, standard: str, schema_name, version) -> None: + if standard != "redfish" and standard != "redfish/swordfish": + raise Exception("API Standard \"%s\" in not supported" % standard) + self._standard = standard + self._schema_name = schema_name + if self._schema_name is None: + raise ValueError("Node config: require field 'Schema'") + self._schema_version = version[1:] if version is not None and version.startswith("v") else version + + def standard(self): + return self._standard + + def schema_id(self): + return self._schema_name + + def version(self): + if self._schema_version: + return "v" + self._schema_version.replace(".", "_") + return None + + def _available_schema(self): + if self.schema is not None: + return self.schema + return self.schema_spec + + def is_oem(self): + return self._schema_name.startswith("Oem") + + def get_json_schema(self): + return self.schema diff --git a/tools/redfish-gen/redfish_gen/redfish_node.py b/tools/redfish-gen/redfish_gen/redfish_node.py index 3c63f7e4..a620b6ce 100644 --- a/tools/redfish-gen/redfish_gen/redfish_node.py +++ b/tools/redfish-gen/redfish_gen/redfish_node.py @@ -5,12 +5,13 @@ import json from xml.dom import minidom +from .redfish_base import RedfishBase from .property import OemFragment, Property from .globals import __RFG_PATH__ from .node_parameter import NodeParameter, StaticNodeParameter -class RedfishNode: +class RedfishNode(RedfishBase): """ Redfish node instance. Contains all relevant configs, provides engine to acquire @@ -37,31 +38,20 @@ def build(parent=None, **kwargs): else: raise ValueError("Invalid config definition") - # Load configs - @staticmethod - def __load_file(filename, schema_path=__RFG_PATH__): - with open(schema_path + "/assets/schemas/bundle/" + filename) as f: - data = f.read() - return data - def __load_openapi(self): - data = self.__load_file(self.__schema_file_openapi()) + data = self._load_bundle_file(self.__schema_file_openapi()) return yaml.safe_load(data) def __load_schema_json(self): file = self.__schema_file_json() if file is not None: - return json.loads(self.__load_file(file)) + return json.loads(self._load_bundle_file(file)) return file def __load_schema_spec_json(self): - data = self.__load_file(self.__schema_spec_file_json()) + data = self._load_bundle_file(self.__schema_spec_file_json()) return json.loads(data) - def __load_csdl(self): - data = self.__load_file(self.__schema_file_csdl()) - return minidom.parseString(data) - def __schema_file(self): if self._schema_version: return self._schema_name + "." + self.version() @@ -92,21 +82,20 @@ def __build_id(self): # Constructor def __init__(self, parent, segment, **kwargs): + super().__init__(standard="redfish", + schema_name=kwargs.get("Schema", None), + version=str(kwargs.get("Version", ""))) self._parent = parent self._segment = kwargs.get("Segment", segment) self._classname = segment self._prefix = kwargs.get("Prefix", "").split("/") self.__build_id() - self._schema_name = kwargs.get("Schema", None) - if self._schema_name is None: - raise ValueError("Node config: require field 'Schema'") - self._schema_version = str(kwargs.get("Version", "")) self._y = kwargs # Load resourses self.openapi = self.__load_openapi() self.schema_spec = self.__load_schema_spec_json() self.schema = self.__load_schema_json() - self.csdl = self.__load_csdl() + self.csdl = RedfishBase._load_csdl_file(self.__schema_file_csdl()) def is_dynamic(self): return False @@ -136,11 +125,6 @@ def __get(self): def __modify(self): return self.__actions()("Modify") - def __available_schema(self): - if self.schema is not None: - return self.schema - return self.schema_spec - def __schema_spec_node(self): """ BUG note @@ -162,9 +146,6 @@ def __formatting_classname(classname): def name(self): return self._y["Name"] - def schema_id(self): - return self._schema_name - def segment(self): return self._segment @@ -192,7 +173,7 @@ def id(self): return self._id def odata_type(self): - return self.__available_schema()["title"] + return self._available_schema()["title"] def classname(self): return RedfishNode.__formatting_classname(self._classname) @@ -200,11 +181,6 @@ def classname(self): def def_filename(self): return self._classname - def version(self): - if self._schema_version: - return "v" + self._schema_version.replace(".", "_") - return None - def insertable(self): return self.__schema_spec_node()["insertable"] @@ -299,7 +275,7 @@ def __check_properties_definition(self, source: str) -> bool: def _properties_expand(self, source: str): if not self.__check_properties_definition(source): return [] - return Property.build(source, self.schema_id(), self.__available_schema(), self.__get()["Properties"]) + return Property.build(source, self.schema_id(), self._available_schema(), self.__get()["Properties"]) def links(self): return self._properties_expand("Links") diff --git a/tools/redfish-gen/redfish_gen/redfish_schema.py b/tools/redfish-gen/redfish_gen/redfish_schema.py index 6c825e1e..bca7b009 100644 --- a/tools/redfish-gen/redfish_gen/redfish_schema.py +++ b/tools/redfish-gen/redfish_gen/redfish_schema.py @@ -1,15 +1,16 @@ ## SPDX-License-Identifier: Apache-2.0 -## Copyright (C) 2022, KNS Group LLC (YADRO) +## Copyright (C) 2023, KNS Group LLC (YADRO) import json from shutil import copy from os import path, makedirs, walk from xml.dom import minidom +from .redfish_base import RedfishBase from .globals import __RFG_PATH__ from .globals import __WWW_PATH__ -class RedfishSchema: +class RedfishSchema(RedfishBase): """ Redfish schema instance. Contains all relevant schemes and versions, provides engine to acquire @@ -27,51 +28,30 @@ def build(standard: str, schemaName, version, resolved, schema_json): RedfishSchema.schemas[schemaName] = schema return schema - def __init__(self, standard: str, schemaName, version, resolved, schema_json) -> None: - if standard != "redfish" and standard != "redfish/swordfish": - raise Exception("API Standard \"%s\" in not supported" % standard) - self._standard = standard - self._schema_name = schemaName - self._version = version + def __init__(self, standard: str, schema_name, version, resolved, schema_json) -> None: + super().__init__(standard=standard, + schema_name=schema_name, + version=version) self._dependencies = set() self._resolved = resolved if schema_json is not None: - self._json_schema = schema_json + self.schema = schema_json else: filename = self.__json_full_filename() - self._json_schema = RedfishSchema.load_json(filename) - - def standard(self): - return self._standard - - def id(self): - return self._schema_name - - def version(self): - return self._version + self.schema = RedfishSchema.load_json(filename) def schema_and_version(self): - if self._version: - return self._schema_name + "." + self._version + if self._schema_version: + return self._schema_name + ".v" + self._schema_version else: return self._schema_name - def is_oem(self): - return self._schema_name.startswith("Oem") - def resolved(self): return self._resolved - def get_json_schema(self): - return self._json_schema - - @staticmethod - def redfish_version(): - return "v1" - @staticmethod def redfish_v1_relative_path(standard: str): - return path.join(standard, RedfishSchema.redfish_version()) + return path.join(standard, RedfishBase.redfish_version()) @staticmethod def extract_schemas(properties: dict, deep = 0): @@ -178,7 +158,7 @@ def __prepare_oem_schemas(jsondir=path.join(__RFG_PATH__, "assets", "schemas", " for file in files: if file.startswith("Oem") and file.endswith(".json"): filename = path.join(root, file) - _, schemaVersion = RedfishSchema.schema_and_version_from(file) + _, schemaVersion = RedfishBase.schema_and_version_from(file) # Add Oem*.json schemas only with version if path.isfile(filename) and schemaVersion is not None: json_spec = RedfishSchema.load_json(filename) @@ -201,7 +181,7 @@ def __resolve_remaining_schemas(): break @staticmethod - def __resolve(schema, srcdir=__RFG_PATH__): + def __resolve(schema): json = schema.get_json_schema() if "definitions" in json: RedfishSchema.extract_schemas(json["definitions"]) @@ -216,7 +196,7 @@ def __build_result_schemas(schemaIndexDoc, rootElement, jsonSchemas): RedfishSchema.__resolve(schema) print(" - Generating Redfish scheme file: " + schema.schema_and_version()) RedfishSchema.__copy_schema_scdl_from_assets(schema) - RedfishSchema.__add_index_refs_part(schema.standard(), schema.id(), schemaIndexDoc, rootElement) + RedfishSchema.__add_index_refs_part(schema.standard(), schema.schema_id(), schemaIndexDoc, rootElement) # JSON schema part jsonSchemaIndexPath = RedfishSchema.__copy_schema_json_file_from_assets(schema) jsonSchemas.append(jsonSchemaIndexPath) @@ -235,7 +215,7 @@ def __build_indexes(schemaIndexDoc, rootElement, jsonSchemas): # Generate "/redfish/v1/$metadata/index.xml" csdlSchemeIndex = schemaIndexDoc.toprettyxml(encoding="utf-8") csdlOutDir = path.join(__WWW_PATH__, RedfishSchema.redfish_v1_relative_path("redfish"), "$metadata") - RedfishSchema.write_file(csdlOutDir, "index.xml", csdlSchemeIndex) + RedfishBase.write_file(csdlOutDir, "index.xml", csdlSchemeIndex) # Generate "/redfish/v1/JsonSchemas/index.json" RedfishSchema.__generate_json_index("redfish", jsonSchemas) @@ -248,9 +228,9 @@ def __add_schema_by_name(standard: str, schemaName): def __add_index_refs_part(standard: str, schema_name: str, csdlIndexDoc, csdlIndexRoot, scdlOemDir = ""): referenceElement = csdlIndexDoc.createElement('edmx:Reference') uri = path.join(RedfishSchema.redfish_v1_relative_path(standard), "schema", RedfishSchema.__schema_file_csdl_by_name(schema_name)) \ - if len(scdlOemDir) == 0 else path.join(scdlOemDir, schema_name + "_" + RedfishSchema.redfish_version() + ".xml") + if len(scdlOemDir) == 0 else path.join(scdlOemDir, schema_name + "_" + RedfishBase.redfish_version() + ".xml") csdlPath = path.join(__WWW_PATH__, uri) - csdl = RedfishSchema.__load_csdl(csdlPath) + csdl = RedfishBase._load_csdl_file(csdlPath) referenceElement.setAttribute("Uri", "/" + uri) for schema in csdl.getElementsByTagName("Schema"): schemaNamespace = schema.getAttribute("Namespace") @@ -268,22 +248,22 @@ def __copy_schema_scdl_from_assets(schema, srcdir=__RFG_PATH__, destdir=__WWW_PA fromfile = path.join( srcdir, RedfishSchema.__oem_relative_path(), - RedfishSchema.__schema_file_csdl_by_name(schema.id())) + RedfishSchema.__schema_file_csdl_by_name(schema.schema_id())) else: fromfile = path.join( srcdir, RedfishSchema.__bundle_csdl_relative_path(schema.standard()), - RedfishSchema.__schema_file_csdl_by_name(schema.id())) + RedfishSchema.__schema_file_csdl_by_name(schema.schema_id())) todir = path.join( destdir, schema.standard(), - RedfishSchema.redfish_version(), + RedfishBase.redfish_version(), "schema") if not path.isdir(todir): makedirs(todir, exist_ok=True) tofile = path.join( todir, - RedfishSchema.__schema_file_csdl_by_name(schema.id())) + RedfishSchema.__schema_file_csdl_by_name(schema.schema_id())) copy(fromfile, tofile) @staticmethod @@ -302,11 +282,11 @@ def __copy_schema_json_file_from_assets(schema, srcdir=__RFG_PATH__, destdir=__W schemaNameVer = schema.schema_and_version() fromfile = schema.__json_full_filename() - toname = schema.id() + toname = schema.schema_id() redfishdir = path.join( schema.standard(), - RedfishSchema.redfish_version(), - RedfishSchema.__json_schemas_relative_dir(), + RedfishBase.redfish_version(), + RedfishBase._json_schemas_relative_dir(), toname) todir = path.join( destdir, @@ -326,14 +306,14 @@ def __copy_schema_json_file_from_assets(schema, srcdir=__RFG_PATH__, destdir=__W fromfile = RedfishSchema.__fix_to_dot_version(fromfile) copy(fromfile, tofile) if not schemaNameVer.startswith("Oem"): - RedfishSchema.__write_json_subindex(schema.standard(), redfishdir, schema.id()) + RedfishSchema.__write_json_subindex(schema.standard(), redfishdir, schema.schema_id()) return redfishdir def __json_full_filename(self, srcdir=__RFG_PATH__): - fromname = RedfishSchema.__map_json_schema_file(self.id()) + fromname = RedfishSchema.__map_json_schema_file(self.schema_id()) from_name_version = fromname - if self._version is not None: - from_name_version += "." + self._version + if self._schema_version is not None: + from_name_version += ".v" + self._schema_version return path.join( srcdir, RedfishSchema.bundle_relative_path(self._standard), @@ -350,23 +330,23 @@ def __generate_json_index(standard: str, json_schemas): content = json.dumps(json_schema_index, indent=2) json_out_dir = path.join(__WWW_PATH__, standard, - RedfishSchema.redfish_version(), + RedfishBase.redfish_version(), "JsonSchemas" ) - RedfishSchema.write_file(json_out_dir, "index.json", content) + RedfishBase.write_file(json_out_dir, "index.json", content) @staticmethod def __write_json_subindex(standard: str, relative_schema_path, schema_spec_name, destdir = __WWW_PATH__): content = RedfishSchema.__generate_json_schema_index(standard, schema_spec_name, relative_schema_path) fullpath = path.join(destdir, relative_schema_path) - RedfishSchema.write_file(fullpath, "index.json", content) + RedfishBase.write_file(fullpath, "index.json", content) @staticmethod def __create_json_schema_index(standard: str): return { - "@odata.id": "/" + standard + "/" + RedfishSchema.redfish_version() + "/" + RedfishSchema.__json_schemas_relative_dir(), + "@odata.id": "/" + standard + "/" + RedfishBase.redfish_version() + "/" + RedfishBase._json_schemas_relative_dir(), # TODO why the next line contains "$metadata" subdir? - "@odata.context": "/" + standard +"/" + RedfishSchema.redfish_version() + "/$metadata#JsonSchemaFileCollection.JsonSchemaFileCollection", + "@odata.context": "/" + standard +"/" + RedfishBase.redfish_version() + "/$metadata#JsonSchemaFileCollection.JsonSchemaFileCollection", "@odata.type": "#JsonSchemaFileCollection.JsonSchemaFileCollection", "Name": "JsonSchemaFile Collection", "Description": "Collection of JsonSchemaFiles", @@ -386,9 +366,9 @@ def __generate_json_schema_index(standard: str, schema_spec_name, relative_schem template = RedfishSchema.__generate_json_schema_index_template() template = template.replace("{name}", schema_spec_name) template = template.replace("{relativeSchemaPath}", relative_schema_path) - template = template.replace("{JsonSchemas}", RedfishSchema.__json_schemas_relative_dir()) + template = template.replace("{JsonSchemas}", RedfishBase._json_schemas_relative_dir()) template = template.replace("{standard}",standard) - template = template.replace("{v}", RedfishSchema.redfish_version()) + template = template.replace("{v}", RedfishBase.redfish_version()) return template @staticmethod @@ -415,10 +395,6 @@ def __generate_json_schema_index_template(): "Location@odata.count": 1 }''' - @staticmethod - def __json_schemas_relative_dir(): - return "JsonSchemas" - @staticmethod def __find_latest_version_schema_in_json(standartd: str, schema_id, srcdir=__RFG_PATH__): json_bundle_path = path.join( @@ -435,11 +411,11 @@ def __find_latest_version_schema_in_json(standartd: str, schema_id, srcdir=__RFG if newest_double_ver < RedfishSchema.__version_to_double(file): newest_name = file newest_double_ver = current_double_ver - return RedfishSchema._version_from_filename(newest_name) + return RedfishBase._version_from_filename(newest_name) @staticmethod def __schema_file_csdl_by_name(schema_id): - return schema_id + "_" + RedfishSchema.redfish_version() + ".xml" + return schema_id + "_" + RedfishBase.redfish_version() + ".xml" @staticmethod def __bundle_csdl_relative_path(standard: str): @@ -461,22 +437,11 @@ def bundle_relative_path(standard: str): def __oem_relative_path(): return path.join("assets", "oem") - @staticmethod - def __load_csdl(fullname): - data = RedfishSchema.__load_file(fullname) - return minidom.parseString(data) - @staticmethod def load_json(fullname): - data = RedfishSchema.__load_file(fullname) + data = RedfishSchema._load_file_fullpath(fullname) return json.loads(data) - @staticmethod - def __load_file(fullname): - with open(fullname) as f: - data = f.read() - return data - @staticmethod def __version_to_double(schema_spec): parts = schema_spec.split(".") @@ -488,35 +453,6 @@ def __version_to_double(schema_spec): return int(ver_parts[1]) + 0.00000001 return 0 - @staticmethod - def _version_from_filename(filename): - parts = filename.split(".") - if len(parts) > 1: - return parts[1] - return None - - - @staticmethod - def schema_and_version_from(filename): - filename = path.basename(filename) - filename = path.splitext(filename)[0] - parts = filename.split(".") - if len(parts) > 1: - return parts[0], parts[1] - return parts[0], None - - @staticmethod - def write_file(destdir, filename, content): - if not path.isdir(destdir): - makedirs(destdir, exist_ok=True) - filename = path.join(destdir, filename) - if isinstance(content, str): - with open(filename, "w") as file: - file.writelines(content) - else: - with open(filename, "wb") as file: - file.write(content) - @staticmethod def __map_json_schema_file(basename): schemas_map = RedfishSchema.__json_schema_file_map() @@ -534,7 +470,7 @@ def __fix_to_dot_version(filename): """ Fix "*v1_0_0.json" to "1.0.0.json" for "old" version schemas in the DMTF bundle. """ - ver_index = filename.rfind(RedfishSchema.redfish_version()) + ver_index = filename.rfind(RedfishBase.redfish_version()) last_dot_ndex = filename.rfind(".", ver_index) if ver_index != -1 and last_dot_ndex != -1: return filename[0: ver_index] \