[edk2-devel] [staging/edk2-redfish-client Tools PATCH 3/6] RedfishClientPkg/Redfish-Profile-Simulator: Add more features

Abner Chang abner.chang at hpe.com
Thu Jul 22 06:08:14 UTC 2021


- Add HTTPs support
- Add ETAG support
- Change default credential to admin/pwd123456
- Add HTTP methods on BIOS managed resource.

Signed-off-by: Abner Chang <abner.chang at hpe.com>
Cc: Nickle Wang <nickle.wang at hpe.com>
Cc: Liming Gao <gaoliming at byosoft.com.cn>
---
 .../redfishProfileSimulator.py                |  92 ++++++++--
 .../v1sim/redfishURIs.py                      | 161 ++++++++++++------
 .../v1sim/registry.py                         |  14 ++
 .../v1sim/resource.py                         |  27 ++-
 .../v1sim/systems.py                          |  85 ++++++++-
 5 files changed, 311 insertions(+), 68 deletions(-)
 create mode 100644 RedfishClientPkg/Tools/Redfish-Profile-Simulator/v1sim/registry.py

diff --git a/RedfishClientPkg/Tools/Redfish-Profile-Simulator/redfishProfileSimulator.py b/RedfishClientPkg/Tools/Redfish-Profile-Simulator/redfishProfileSimulator.py
index 24be52bafc..91c792a2b7 100644
--- a/RedfishClientPkg/Tools/Redfish-Profile-Simulator/redfishProfileSimulator.py
+++ b/RedfishClientPkg/Tools/Redfish-Profile-Simulator/redfishProfileSimulator.py
@@ -1,4 +1,9 @@
 # Copyright Notice:
+#
+# Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+# Copyright Notice:
 # Copyright 2016 Distributed Management Task Force, Inc. All rights reserved.
 # License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/Redfish-Profile-Simulator/blob/master/LICENSE.md
 
@@ -9,13 +14,16 @@
 import sys
 import getopt
 import os
+import functools
+import flask
+import werkzeug
 
 rfVersion = "0.9.6"
 rfProgram1 = "redfishProfileSimulator"
 rfProgram2 = "                "
 rfUsage1 = "[-Vh]  [--Version][--help]"
-rfUsage2 = "[-H<hostIP>] [-P<port>] [-p<profile_path>]"
-rfUsage3 = "[--Host=<hostIP>] [--Port=<port>] [--profile_path=<profile_path>]"
+rfUsage2 = "[-H<hostIP>] [-P<port>] [-C<cert>] [-K<key>] [-p<profile_path>]"
+rfUsage3 = "[--Host=<hostIP>] [--Port=<port>] [--Cert=<cert>] [--Key=<key>] [--profile_path=<profile_path>]"
 
 
 def rf_usage():
@@ -27,18 +35,19 @@ def rf_usage():
 
 def rf_help():
         print(rfProgram1,"implements a simulation of a redfish service for the \"Simple OCP Server V1\" Mockup.")
-        print(" The simulation includes an http server, RestEngine, and dynamic Redfish datamodel.")
+        print(" The simulation includes an http/https server, RestEngine, and dynamic Redfish datamodel.")
         print(" You can GET, PATHCH,... to the service just like a real Redfish service.")
         print(" Both Basic and Redfish Session/Token authentication is supported (for a single user/passwd and token")
         print("    the user/passwd is:   root/password123456.    The authToken is: 123456SESSIONauthcode")
         print("    these can be changed by editing the redfishURIs.py file.  will make dynamic later.")
-        print(" The http service and Rest engine is built on Flask, and all code is Python 3.4+")
+        print(" The http/https service and Rest engine is built on Flask, and all code is Python 3.4+")
         print(" The data model resources are \"initialized\" from the SPMF \"SimpleOcpServerV1\" Mockup.")
         print("     and stored as python dictionaries--then the dictionaries are updated with patches, posts, deletes.")
         print(" The program can be extended to support other mockup \"profiles\".")
         print("")
-        print(" By default, the simulation runs on localhost (127.0.0.1), on port 5000.")
-        print(" These can be changed with CLI options: -P<port> -H <hostIP>  | --port=<port> --host=<hostIp>")
+        print(" By default, the simulation runs over http, on localhost (127.0.0.1), on port 5000.")
+        print(" These can be changed with CLI options: -P<port> -C<cert> -K<key> -H <hostIP> | --port=<port> --Cert=<cert> --Key=<key> --host=<hostIp>")
+        print(" -C<cert> -K<key> | --Cert=<cert> --Key=<key> options must be used together with port 443 to enable https session.")
         print("")
         print("Version: ", rfVersion)
         rf_usage()
