* [edk2-redfish-client][PATCH 1/4] Redfish-Profile-Simulator: Add more features
@ 2023-05-02 7:50 Nickle Wang
0 siblings, 0 replies; only message in thread
From: Nickle Wang @ 2023-05-02 7:50 UTC (permalink / raw)
To: devel; +Cc: Abner Chang, Igor Kulchytskyy
- Add HTTPs support
- Add ETAG support
- Change default credential to admin/pwd123456
- Add HTTP methods on BIOS managed resource.
Signed-off-by: Nickle Wang <nicklew@nvidia.com>
Cc: Abner Chang <abner.chang@amd.com>
Cc: Igor Kulchytskyy <igork@ami.com>
---
.../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 Tools/Redfish-Profile-Simulator/v1sim/registry.py
diff --git a/Tools/Redfish-Profile-Simulator/redfishProfileSimulator.py b/Tools/Redfish-Profile-Simulator/redfishProfileSimulator.py
index 24be52b..91c792a 100644
--- a/Tools/Redfish-Profile-Simulator/redfishProfileSimulator.py
+++ b/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/Tools/Redfish-Profile-Simulator/v1sim/redfishURIs.py b/Tools/Redfish-Profile-Simulator/v1sim/redfishURIs.py
index 2380a40..3c912f7 100644
--- a/Tools/Redfish-Profile-Simulator/v1sim/redfishURIs.py
+++ b/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/Tools/Redfish-Profile-Simulator/v1sim/registry.py b/Tools/Redfish-Profile-Simulator/v1sim/registry.py
new file mode 100644
index 0000000..9cfbb30
--- /dev/null
+++ b/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/Tools/Redfish-Profile-Simulator/v1sim/resource.py b/Tools/Redfish-Profile-Simulator/v1sim/resource.py
index 6fee348..ca7541f 100644
--- a/Tools/Redfish-Profile-Simulator/v1sim/resource.py
+++ b/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/Tools/Redfish-Profile-Simulator/v1sim/systems.py b/Tools/Redfish-Profile-Simulator/v1sim/systems.py
index b107f03..b8b3788 100644
--- a/Tools/Redfish-Profile-Simulator/v1sim/systems.py
+++ b/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@odata.count:{}".format(self.res_data["Members@odata.count"]))
+ print("Members:{}".format(self.res_data["Members"]))
+ print("post_data:{}".format(post_data))
+
+ self.res_data["Members@odata.count"] = self.res_data["Members@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@odata.count"] = self.res_data["Members@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
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2023-05-02 7:50 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-05-02 7:50 [edk2-redfish-client][PATCH 1/4] Redfish-Profile-Simulator: Add more features Nickle Wang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox