mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2025-12-12 01:00:29 +08:00
basic redfish api
This commit is contained in:
parent
7e874b035d
commit
ccab97a56f
@ -93,3 +93,9 @@ location /streamer {
|
|||||||
proxy_buffering off;
|
proxy_buffering off;
|
||||||
proxy_ignore_headers X-Accel-Buffering;
|
proxy_ignore_headers X-Accel-Buffering;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
location /redfish {
|
||||||
|
proxy_pass http://kvmd;
|
||||||
|
include /etc/kvmd/nginx/loc-proxy.conf;
|
||||||
|
auth_request off;
|
||||||
|
}
|
||||||
|
|||||||
127
kvmd/apps/kvmd/api/redfish.py
Normal file
127
kvmd/apps/kvmd/api/redfish.py
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
# ========================================================================== #
|
||||||
|
# #
|
||||||
|
# KVMD - The main Pi-KVM daemon. #
|
||||||
|
# #
|
||||||
|
# Copyright (C) 2018 Maxim Devaev <mdevaev@gmail.com> #
|
||||||
|
# #
|
||||||
|
# This program is free software: you can redistribute it and/or modify #
|
||||||
|
# it under the terms of the GNU General Public License as published by #
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or #
|
||||||
|
# (at your option) any later version. #
|
||||||
|
# #
|
||||||
|
# This program is distributed in the hope that it will be useful, #
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||||
|
# GNU General Public License for more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the GNU General Public License #
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||||
|
# #
|
||||||
|
# ========================================================================== #
|
||||||
|
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
from aiohttp.web import Request
|
||||||
|
from aiohttp.web import Response
|
||||||
|
|
||||||
|
from ....plugins.atx import BaseAtx
|
||||||
|
|
||||||
|
from ....validators import ValidatorError
|
||||||
|
from ....validators import check_string_in_list
|
||||||
|
|
||||||
|
from ..info import InfoManager
|
||||||
|
|
||||||
|
from ..http import HttpError
|
||||||
|
from ..http import exposed_http
|
||||||
|
from ..http import make_json_response
|
||||||
|
|
||||||
|
|
||||||
|
# =====
|
||||||
|
class RedfishApi:
|
||||||
|
# https://github.com/DMTF/Redfishtool
|
||||||
|
# https://github.com/DMTF/Redfish-Mockup-Server
|
||||||
|
# https://redfish.dmtf.org/redfish/v1
|
||||||
|
# https://www.dmtf.org/documents/redfish-spmf/redfish-mockup-bundle-20191
|
||||||
|
# https://www.dmtf.org/sites/default/files/Redfish_School-Sessions.pdf
|
||||||
|
# https://www.ibm.com/support/knowledgecenter/POWER9/p9ej4/p9ej4_kickoff.htm
|
||||||
|
#
|
||||||
|
# Quick examples:
|
||||||
|
# redfishtool -S Never -u admin -p admin -r localhost:8080 Systems
|
||||||
|
# redfishtool -S Never -u admin -p admin -r localhost:8080 Systems reset ForceOff
|
||||||
|
|
||||||
|
def __init__(self, info_manager: InfoManager, atx: BaseAtx) -> None:
|
||||||
|
self.__info_manager = info_manager
|
||||||
|
self.__atx = atx
|
||||||
|
|
||||||
|
self.__actions = {
|
||||||
|
"On": self.__atx.power_on,
|
||||||
|
"ForceOff": self.__atx.power_off_hard,
|
||||||
|
"GracefulShutdown": self.__atx.power_off,
|
||||||
|
"ForceRestart": self.__atx.power_reset_hard,
|
||||||
|
"ForceOn": self.__atx.power_on,
|
||||||
|
"PushPowerButton": self.__atx.click_power,
|
||||||
|
}
|
||||||
|
|
||||||
|
# =====
|
||||||
|
|
||||||
|
@exposed_http("GET", "/redfish/v1", auth_required=False)
|
||||||
|
async def __root_handler(self, _: Request) -> Response:
|
||||||
|
return make_json_response({
|
||||||
|
"@odata.id": "/redfish/v1",
|
||||||
|
"@odata.type": "#ServiceRoot.v1_6_0.ServiceRoot",
|
||||||
|
"Id": "RootService",
|
||||||
|
"Name": "Root Service",
|
||||||
|
"RedfishVersion": "1.6.0",
|
||||||
|
"Systems": {"@odata.id": "/redfish/v1/Systems"},
|
||||||
|
}, wrap_result=False)
|
||||||
|
|
||||||
|
@exposed_http("GET", "/redfish/v1/Systems")
|
||||||
|
async def __systems_handler(self, _: Request) -> Response:
|
||||||
|
return make_json_response({
|
||||||
|
"@odata.id": "/redfish/v1/Systems",
|
||||||
|
"@odata.type": "#ComputerSystemCollection.ComputerSystemCollection",
|
||||||
|
"Members": [{"@odata.id": "/redfish/v1/Systems/0"}],
|
||||||
|
"Members@odata.count": 1,
|
||||||
|
"Name": "Computer System Collection",
|
||||||
|
}, wrap_result=False)
|
||||||
|
|
||||||
|
@exposed_http("GET", "/redfish/v1/Systems/0")
|
||||||
|
async def __server_handler(self, _: Request) -> Response:
|
||||||
|
(atx_state, meta_state) = await asyncio.gather(*[
|
||||||
|
self.__atx.get_state(),
|
||||||
|
self.__info_manager.get_submanager("meta").get_state(),
|
||||||
|
])
|
||||||
|
try:
|
||||||
|
host = meta_state.get("server", {})["host"]
|
||||||
|
except Exception:
|
||||||
|
host = ""
|
||||||
|
return make_json_response({
|
||||||
|
"@odata.id": "/redfish/v1/Systems/0",
|
||||||
|
"@odata.type": "#ComputerSystem.v1_10_0.ComputerSystem",
|
||||||
|
"Actions": {
|
||||||
|
"#ComputerSystem.Reset": {
|
||||||
|
"ResetType@Redfish.AllowableValues": list(self.__actions),
|
||||||
|
"target": "/redfish/v1/Systems/0/Actions/ComputerSystem.Reset"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"Id": "0",
|
||||||
|
"HostName": host,
|
||||||
|
"PowerState": ("On" if atx_state["leds"]["power"] else "Off"),
|
||||||
|
}, wrap_result=False)
|
||||||
|
|
||||||
|
@exposed_http("POST", "/redfish/v1/Systems/0/Actions/ComputerSystem.Reset")
|
||||||
|
async def __power_handler(self, request: Request) -> Response:
|
||||||
|
try:
|
||||||
|
action = check_string_in_list(
|
||||||
|
arg=(await request.json())["ResetType"],
|
||||||
|
name="Redfish ResetType",
|
||||||
|
variants=set(self.__actions),
|
||||||
|
lower=False,
|
||||||
|
)
|
||||||
|
except Exception as err:
|
||||||
|
if isinstance(err, ValidatorError):
|
||||||
|
raise
|
||||||
|
raise HttpError("Missing Redfish ResetType", 400)
|
||||||
|
await self.__actions[action](False)
|
||||||
|
return Response(body=None, status=204)
|
||||||
@ -117,13 +117,14 @@ def make_json_response(
|
|||||||
result: Optional[Dict]=None,
|
result: Optional[Dict]=None,
|
||||||
status: int=200,
|
status: int=200,
|
||||||
set_cookies: Optional[Dict[str, str]]=None,
|
set_cookies: Optional[Dict[str, str]]=None,
|
||||||
|
wrap_result: bool=True,
|
||||||
) -> aiohttp.web.Response:
|
) -> aiohttp.web.Response:
|
||||||
|
|
||||||
response = aiohttp.web.Response(
|
response = aiohttp.web.Response(
|
||||||
text=json.dumps({
|
text=json.dumps(({
|
||||||
"ok": (status == 200),
|
"ok": (status == 200),
|
||||||
"result": (result or {}),
|
"result": (result or {}),
|
||||||
}, sort_keys=True, indent=4),
|
} if wrap_result else result), sort_keys=True, indent=4),
|
||||||
status=status,
|
status=status,
|
||||||
content_type="application/json",
|
content_type="application/json",
|
||||||
)
|
)
|
||||||
|
|||||||
@ -91,6 +91,7 @@ from .api.atx import AtxApi
|
|||||||
from .api.msd import MsdApi
|
from .api.msd import MsdApi
|
||||||
from .api.streamer import StreamerApi
|
from .api.streamer import StreamerApi
|
||||||
from .api.export import ExportApi
|
from .api.export import ExportApi
|
||||||
|
from .api.redfish import RedfishApi
|
||||||
|
|
||||||
|
|
||||||
# =====
|
# =====
|
||||||
@ -198,6 +199,7 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins
|
|||||||
MsdApi(msd, sync_chunk_size),
|
MsdApi(msd, sync_chunk_size),
|
||||||
StreamerApi(streamer),
|
StreamerApi(streamer),
|
||||||
ExportApi(info_manager, atx, user_gpio),
|
ExportApi(info_manager, atx, user_gpio),
|
||||||
|
RedfishApi(info_manager, atx),
|
||||||
]
|
]
|
||||||
|
|
||||||
self.__ws_handlers: Dict[str, Callable] = {}
|
self.__ws_handlers: Dict[str, Callable] = {}
|
||||||
@ -291,7 +293,11 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins
|
|||||||
super().run(**kwargs)
|
super().run(**kwargs)
|
||||||
|
|
||||||
async def _make_app(self) -> aiohttp.web.Application:
|
async def _make_app(self) -> aiohttp.web.Application:
|
||||||
app = aiohttp.web.Application()
|
app = aiohttp.web.Application(middlewares=[aiohttp.web.normalize_path_middleware(
|
||||||
|
append_slash=False,
|
||||||
|
remove_slash=True,
|
||||||
|
merge_slashes=True,
|
||||||
|
)])
|
||||||
app.on_shutdown.append(self.__on_shutdown)
|
app.on_shutdown.append(self.__on_shutdown)
|
||||||
app.on_cleanup.append(self.__on_cleanup)
|
app.on_cleanup.append(self.__on_cleanup)
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user