@@ -47,19 +56,69 @@ def rf_help():
         print("       -h,          --help,                          --- help")
         print("       -H<hostIP>,  --Host=<hostIp>                  --- host IP address. dflt=127.0.0.1")
         print("       -P<port>,    --Port=<port>                    --- the port to use. dflt=5000")
+        print("       -C<cert>,    --Cert=<cert>                    --- Server certificate.")
+        print("       -K<key>,     --Key=<key>                      --- Server key.")
         print("       -p<profile_path>, --profile=<profile_path>    --- the path to the Redfish profile to use. "
               "dflt=\"./MockupData/SimpleOcpServerV1\" ")
 
+# Conditional Requests with ETags
+# http://flask.pocoo.org/snippets/95/
+def conditional(func):
+    '''Start conditional method execution for this resource'''
+    @functools.wraps(func)
+    def wrapper(*args, **kwargs):
+        flask.g.condtnl_etags_start = True
+        return func(*args, **kwargs)
+    return wrapper
+
+class NotModified(werkzeug.exceptions.HTTPException):
+    code = 304
+    def get_response(self, environment):
+        return flask.Response(status=304)
+
+class PreconditionRequired(werkzeug.exceptions.HTTPException):
+    code = 428
+    description = ('<p>This request is required to be '
+                   'conditional; try using "If-Match".')
+    name = 'Precondition Required'
+    def get_response(self, environment):
+        resp = super(PreconditionRequired,
+                     self).get_response(environment)
+        resp.status = str(self.code) + ' ' + self.name.upper()
+        return resp
 
 def main(argv):
+    #Monkey patch the set_etag() method for conditional request.
+    _old_set_etag = werkzeug.ETagResponseMixin.set_etag
+    @functools.wraps(werkzeug.ETagResponseMixin.set_etag)
+    def _new_set_etag(self, etag, weak=False):
+        # only check the first time through; when called twice
+        # we're modifying
+        if (hasattr(flask.g, 'condtnl_etags_start') and
+                                   flask.g.condtnl_etags_start):
+            if flask.request.method in ('PUT', 'DELETE', 'PATCH'):
+                if not flask.request.if_match:
+                    raise PreconditionRequired
+                if etag not in flask.request.if_match:
+                    flask.abort(412)
+            elif (flask.request.method == 'GET' and
+                  flask.request.if_none_match and
+                  etag in flask.request.if_none_match):
+                raise NotModified
+            flask.g.condtnl_etags_start = False
+        _old_set_etag(self, etag, weak)
+    werkzeug.ETagResponseMixin.set_etag = _new_set_etag
+
     # set default option args
     rf_profile_path = os.path.abspath("./MockupData/SimpleOcpServerV1")
-    rf_host = "127.0.0.1"
+    rf_host = "0.0.0.0"
     rf_port = 5000
+    rf_cert =""
+    rf_key=""
 
     try:
-        opts, args = getopt.getopt(argv[1:], "VhH:P:p:",
-                                   ["Version", "help", "Host=", "Port=", "profile="])
+        opts, args = getopt.getopt(argv[1:], "VhH:P:C:K:p:",
+                                   ["Version", "help", "Host=", "Port=", "Cert=", "Key=", "profile="])
     except getopt.GetoptError:
         print(rfProgram1, ":  Error parsing options")
         rf_usage()
@@ -77,11 +136,24 @@ def main(argv):
             rf_host = arg
         elif opt in "--Port=":
             rf_port=int(arg)
+        elif opt in "--Cert=":
+            rf_cert=arg
+        elif opt in "--Key=":
+            rf_key=arg
         else:
             print("  ", rfProgram1, ":  Error: unsupported option")
             rf_usage()
             sys.exit(2)
 
+    if rf_port == 443:
+        if rf_cert == "" or rf_key == "":
+            print("  ", rfProgram1, ":  Error: port 443 must be used together with -C<cert> and -K<key> to enable https session")
+            sys.exit(2)
+    else:
+        if rf_cert != "" or rf_key != "":
+            print("  ", rfProgram1, ":  Error: -C<cert> and -K<key> options must be used together with port 443 to enable https session")
+            sys.exit(2)
+
     print("{} Version: {}".format(rfProgram1,rfVersion))
     print("   Starting redfishProfileSimulator at:  hostIP={},  port={}".format(rf_host, rf_port))
     print("   Using Profile at {}".format(rf_profile_path))
@@ -102,7 +174,7 @@ def main(argv):
         root = RfServiceRoot(rf_profile_path, root_path)
 
         # start the flask REST API service
