From b4c17555fa1a7c6c1ab788ece3114dbd316a6b93 Mon Sep 17 00:00:00 2001 From: Scott Phuong Date: Tue, 17 Nov 2020 12:23:44 -0800 Subject: [PATCH] Initial commit for OEM and Redfish OpenAPI consolidator. --- .../ComputerSystem-OEM.v1_12_0.yaml | 23 + .../oem-contoso/Contoso-definitions.yaml | 12 + .../ManagerAccount-OEM.all-vers.yaml | 12 + .../Example/oem-contoso/Resource-OEM.yaml | 10 + .../Example/oem-contoso/openapi.yaml | 46 ++ openapi-oem-consolidator/LICENSE.md | 57 +++ openapi-oem-consolidator/README.md | 159 ++++++ .../openapi-oem-consolidator.py | 455 ++++++++++++++++++ 8 files changed, 774 insertions(+) create mode 100644 openapi-oem-consolidator/Example/oem-contoso/ComputerSystem-OEM.v1_12_0.yaml create mode 100644 openapi-oem-consolidator/Example/oem-contoso/Contoso-definitions.yaml create mode 100644 openapi-oem-consolidator/Example/oem-contoso/ManagerAccount-OEM.all-vers.yaml create mode 100644 openapi-oem-consolidator/Example/oem-contoso/Resource-OEM.yaml create mode 100644 openapi-oem-consolidator/Example/oem-contoso/openapi.yaml create mode 100644 openapi-oem-consolidator/LICENSE.md create mode 100644 openapi-oem-consolidator/README.md create mode 100755 openapi-oem-consolidator/openapi-oem-consolidator.py diff --git a/openapi-oem-consolidator/Example/oem-contoso/ComputerSystem-OEM.v1_12_0.yaml b/openapi-oem-consolidator/Example/oem-contoso/ComputerSystem-OEM.v1_12_0.yaml new file mode 100644 index 00000000..39b47251 --- /dev/null +++ b/openapi-oem-consolidator/Example/oem-contoso/ComputerSystem-OEM.v1_12_0.yaml @@ -0,0 +1,23 @@ +components: + schemas: + ComputerSystem_v1_12_0_ComputerSystem: + properties: + Oem: + additionalProperties: true + description: Contoso's OEM extension. + properties: + MyFoo : + type : string + type: object + x-longDescription: Some long description. + x-patternProperties: + ^([a-zA-Z_][a-zA-Z0-9_]*)?@(odata|Redfish|Message)\.[a-zA-Z_][a-zA-Z0-9_]*$: + description: This property shall specify a valid Contoso property. + ^[A-Za-z0-9_]+$: + ComputerSystem_v1_12_0_OemActions: + additionalProperties: true + description: Contoso's Action OEM extension. + properties: + MyActionParameter: + type : string + diff --git a/openapi-oem-consolidator/Example/oem-contoso/Contoso-definitions.yaml b/openapi-oem-consolidator/Example/oem-contoso/Contoso-definitions.yaml new file mode 100644 index 00000000..bcd3e564 --- /dev/null +++ b/openapi-oem-consolidator/Example/oem-contoso/Contoso-definitions.yaml @@ -0,0 +1,12 @@ + +# This file contains base definitions for Contoso. + +components: + schema: + blah: + type: string + blah-blah: + type: number + blah-blah-blah: + type: string + diff --git a/openapi-oem-consolidator/Example/oem-contoso/ManagerAccount-OEM.all-vers.yaml b/openapi-oem-consolidator/Example/oem-contoso/ManagerAccount-OEM.all-vers.yaml new file mode 100644 index 00000000..230b14c7 --- /dev/null +++ b/openapi-oem-consolidator/Example/oem-contoso/ManagerAccount-OEM.all-vers.yaml @@ -0,0 +1,12 @@ + +components: + schemas: + ManagerAccount_v1_6_1_ManagerAccount: + properties: + Oem: + additionalProperties: true + description: Contoso's OEM extension. + properties: + MyMAccount : + type : string + diff --git a/openapi-oem-consolidator/Example/oem-contoso/Resource-OEM.yaml b/openapi-oem-consolidator/Example/oem-contoso/Resource-OEM.yaml new file mode 100644 index 00000000..e379e8bd --- /dev/null +++ b/openapi-oem-consolidator/Example/oem-contoso/Resource-OEM.yaml @@ -0,0 +1,10 @@ + +components: + schemas: + Resource_Oem: + additionalProperties: true + description: Contoso's OEM Base Resource extension. + properties: + A : + type : string + diff --git a/openapi-oem-consolidator/Example/oem-contoso/openapi.yaml b/openapi-oem-consolidator/Example/oem-contoso/openapi.yaml new file mode 100644 index 00000000..84d0adf0 --- /dev/null +++ b/openapi-oem-consolidator/Example/oem-contoso/openapi.yaml @@ -0,0 +1,46 @@ +## Contoso's Open API Yaml + +info: + contact: + name: Contoso, Inc. + url: https://www.contoso.org + description: This contains the definition of a Redfish service with Contoso's OEM extensions. + title: Redfish + version: 'Contoso-2020.3-1.0.0' + x-copyright: Copyright 2020 DMTF and Contoso. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright. For the full Contoso's copyright, visit https://www.contoso.org + +openapi: 3.0.1 + +paths: + /redfish/v1/Contoso/blah: + get: + responses: + '200': + content: + application/json: + schema: + $ref: http://www.contoso.org/schemas/v1/ServiceRoot.v1_9_0.yaml#/components/schemas/ServiceRoot_v1_9_0_ServiceRoot + description: The response contains a representation of the ServiceRoot resource + default: + content: + application/json: + schema: + $ref: '#/components/schemas/RedfishError' + description: Bad/Error condition + /redfish/v1/Contoso/blahblah: + get: + responses: + '200': + content: + application/json: + schema: + $ref: http://www.contoso.org/schemas/v1/ServiceRoot.v1_9_0.yaml#/components/schemas/ServiceRoot_v1_9_0_ServiceRoot + description: The response contains a representation of the ServiceRoot resource + default: + content: + application/json: + schema: + $ref: '#/components/schemas/RedfishError' + description: Bad/Error condition + + diff --git a/openapi-oem-consolidator/LICENSE.md b/openapi-oem-consolidator/LICENSE.md new file mode 100644 index 00000000..5298393f --- /dev/null +++ b/openapi-oem-consolidator/LICENSE.md @@ -0,0 +1,57 @@ +The Distributed Management Task Force (DMTF) grants rights under copyright in +this software on the terms of the BSD 3-Clause License as set forth below; no +other rights are granted by DMTF. This software might be subject to other rights +(such as patent rights) of other parties. + + +### Copyrights. + +Copyright (c) 2020, Contributing Member(s) of Distributed Management Task Force, +Inc.. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. +* Neither the name of the Distributed Management Task Force (DMTF) nor the names +of its contributors may be used to endorse or promote products derived from this +software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +### Patents. + +This software may be subject to third party patent rights, including provisional +patent rights ("patent rights"). DMTF makes no representations to users of the +standard as to the existence of such rights, and is not responsible to +recognize, disclose, or identify any or all such third party patent right, +owners or claimants, nor for any incomplete or inaccurate identification or +disclosure of such rights, owners or claimants. DMTF shall have no liability to +any party, in any manner or circumstance, under any legal theory whatsoever, for +failure to recognize, disclose, or identify any such third party patent rights, +or for such party's reliance on the software or incorporation thereof in its +product, protocols or testing procedures. DMTF shall have no liability to any +party using such software, whether such use is foreseeable or not, nor to any +patent owner or claimant, and shall have no liability or responsibility for +costs or losses incurred if software is withdrawn or modified after publication, +and shall be indemnified and held harmless by any party using the software from +any and all claims of infringement by a patent owner for such use. + +DMTF Members that contributed to this software source code might have made +patent licensing commitments in connection with their participation in the DMTF. +For details, see http://dmtf.org/sites/default/files/patent-10-18-01.pdf and +http://www.dmtf.org/about/policies/disclosures. diff --git a/openapi-oem-consolidator/README.md b/openapi-oem-consolidator/README.md new file mode 100644 index 00000000..7c61cc37 --- /dev/null +++ b/openapi-oem-consolidator/README.md @@ -0,0 +1,159 @@ +# OpenAPI OEM and Redfish Bundle Consolidator + +Copyright 2020 DMTF. All rights reserved. + +## About + +The OpenAPI OEM and Redfish Bundle Consolidator is a tool that consolidates +a published Redfish schema bundle with an OEM's bundle into a single bundle to +compliant with OpenAPI specification. + +## Requirements + +This tool can run on any operating systems that has Python3 installed. The tool requires the following packages to be installed already: + + * PyYaml + * ArgParse + * Pprint + * re + * glob + * os + * sys + +## Usage + +Consult the help provided by the tool itself for exact usage syntax: + +``` +usage: openapi-oem-consolidator.py --help +``` + +### Preparation + +Before running the tool, create two directories: + + * Redfish Directory + * OEM Directory + +These two directories must be different and not a child directory of each other. The directory names are of your choosing. + +The Redfish directory must contain the extracted DSP8010 bundle. The OEM directory will contain the OEM's files as described in [OEM Bundle Methodology](#oem-bundle-methodology) + +## OEM Bundle Methodology + +The OEM directory is a directory that contains two types of files: + + * Normal OEM files + * Special OEM files + +Special OEM files are identified by a file naming convention. Normal OEM files are any files not starting with a period and not having the special file naming convention defined in this document. + +Normal OEM files are simply copied to the consolidated (i.e., output) directory. They are expected to be OpenAPI compliant especially if they are referenced by other other OpenAPI files. + +There is no limitation to the number of files in this directory. + +For an example of an OEM Bundle, look at the contents of the `Example` directory. These examples are not Redfish compliant. The example was tested against 2020.3 Redfish schema bundle. + +### Special OEM Files + +Special OEM files are files that describe to the tool how to modify the Redfish equivalent files to inject or replace OEM sections in standard Redfish schema. These files are populated by the OEM in an OEM directory. + +The following subsections describe their respective special OEM file. + +Lastly, all files in the Redfish directory and OEM directories are never modified. The specified output directory will contained the modified versions. + +#### openapi.yaml + +This file has the same filename as the Redfish one. The tool expects the OEM version to contain the following yaml sections: + + * `info` + * `opeanapi` + +The tool will error if the OEM's `openapi.yaml` contains a `components` section. + +The tool takes the `info` section from the OEM's and replaces the `info` section in the Redfish's `openapi.yaml`. + +The tool expects that the `openapi` section specify the same version as the Redfish one. If not, it is an error. + +The OEM `openapi.yaml` may contain a `paths` section. The tool just merges the `path` section from both files into one. + +This file is required. + +#### $(ResourceName)-OEM.yaml + +`$(ResourceName)` is a name of a resource such as *ManagerAccount*, *ComputerSystem*. The tool matches `$(ResourceName)-OEM.yaml` to the equivalent unversioned Redfish resource of the name `$(ResourceName).yaml`. + +To replace one or more OEM sections in an unversioned Redfish resource YAML file, create a file whose name is `$(ResourceName)-OEM.yaml` in the given OEM directory. + +`$(ResourceName)-OEM.yaml` must have the same YAML path as in the Redfish `$(ResourceName).yaml`. + +Only OEM YAML paths are allowed in `$(ResourceName)-OEM.yaml`. All other paths will cause an error. See [OEM YAML Paths](#oem-yaml-paths) for details. The OEM YAML paths will replace the same path in the Redfish `$(ResourceName).yaml`. If a path in `$(ResourceName)-OEM.yaml` is not found in the Redfish equivalent, an error will occur. + +#### $(ResourceName)-OEM.v$(ver).yaml + +`$(ResourceName)` is a name of a resource such as *ManagerAccount*, *ComputerSystem*. `$(ver)` is the version, using underscore (_) as the version separator, such as *1_0_0*, *1_7_5*. The tool matches `$(ResourceName)-OEM.v$(ver).yaml` to the equivalent versioned Redfish resource of the name `$(ResourceName).v$(ver).yaml`. + +To replace one or more OEM sections in a versioned Redfish resource YAML file, create a file whose name is `$(ResourceName)-OEM.v$(ver).yaml` in the given OEM directory. + +`$(ResourceName)-OEM.v($ver).yaml` must have the same YAML path as in the Redfish `$(ResourceName).v$(ver).yaml`. + +Only OEM YAML paths are allowed in `$(ResourceName)-OEM.v($ver)yaml`. All other paths will cause an error. See [OEM YAML Paths](#oem-yaml-paths) for details. The OEM YAML paths will replace the same path in the Redfish `$(ResourceName).v$(ver).yaml`. If a path in `$(ResourceName)-OEM.v($ver).yaml` is not found in the Redfish equivalent, an error will occur. + +#### $(ResourceName)-OEM.all-vers.y + +`$(ResourceName)` is a name of a resource such as *ManagerAccount*, *ComputerSystem*. The tool matches `$(ResourceName)-OEM.all-vers.yaml` to **all** versioned Redfish resource of the name `$(ResourceName).v*.yaml` where the asterisks (*) serves as a wildcard. + +This method allows a single OEM file to replace all OEM sections in **all** versions of the same resource. + +To replace one or more OEM sections in all versions of a versioned Redfish resource YAML file, create a file whose name is `$(ResourceName)-OEM.all-vers.yaml` in the given OEM directory. + +`$(ResourceName)-OEM.all-vers.yaml` must have the same YAML path as in **one** of the versions of a versions Redfish resource of the name `$(ResourceName).v$(ver).yaml`. The tool assumes the same path, ignoring the version information embedded in YAML property names, exist in all versions. If one version does not have the same path, an error will occur. + +Only OEM YAML paths are allowed in `$(ResourceName)-OEM.all-vers.yaml`. All other paths will cause an error. See [OEM YAML Paths](#oem-yaml-paths) for details. The OEM YAML paths will replace the same path in each version of the versioned Redfish `$(ResourceName).v$(ver).yaml`. If a path in `$(ResourceName)-OEM.v($ver).yaml` is not found in the Redfish equivalent, an error will occur. + +### OEM YAML Paths + +YAML paths are YAML object hierarchy paths. OEM YAML paths are YAML paths starting at the root/top to the first occurrence of a property/key name that meets one of these conditions: + + * Property/Key name is exactly "OEM" + * Property/Key name is exactly "Oem" + * Property/Key name is exactly "Resource_Oem" + * Property/Key name ends with "OemActions" + +Here are examples of OEM YAML Paths: + +OEM YAML Path Example 1: + +```yaml +components: + schemas: + ComputerSystem_v1_12_0_OemActions: +``` + +OEM YAML Path Example 2: + +```yaml +components: + schemas: + ManagerAccount_v1_6_1_ManagerAccount: + properties: + Oem: + +``` + +OEM YAML Path Example 3: + +```yaml +components: + schemas: + Resource_Oem: + +``` + +All content under an OEM YAML path will replace the equivalent path in the equivalent Redfish resource. + +## Miscellaneous + +The tool strips comments in YAML files when consolidating YAML files with OEM extensions. If a Redfish resource YAML file gets combined with OEM extensions, the resulting consolidated file will not contain any comments. The contents of Normal OEM files or non-consolidated Redfish YAML files are preserved as they are simply copied to output directory. + +For Special OEM files, if the equivalent file in the Redfish bundle is not found, an error will result. diff --git a/openapi-oem-consolidator/openapi-oem-consolidator.py b/openapi-oem-consolidator/openapi-oem-consolidator.py new file mode 100755 index 00000000..8d27e281 --- /dev/null +++ b/openapi-oem-consolidator/openapi-oem-consolidator.py @@ -0,0 +1,455 @@ +#! /usr/bin/env python3 + +## This tool consolidates the OEM's OpenAPI YAML file with Redfish Bundle. + +import argparse; +import glob; +import os; +import pprint; +import re; +import sys; +import shutil; +import yaml; + +dbg = False; +err_count = 0; +dbg_pp = None; + +def dbg_print(dbg_text): + global dbg; + + if dbg: + print("[DEBUG]: ", end=""); + print(dbg_text); + +def dbg_dict_pretty_print(indent, obj): + hdr = "[DEBUG]: "; + + for key, value in obj.items(): + print(hdr + (" " * indent) + str(key) + " : ", end=""); + + if (isinstance(value, dict)): + print("{"); + dbg_dict_pretty_print(indent + 1, value); + print(hdr + (" " * indent) + "}"); + else: + print(str(value)); + + return; + +def dbg_pprint(dbg_title, obj): + global dbg; + global dbg_pp; + + if dbg: + print("[DEBUG]: ", end=""); + print(dbg_title + ":"); + + if isinstance(obj, dict): + dbg_dict_pretty_print(0, obj); + else: + dbg_pp.pprint(obj); + + return; + +def err_print(err_type, err_txt): + global err_count; + + if err_type == 1: + print("[ERROR SUMMARY]: ", end=""); + else: + print("[ERROR #" + str(err_count)+ "]: ", end=""); + err_count += 1; + + print(err_txt); + +def check_redfish_dir(basepath): + # This is a simple sanity check that the directory provided is a Redfish + # DSP8010 directory. In other words, an extracted DSP8010 directory. + + if (not os.path.isdir(os.path.join(basepath, "openapi"))) or \ + (not os.path.isdir(os.path.join(basepath, "csdl"))) or \ + (not os.path.isdir(os.path.join(basepath, "json-schema"))) or \ + (not os.path.isdir(os.path.join(basepath, "dictionaries"))) or \ + (not os.path.isfile(os.path.join(basepath, "openapi", "openapi.yaml"))): + return 1; + + return 0; + +def check_oem_dir(basepath): + + if not os.path.isfile(os.path.join(basepath, "openapi.yaml")): + return 1; + + return 0; + + +def copy_redfish_yaml_to_output(src, dst): + + shutil.copytree(os.path.join(src, "openapi"), dst, dirs_exist_ok=True); + +def read_oem_directory(oem_dir): + + oem_files = os.listdir(path=oem_dir); + return oem_files; + +def parse_redfish_resource_name(name): + # This function splits out the base resource name and its version + # from the YAML file name. + resource_info = {}; + + bname = os.path.basename(name); + pat = re.compile("^(.+)\.v([0-9]+_[0-9]+_[0-9]+)\.yaml"); + + match = pat.search(bname); + + if match: + resource_info["resource"] = match.group(1); + resource_info["version"] = match.group(2); + + dbg_pprint("Resource Info", resource_info); + + return resource_info; + +def do_search_and_replace_helper(oem_filename, rf_filename, patterns, + oem_yaml, redfish_yaml, yaml_path, + resource_info, ignore_ver): + + for key, value in oem_yaml.items(): + new_yaml_path = yaml_path + "/" + str(key); + + dbg_print("key: " + str(key)); + dbg_print("Processing YAML Path: " + new_yaml_path); + + if not key in redfish_yaml: + missing = 1; + + # Check to see if this property's name has a version string. + if ignore_ver and patterns["ver"].search(key): + sub_value = "_v" + resource_info["version"] + "_"; + key = patterns["ver"].sub(sub_value, key); + dbg_print("New Key Name: " + key); + + if key in redfish_yaml: + missing = 0; + + if missing: + err_print(0, oem_filename + ": YAML path, " + new_yaml_path + + ", not found in " + rf_filename); + return ; + + if (key == "Oem") or \ + (key == "OEM") or \ + (key == "Resource_Oem") or \ + (patterns["oem"].search(key)): + dbg_print("Found Matching OEM YAML path: " + new_yaml_path); + dbg_pprint("Value for Matching OEM YAML path", value); + redfish_yaml[key] = value; + return; + + do_search_and_replace_helper(oem_filename, rf_filename, patterns, value, + redfish_yaml[key], new_yaml_path, + resource_info, ignore_ver); + + return ; + +def do_search_and_replace(output_dir, oem_filename, rf_filename, oem_yaml, + redfish_yaml, ignore_ver): + + patterns = {}; + patterns["oem"] = re.compile("OemActions$"); + patterns["ver"] = re.compile("_v[0-9]+_[0-9]+_[0-9]+_"); + + resource_info = parse_redfish_resource_name(rf_filename); + + do_search_and_replace_helper(oem_filename, rf_filename, patterns, oem_yaml, + redfish_yaml, "", resource_info, ignore_ver); + + new_rf_data = yaml.dump(redfish_yaml); + + with open (os.path.join(output_dir, rf_filename), "w") as rf: + rf.write(new_rf_data); + + return; + +def do_single_substitution(src_dir, output_dir, of, rf, txt_hdr, ignore_ver): + oem_file = os.path.join(src_dir, of); + rf_file = os.path.join(output_dir, rf); + err = False; + + with open(oem_file, 'r') as f: + input_data = f.read(); + + with open(rf_file, 'r') as r: + rf_data = r.read(); + + f.close(); + r.close(); + + print(txt_hdr + "Parsing " + of + ". ** This may take a while. **"); + oem_yaml = yaml.safe_load(input_data); + + if (not isinstance(oem_yaml, dict)): + err_print(0, of + "does not contain any yaml data."); + err = True; + else: + dbg_pprint("OEM " + of + " YAML", oem_yaml); + + + print(txt_hdr + "Parsing " + rf + ". ** This may take a while. **"); + rf_yaml = yaml.safe_load(rf_data); + + if (not isinstance(rf_yaml, dict)): + err_print(0, rf + "does not contain any yaml data."); + err = True; + else: + dbg_pprint("Refish " + rf + " YAML", rf_yaml); + + if not err: + do_search_and_replace(output_dir, of, rf, oem_yaml, rf_yaml, ignore_ver); + + return; + + +def do_multiple_substitutions(src_dir, output_dir, of, resource): + # This function does multiple substitions. It substitues all versions for + # a given Redfish resource. + resource_filter = resource + ".v*.yaml"; + + matching_files = glob.glob(os.path.join(output_dir, resource_filter)); + + for rf in matching_files: + base_rf = os.path.basename(rf); + print(" Processing versioned Redfish resource: " + base_rf); + do_single_substitution(src_dir, output_dir, of, base_rf, " ", True); + +def process_openapi(oem_dir, output_dir): + err = False; + + with open(os.path.join(oem_dir, "openapi.yaml"), 'r') as oem: + oem_data = oem.read(); + + with open(os.path.join(output_dir, "openapi.yaml"), 'r') as rf: + rf_data = rf.read(); + + oem.close(); + rf.close(); + + print(" Parsing OEM OpenAPI. ** This may take a while. **"); + oem_yaml = yaml.safe_load(oem_data); + + if (not isinstance(oem_yaml, dict)): + err_print(0, "OEM's openapi.yaml does not contain any yaml data."); + err = True; + + print(" Parsing Redfish OpenAPI. ** This may take a while. **"); + rf_yaml = yaml.safe_load(rf_data); + + if (not isinstance(rf_yaml, dict)): + err_print(0, "Redfish's openapi.yaml does not contain any yaml data."); + err = True; + + if err: + return; + + print(" Checking OEM's OpenAPI requirements."); + + ## If components exist in OEM openapi.yaml, print an error. + if "components" in oem_yaml: + err_print(0, "OEM's openapi.yaml is prohibited from having components " + + "section."); + err = True; + + ## Check Info section. + if not "info" in oem_yaml: + err_print(0, "OEM's openapi.yaml is missing 'info' section."); + err = True; + else: + ## Replace the info section completely with the OEM's info section. + rf_yaml["info"] = oem_yaml["info"]; + + ## Check open api version. + if not "openapi" in oem_yaml: + err_print(0, "OEM's openapi.yaml is missing 'openapi' section."); + err = True; + + if not "openapi" in rf_yaml: + err_print(0, "Redfish's openapi.haml is missing 'openapi' section."); + err = True; + + ## Check that open api versions match. + if ("openapi" in oem_yaml) and \ + ("openapi" in rf_yaml): + if oem_yaml["openapi"] != rf_yaml["openapi"]: + err_print(0, "OEM and Redfish openapi versions do not match. " + + "Redfish's OpenAPI version: " + rf_yaml["openapi"] + ". " + + "OEM's OpenAPI version: " + oem_yaml["openapi"] ); + err = True; + + ## Process the paths section. + if "paths" in oem_yaml: + if "paths" in rf_yaml: + rf_yaml["paths"].update(oem_yaml["paths"]); + else: + err_print(0, "Redfish's openapi is missing 'paths' section."); + err = True; + + if not err: + print(" Writing consolidated OEM and Redfish openapi.yaml."); + + rf_data = yaml.dump(rf_yaml); + + with open(os.path.join(output_dir, "openapi.yaml"), "w") as out: + out.write(rf_data); + + out.close(); + + return; + +def process_oem_files(oem_dir, oem_files, dst_dir): + # This is the core logic for processing the OEM OpenAPI directory. + + single = re.compile(".+-OEM\.yaml$"); + single_ver = re.compile(".+-OEM\.v[0-9]+_[0-9]+_[0-9]+\.yaml$"); + all_vers = re.compile(".+-OEM\.all-vers\.yaml$"); + ignore_files = re.compile("^\..*$"); + multiple_files = 0; + + for f in oem_files: + print("Processing OEM file: " + f); + multiple_files = 0; + + if ignore_files.search(f): + print(" Ignored. Skipping."); + print(""); + continue; + + if f == "openapi.yaml": + process_openapi(oem_dir, dst_dir); + print(""); + continue; + + if single.search(f): + rf_basename = re.sub("-OEM\.yaml$", ".yaml", f); + elif single_ver.search(f): + rf_basename = re.sub("-OEM\.v", ".v", f); + elif all_vers.search(f): + multiple_files = 1; + rf_basename = re.sub("-OEM\.all-vers\.yaml", "", f); + else: + rf_basename = ""; + + if (rf_basename == ""): + # Just copy the file over to output directory + shutil.copy2(os.path.join(oem_dir, f), dst_dir); + print(" Copying file to " + dst_dir + "."); + elif multiple_files: + print(" Replacing all versions of " + rf_basename + "."); + do_multiple_substitutions(oem_dir, dst_dir, f, rf_basename); + else: + print(" Replacing single file or single versione of " + rf_basename + + " resource."); + do_single_substitution(oem_dir, dst_dir, f, rf_basename, " ", False); + + print(""); + +def main(): + global dbg; + global err_count; + global dbg_pp; + + # Create Command Line Parameters + desc_text = (f"Consolidates OEM's OpenAPI bundle with Redfish OpenAPI bundle " + "into a single OpenAPI bundle. For detailed help, please " + "consult README.md.\n" + "Positional arguments are required.\n" + ""); + parser = argparse.ArgumentParser(description=desc_text); + parser.add_argument('redfish_dir', action='store', + help=('Specifies the directory of the extracted Redfish ' + 'DSP8010 bundle.')); + parser.add_argument('oem_dir', action='store', + help='Specifies the OEM directory.'); + parser.add_argument('output_dir', action='store', + help=( + 'Specifies the consolidated (i.e., output) directory. ' + 'This directory will be created.')); + parser.add_argument('--debug', required=False, action='store_true', + default=False, + help="Enabled debug messages."); + parser.add_argument('--remove-output-dir', required=False, + action='store_true', default=False, + help="Remove the output directory if it exists."); + + args = parser.parse_args(); + dbg = args.debug; + err = 0; + + if dbg: + dbg_pp = pprint.PrettyPrinter(indent=1, compact=False); + + dbg_pprint("Arugments Passed In", args); + redfish_dir = args.redfish_dir; + oem_dir = args.oem_dir; + output_dir = args.output_dir; + + # Let's check that the "input" directory exists. + if not os.path.isdir(redfish_dir): + err_print(0, "The directory, " + redfish_dir + " does not exist."); + err = 1; + + if not os.path.isdir(oem_dir): + err_print(0, "The directory, " + oem_dir + " does not exist."); + err = 1; + + # Make sure the output directory does not exist to ensure we do not + # screw up an existing directory. + if os.path.isdir(output_dir): + if args.remove_output_dir: + shutil.rmtree(output_dir); + else: + err_print(0, "The directory, " + output_dir + " exists. Since this is " + + "an output " + "directory, please remove the directory or " + + "specify another directory."); + err = 1; + + if check_redfish_dir(redfish_dir): + err_print(0, redfish_dir + + " is not an extracted Redfish DSP8010 bundle directory."); + err = 1; + + if check_oem_dir(oem_dir): + err_print(0, oem_dir + " does not contain openapi.yaml."); + err = 1; + + if err: + err_print(1, "There were " + str(err_count) + " error(s) encountered."); + sys.exit(-1); + + err = 0; + + # Let's create the output directory. + try: + os.makedirs(output_dir); + except OSError: + print("Cannot create directory " + output_dir + "."); + sys.exit(-1); + else: + print("Successfully created directory " + output_dir + "."); + + print(""); + copy_redfish_yaml_to_output(redfish_dir, output_dir); + files = read_oem_directory(oem_dir); + process_oem_files(oem_dir, files, output_dir); + + if err_count: + err_print(1, "There were " + str(err_count) + " error(s) encountered."); + sys.exit(-1); + + print("*****************************************************"); + print("Redfish and OEM OpenAPI Consolidation was successful."); + print("*****************************************************"); + print(""); + sys.exit(0); + +main()