-        rfApi_SimpleServer(root, versions, host=rf_host, port=rf_port)
+        rfApi_SimpleServer(root, versions, host=rf_host, port=rf_port, cert=rf_cert, key=rf_key)
     else:
         print("invalid profile path")
 
diff --git a/RedfishClientPkg/Tools/Redfish-Profile-Simulator/v1sim/redfishURIs.py b/RedfishClientPkg/Tools/Redfish-Profile-Simulator/v1sim/redfishURIs.py
index 2380a4058a..3c912f7ce1 100644
--- a/RedfishClientPkg/Tools/Redfish-Profile-Simulator/v1sim/redfishURIs.py
+++ b/RedfishClientPkg/Tools/Redfish-Profile-Simulator/v1sim/redfishURIs.py
@@ -1,17 +1,23 @@
+#
+# Copyright Notice:
+# Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
 # Copyright Notice:
 # Copyright 2016 Distributed Management Task Force, Inc. All rights reserved.
 # License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/Redfish-Profile-Simulator/blob/master/LICENSE.md
 
 import json
+from collections import OrderedDict
 
 from flask import Flask
 from flask import request
 
 from .flask_redfish_auth import RfHTTPBasicOrTokenAuth
-from .resource import RfResource, RfResourceRaw, RfCollection
 
+from werkzeug.serving import WSGIRequestHandler
 
-def rfApi_SimpleServer(root, versions, host="127.0.0.1", port=5000):
+def rfApi_SimpleServer(root, versions, host="127.0.0.1", port=5000, cert="", key=""):
     app = Flask(__name__)
 
     # create auth class that does basic or redifish session auth
@@ -21,8 +27,8 @@ def rfApi_SimpleServer(root, versions, host="127.0.0.1", port=5000):
     # for basic auth, we only support user=catfish, passwd=hunter
     @auth.verify_basic_password
     def verify_rf_passwd(user, passwd):
-        if user == "root":
-            if passwd == "password123456":
+        if user == "admin":
+            if passwd == "pwd123456":
                 return True
         return False
 
@@ -43,13 +49,13 @@ def rfApi_SimpleServer(root, versions, host="127.0.0.1", port=5000):
 
     # GET /redfish
     @app.route("/redfish", methods=['GET'])
-    @app.route("/redfish/", methods=['GET'])
+    #@app.route("/redfish/", methods=['GET'])
     def rf_versions():
         return versions.get_resource()
 
     # GET /redfish/v1
     @app.route("/redfish/v1", methods=['GET'])
-    @app.route("/redfish/v1/", methods=['GET'])
+    #@app.route("/redfish/v1/", methods=['GET'])
     def rf_service_root():
         return root.get_resource()
 
@@ -65,8 +71,9 @@ def rfApi_SimpleServer(root, versions, host="127.0.0.1", port=5000):
         return resolve_path(root, rf_path)
 
     @app.route("/redfish/v1/<path:rf_path>", methods=['GET'])
-    @app.route("/redfish/v1/<path:rf_path>/", methods=['GET'])
+    #@app.route("/redfish/v1/<path:rf_path>/", methods=['GET'])
     @auth.rfAuthRequired
+    @conditional
     def rf_subsystems(rf_path):
         return resolve_path(root, rf_path)
 
@@ -78,135 +85,189 @@ def rfApi_SimpleServer(root, versions, host="127.0.0.1", port=5000):
         return root.get_resource()
 
     @app.route("/redfish/v1/Systems/<path:sys_path>", methods=['PATCH'])
-    @app.route("/redfish/v1/Systems/<path:sys_path>/", methods=['PATCH'])
+    #@app.route("/redfish/v1/Systems/<path:sys_path>/", methods=['PATCH'])
     @auth.rfAuthRequired
     def rf_computer_systempatch(sys_path):
-        rdata = request.get_json(cache=True)
+        rdata = json.loads(request.data,object_pairs_hook=OrderedDict)
         print("rdata:{}".format(rdata))
-        obj = patch_path(root.systems, sys_path)
+        obj = patch_path(root.components['Systems'], sys_path)
         rc, status_code, err_string, resp = obj.patch_resource(rdata)
         if rc == 0:
-            return "", status_code
+            return resp, status_code
+        else:
+            return err_string, status_code
+
+    @app.route("/redfish/v1/Systems/<string:system_id>/BootOptions/<string:bootOptIdx>", methods=['GET'])
+    @auth.rfAuthRequired
+    def rf_computer_bootoptions_get(system_id, bootOptIdx):
+        return root.components['Systems'].get_element(system_id).components['BootOptions'].get_bootOpt(bootOptIdx)
+
+    @app.route("/redfish/v1/Systems/<string:system_id>/BootOptions/<string:bootOptIdx>", methods=['DELETE'])
+    @auth.rfAuthRequired
+    def rf_computer_bootoptions_del(system_id, bootOptIdx):
+        print("in rf_computer_bootoptions_del")
+        rc, status_code, err_string, resp =  root.components['Systems'].get_element(system_id).components['BootOptions'].delete_bootOpt(bootOptIdx)
+        if rc == 0:
+            return resp, status_code
+        else:
+            return err_string, status_code
+
+    @app.route("/redfish/v1/Systems/<string:system_id>/BootOptions/<string:bootOptIdx>", methods=['PATCH'])
+    @auth.rfAuthRequired
+    def rf_computer_bootoption_patch(system_id, bootOptIdx):
+        print ("in POST boot options")
+        rdata = json.loads(request.data,object_pairs_hook=OrderedDict)
+        print("rdata:{}".format(rdata))
+        rc, status_code, err_string, resp = root.components['Systems'].get_element(system_id).components['BootOptions'].patch_bootOpt(bootOptIdx, rdata)
+        if rc == 0:
+            return resp, status_code
+        else:
+            return err_string, status_code
+
+    @app.route("/redfish/v1/Systems/<string:system_id>/BootOptions", methods=['POST'])
+    @auth.rfAuthRequired
+    def rf_computer_bootoptions_post(system_id):
+        print ("in POST boot options")
+        rdata = json.loads(request.data,object_pairs_hook=OrderedDict)
+        print("rdata:{}".format(rdata))
+        rc, status_code, err_string, resp = root.components['Systems'].get_element(system_id).components['BootOptions'].post_resource(rdata)
+        if rc == 0:
+            return resp, status_code
         else:
             return err_string, status_code
 
     @app.route("/redfish/v1/Systems/<string:system_id>/Actions/ComputerSystem.Reset", methods=['POST'])
-    @app.route("/redfish/v1/Systems/<string:system_id>/Actions/ComputerSystem.Reset/", methods=['POST'])
+    #@app.route("/redfish/v1/Systems/<string:system_id>/Actions/ComputerSystem.Reset/", methods=['POST'])
     @auth.rfAuthRequired
     def rf_computer_systemreset(system_id):
         # print("in reset")
-        rdata = request.get_json(cache=True)
+        rdata = json.loads(request.data,object_pairs_hook=OrderedDict)
         # print("rdata:{}".format(rdata))
         rc, status_code, err_string, resp = root.components['Systems'].get_element(system_id).reset_resource(rdata)
         if rc == 0:
-            return "", status_code
+            return resp, status_code
         else:
             return err_string, status_code
 
-    @app.route("/redfish/v1/Systems/<string:system_id>/bios/Actions/Bios.ResetBios", methods=['POST'])
-    @app.route("/redfish/v1/Systems/<string:system_id>/bios/Actions/Bios.ResetBios/", methods=['POST'])
+    @app.route("/redfish/v1/Systems/<string:system_id>/Bios/Actions/Bios.ResetBios", methods=['POST'])
+    #@app.route("/redfish/v1/Systems/<string:system_id>/Bios/Actions/Bios.ResetBios/", methods=['POST'])
     @auth.rfAuthRequired
     def rf_computer_biosreset(system_id):
         # print("in reset")
-        rdata = request.get_json(cache=True)
+        rdata = json.loads(request.data,object_pairs_hook=OrderedDict)
         # print("rdata:{}".format(rdata))
         system = root.systems.get_element(system_id)
-        bios = system.get_component("bios")
+        bios = system.get_component("Bios")
         rc, status_code, err_string, resp = bios.reset_resource(rdata)
         if rc == 0:
-            return "", status_code
+            return resp, status_code
         else:
             return err_string, status_code
 
-    @app.route("/redfish/v1/Systems/<string:system_id>/bios/Actions/Bios.ChangePassword", methods=['PATCH'])
-    @app.route("/redfish/v1/Systems/<string:system_id>/bios/Actions/Bios.ChangePassword/", methods=['PATCH'])
+    @app.route("/redfish/v1/Systems/<string:system_id>/Bios/Actions/Bios.ChangePassword", methods=['PATCH'])
+    #@app.route("/redfish/v1/Systems/<string:system_id>/Bios/Actions/Bios.ChangePassword/", methods=['PATCH'])
     @auth.rfAuthRequired
     def rf_computer_change_pswd(system_id):
         # print("in reset")
-        rdata = request.get_json(cache=True)
+        rdata = json.loads(request.data,object_pairs_hook=OrderedDict)
         # print("rdata:{}".format(rdata))
         system = root.systems.get_element(system_id)
-        bios = system.get_component("bios")
+        bios = system.get_component("Bios")
         rc, status_code, err_string, resp = bios.change_password(rdata)
         if rc == 0:
-            return "", status_code
+            return resp, status_code
         else:
             return err_string, status_code
 
     @app.route("/redfish/v1/Chassis/<string:chassis_id>/Actions/Chassis.Reset", methods=['POST'])
-    @app.route("/redfish/v1/Chassis/<string:chassis_id>/Actions/Chassis.Reset/", methods=['POST'])
+    #@app.route("/redfish/v1/Chassis/<string:chassis_id>/Actions/Chassis.Reset/", methods=['POST'])
     @auth.rfAuthRequired
     def rf_computer_chassisreset(chassis_id):
         # print("in reset")
-        rdata = request.get_json(cache=True)
+        rdata = json.loads(request.data,object_pairs_hook=OrderedDict)
         # print("rdata:{}".format(rdata))
         rc, status_code, err_string, resp = root.chassis.get_element(chassis_id).reset_resource(rdata)
         if rc == 0:
-            return "", status_code
+            return resp, status_code
         else:
             return err_string, status_code
 
     @app.route("/redfish/v1/Chassis/<string:chassis_id>/Power", methods=['PATCH'])
-    @app.route("/redfish/v1/Chassis/<string:chassis_id>/Power/", methods=['PATCH'])
+    #@app.route("/redfish/v1/Chassis/<string:chassis_id>/Power/", methods=['PATCH'])
     @auth.rfAuthRequired
     def rf_chassis_powerpatch(chassis_id):
         # rawdata=request.data
-        rdata = request.get_json(cache=True)
+        rdata = json.loads(request.data,object_pairs_hook=OrderedDict)
         # print("RRrdata:{}".format(rdata))
         rc, status_code, err_string, resp = root.chassis.get_element(chassis_id).power.patch_resource(rdata)
+        if rc == 0:
+            return resp, status_code
+        else:
+            return err_string, status_code
+
+    @app.route("/redfish/v1/Registries/<path:sys_path>", methods=['PATCH'])
+    @auth.rfAuthRequired
+    def rf_registries_patch(sys_path):
+        rdata = json.loads(request.data,object_pairs_hook=OrderedDict)
+        print("rdata:{}".format(rdata))
+        obj = patch_path(root.components['Registries'], sys_path)
+        rc, status_code, err_string, resp = obj.patch_resource(rdata)
         if rc == 0:
             return "", status_code
         else:
             return err_string, status_code
 
     @app.route("/redfish/v1/Managers/<string:manager_id>", methods=['PATCH'])
-    @app.route("/redfish/v1/Managers/<string:manager_id>/", methods=['PATCH'])
+    #@app.route("/redfish/v1/Managers/<string:manager_id>/", methods=['PATCH'])
     @auth.rfAuthRequired
     def rf_patch_manager_entity(manager_id):
-        rdata = request.get_json(cache=True)
+        rdata = json.loads(request.data,object_pairs_hook=OrderedDict)
         # print("RRrdata:{}".format(rdata))
         rc, status_code, err_string, resp = root.managers.get_element(manager_id).patch_resource(rdata)
         if rc == 0:
-            return "", status_code
+            return resp, status_code
         else:
             return err_string, status_code
 
     # rest/v1/Managers/1
     @app.route("/redfish/v1/Managers/<string:manager_id>/Actions/Manager.Reset", methods=['POST'])
-    @app.route("/redfish/v1/Managers/<string:manager_id>/Actions/Manager.Reset/", methods=['POST'])
+    #@app.route("/redfish/v1/Managers/<string:manager_id>/Actions/Manager.Reset/", methods=['POST'])
     @auth.rfAuthRequired
     def rf_reset_manager(manager_id):
-        rdata = request.get_json(cache=True)
+        rdata = json.loads(request.data,object_pairs_hook=OrderedDict)
         # print("rdata:{}".format(rdata))
         rc, status_code, err_string, resp = root.managers.get_element(manager_id).reset_resource(rdata)
         if rc == 0:
-            return "", status_code
+            return resp, status_code
         else:
             return err_string, status_code
 
     @app.route("/redfish/v1/Managers/<string:manager_id>/EthernetInterfaces/<string:eth_id>", methods=['PATCH'])
-    @app.route("/redfish/v1/Managers/<string:manager_id>/EthernetInterfaces/<string:eth_id>/", methods=['PATCH'])
+    #@app.route("/redfish/v1/Managers/<string:manager_id>/EthernetInterfaces/<string:eth_id>/", methods=['PATCH'])
     @auth.rfAuthRequired
     def rf_patch_manager_nic_entity(manager_id, eth_id):
         resp = root.managers.get_element(manager_id).ethernetColl.get_interface(eth_id).get_resource()
-        rdata = request.get_json(cache=True)
+        rdata = json.loads(request.data,object_pairs_hook=OrderedDict)
         # print("RRrdata:{}".format(rdata))
         ethernet_coll = root.managers.get_element(manager_id).ethernetColl
         rc, status_code, err_string, resp = ethernet_coll.get_interface(eth_id).patch_resource(rdata)
         if rc == 0:
-            return "", status_code
+            return resp, status_code
         else:
             return err_string, status_code
 
+    @app.route("/redfish/v1/SessionService", methods=['GET'])
+    def rf_get_session_service():
+        return root.components['SessionService'].get_resource()
+
     @app.route("/redfish/v1/SessionService", methods=['PATCH'])
-    @app.route("/redfish/v1/SessionService/", methods=['PATCH'])
-    @auth.rfAuthRequired
+    #@app.route("/redfish/v1/SessionService/", methods=['PATCH'])
     def rf_patch_session_service():
-        rdata = request.get_json(cache=True)
+        rdata = json.loads(request.data,object_pairs_hook=OrderedDict)
         # print("RRrdata:{}".format(rdata))
         rc, status_code, err_string, resp = root.sessionService.patch_resource(rdata)
         if rc == 0:
-            return "", status_code
+            return resp, status_code
         else:
             return err_string, status_code
 
@@ -215,7 +276,7 @@ def rfApi_SimpleServer(root, versions, host="127.0.0.1", port=5000):
     @app.route("/redfish/v1/SessionService/Sessions", methods=['POST'])
     def rf_login():
         print("login")
-        rdata = request.get_json(cache=True)
+        rdata = json.loads(request.data,object_pairs_hook=OrderedDict)
         print("rdata:{}".format(rdata))
         if rdata["UserName"] == "root" and rdata["Password"] == "password123456":
             x = {"Id": "SESSION123456"}
@@ -233,17 +294,17 @@ def rfApi_SimpleServer(root, versions, host="127.0.0.1", port=5000):
     @auth.rfAuthRequired
     def rf_session_logout(session_id):
         print("session logout %s" % session_id)
-        # rdata=request.get_json(cache=True)
+        # rdata = json.loads(request.data,object_pairs_hook=OrderedDict)
         # print("rdata:{}".format(rdata))
         return "", 204
 
     @app.route("/redfish/v1/AccountService", methods=['PATCH'])
     @auth.rfAuthRequired
     def rf_patch_account_service():
-        rdata = request.get_json(cache=True)
+        rdata = json.loads(request.data,object_pairs_hook=OrderedDict)
         rc, status_code, err_string, resp = root.accountService.patch_resource(rdata)
         if rc == 0:
-            return "", status_code
+            return resp, status_code
         else:
             return err_string, status_code
 
@@ -293,12 +354,14 @@ def rfApi_SimpleServer(root, versions, host="127.0.0.1", port=5000):
     '''
 
     # END file redfishURIs
-
     # start Flask REST engine running
-    app.run(host=host, port=port)
 
-    # never returns
+    if key != "" and cert != "":
+        app.run(host=host, port=port, ssl_context=(cert, key))
+    else:
+        app.run(host=host, port=port)
 
+    # never returns
 
 '''
 reference source links:
diff --git a/RedfishClientPkg/Tools/Redfish-Profile-Simulator/v1sim/registry.py b/RedfishClientPkg/Tools/Redfish-Profile-Simulator/v1sim/registry.py
new file mode 100644
index 0000000000..9cfbb30cde
--- /dev/null
+++ b/RedfishClientPkg/Tools/Redfish-Profile-Simulator/v1sim/registry.py
@@ -0,0 +1,14 @@
+#
+# Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+
+from .resource import RfResource, RfCollection
+
+class RfRegistryCollection(RfCollection):
+    def element_type(self):
+        return RfRegistry
+
+#subclass Bios
+class RfRegistry(RfResource):
+    pass
diff --git a/RedfishClientPkg/Tools/Redfish-Profile-Simulator/v1sim/resource.py b/RedfishClientPkg/Tools/Redfish-Profile-Simulator/v1sim/resource.py
index 6fee348064..ca7541f172 100644
--- a/RedfishClientPkg/Tools/Redfish-Profile-Simulator/v1sim/resource.py
+++ b/RedfishClientPkg/Tools/Redfish-Profile-Simulator/v1sim/resource.py
@@ -1,6 +1,13 @@
+#
+# Copyright Notice:
+#
+# Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
 # Copyright Notice:
 # Copyright 2016 Distributed Management Task Force, Inc. All rights reserved.
 # License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/Redfish-Profile-Simulator/blob/master/LICENSE.md
+#
 
 import json
 import os
@@ -23,7 +30,7 @@ class RfResource:
         if os.path.exists(indx_file_path):
             res_file = open(indx_file_path, "r")
             res_rawdata = res_file.read()
-            self.res_data = json.loads(res_rawdata)
+            self.res_data = json.loads(res_rawdata,object_pairs_hook=OrderedDict)
             self.create_sub_objects(base_path, rel_path)
             self.final_init_processing(base_path, rel_path)
         else:
@@ -36,7 +43,15 @@ class RfResource:
         pass
 
     def get_resource(self):
-        return flask.jsonify(self.res_data)
+        self.response=json.dumps(self.res_data,indent=4)
+        try:
+            # SHA1 should generate well-behaved etags
+            response = flask.make_response(self.response)
+            etag = hashlib.sha1(self.response.encode('utf-8')).hexdigest()
+            response.set_etag(etag)
+            return response
+        except KeyError:
+            flask.abort(404)
 
     def get_attribute(self, attribute):
         return flask.jsonify(self.res_data[attribute])
@@ -54,6 +69,14 @@ class RfResource:
             else:
                 raise Exception("attribute %s not found" % key)
 
+        resp = flask.Response(json.dumps(self.res_data,indent=4))
+        return 0, 200, None, resp
+
+    def post_resource(self, post_data):
+        pass
+
+    def delete_resource(self):
+        pass
 
 class RfResourceRaw:
     def __init__(self, base_path, rel_path, parent=None):
diff --git a/RedfishClientPkg/Tools/Redfish-Profile-Simulator/v1sim/systems.py b/RedfishClientPkg/Tools/Redfish-Profile-Simulator/v1sim/systems.py
index b107f035db..b8b3788054 100644
--- a/RedfishClientPkg/Tools/Redfish-Profile-Simulator/v1sim/systems.py
+++ b/RedfishClientPkg/Tools/Redfish-Profile-Simulator/v1sim/systems.py
@@ -1,6 +1,13 @@
+#
+# Copyright Notice:
+#
+# Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
 # Copyright Notice:
 # Copyright 2016 Distributed Management Task Force, Inc. All rights reserved.
 # License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/Redfish-Profile-Simulator/blob/master/LICENSE.md
+#
 
 import os
 
@@ -8,7 +15,9 @@ from .common_services import RfLogServiceCollection
 from .network import RfEthernetCollection, RfNetworkInterfaceCollection
 from .resource import RfResource, RfCollection
 from .storage import RfSimpleStorageCollection, RfSmartStorage
-
+import flask
+import json
+from collections import OrderedDict
 
 class RfSystemsCollection(RfCollection):
     def element_type(self):
@@ -48,15 +57,17 @@ class RfSystemObj(RfResource):
                 self.components[item] = RfUSBDeviceCollection(base_path, os.path.join(rel_path, item), parent=self)
             elif item == "USBPorts":
                 self.components[item] = RfUSBPortCollection(base_path, os.path.join(rel_path, item), parent=self)
+            elif item == "BootOptions":
+                self.components[item] = RfBootOptionCollection(base_path, os.path.join(rel_path, item), parent=self)
 
     def patch_resource(self, patch_data):
         # first verify client didn't send us a property we cant patch
         for key in patch_data.keys():
-            if key != "AssetTag" and key != "IndicatorLED" and key != "Boot":
+            if key != "AssetTag" and key != "IndicatorLED" and key != "Boot" and key != "BiosVersion":
                 return 4, 400, "Invalid Patch Property Sent", ""
             elif key == "Boot":
                 for prop2 in patch_data["Boot"].keys():
-                    if prop2 != "BootSourceOverrideEnabled" and prop2 != "BootSourceOverrideTarget":
+                    if prop2 != "BootSourceOverrideEnabled" and prop2 != "BootSourceOverrideTarget" and prop2 != "BootNext" and prop2 != "BootOrder":
                         return 4, 400, "Invalid Patch Property Sent", ""
         # now patch the valid properties sent
         if "AssetTag" in patch_data:
@@ -64,6 +75,8 @@ class RfSystemObj(RfResource):
             self.res_data['AssetTag'] = patch_data['AssetTag']
         if "IndicatorLED" in patch_data:
             self.res_data['IndicatorLED'] = patch_data['IndicatorLED']
+        if "BiosVersion" in patch_data:
+            self.res_data['BiosVersion'] = patch_data['BiosVersion']
         if "Boot" in patch_data:
             boot_data = patch_data["Boot"]
             if "BootSourceOverrideEnabled" in boot_data:
@@ -80,7 +93,13 @@ class RfSystemObj(RfResource):
                     self.res_data['Boot']['BootSourceOverrideTarget'] = value
                 else:
                     return 4, 400, "Invalid_Value_Specified: BootSourceOverrideTarget", ""
-        return 0, 204, None, None
+            if "BootNext" in boot_data:
+                self.res_data['Boot']['BootNext'] = boot_data['BootNext']
+            if "BootOrder" in boot_data:
+                self.res_data['Boot']['BootOrder'] = boot_data['BootOrder']
+
+        resp = flask.Response(json.dumps(self.res_data,indent=4))
+        return 0, 200, None, resp
 
     def reset_resource(self, reset_data):
         if "ResetType" in reset_data:
@@ -145,13 +164,17 @@ class RfBiosSettings(RfResource):
     def patch_resource(self, patch_data):
         if "Attributes" not in patch_data:
             return 4, 400, "Invalid Payload. No Attributes found", ""
+        self.res_data["Attributes"] = OrderedDict()
         for key in patch_data["Attributes"].keys():
+            print("Check key in patch_data:{}".format(key))
             # verify client didn't send us a property we cant patch
-            if key not in self.res_data["Attributes"]:
+            if key not in self.parent.res_data["Attributes"]:
+                print("Invalid Patch Property Sent")
                 return 4, 400, "Invalid Patch Property Sent", ""
             else:
-                self.parent.res_data["Attributes"][key] = patch_data["Attributes"][key]
-        return 0, 204, None, None
+                self.res_data["Attributes"][key] = patch_data["Attributes"][key]
+        resp = flask.Response(json.dumps(self.res_data,indent=4))
+        return 0, 200, None, resp
 
 
 class RfPCIeDeviceCollection(RfCollection):
@@ -196,3 +219,51 @@ class RfUSBPortCollection(RfCollection):
 
 class RfUSBPort(RfResource):
     pass
+
+class RfBootOptionCollection(RfCollection):
+    def final_init_processing(self, base_path, rel_path):
+        self.maxIdx = 0
+        self.bootOptions = {}
+
+    def element_type(self):
+        return RfBootOption
+
+    def post_resource(self, post_data):
+        print("Members at odata.count:{}".format(self.res_data["Members at odata.count"]))
+        print("Members:{}".format(self.res_data["Members"]))
+        print("post_data:{}".format(post_data))
+
+        self.res_data["Members at odata.count"] = self.res_data["Members at odata.count"] + 1
+        self.maxIdx = self.maxIdx + 1
+        newBootOptIdx = self.maxIdx
+        newBootOptUrl = self.res_data["@odata.id"] + "/" + str(newBootOptIdx)
+        self.res_data["Members"].append({"@odata.id":newBootOptUrl})
+
+        post_data["@odata.id"] = newBootOptUrl
+        self.bootOptions[str(newBootOptIdx)] = post_data
+
+        resp = flask.Response(json.dumps(post_data,indent=4))
+        resp.headers["Location"] = newBootOptUrl
+        return 0, 200, None, resp
+
+    def patch_bootOpt(self, Idx, patch_data):
+        self.bootOptions[str(Idx)] = {**self.bootOptions[str(Idx)], **patch_data}
+        resp = flask.Response(json.dumps(self.bootOptions[str(Idx)],indent=4))
+        return 0, 200, None, resp
+
+    def get_bootOpt(self, Idx):
+        return json.dumps(self.bootOptions[Idx],indent=4)
+
+    def delete_bootOpt(self, Idx):
+        print("in delete_bootOpt")
+
+        resp = flask.Response(json.dumps(self.bootOptions[Idx],indent=4))
+
+        self.bootOptions.pop(Idx)
+        self.res_data["Members at odata.count"] = self.res_data["Members at odata.count"] - 1
+
+        bootOptUrl = self.res_data["@odata.id"] + "/" + str(Idx)
+        self.res_data["Members"].remove({"@odata.id":bootOptUrl})
+        return 0, 200, None, resp
+
+class RfBootOption(RfResource):
-- 
2.17.1



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#78064): https://edk2.groups.io/g/devel/message/78064
Mute This Topic: https://groups.io/mt/84374362/1813853
Group Owner: devel+owner at edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [edk2-devel-archive at redhat.com]
-=-=-=-=-=-=-=-=-=-=-=-





More information about the edk2-devel-archive mailing list