diff options
author | Devaev Maxim <[email protected]> | 2020-06-02 20:59:43 +0300 |
---|---|---|
committer | Devaev Maxim <[email protected]> | 2020-06-02 20:59:43 +0300 |
commit | cb9597679d8c9189e671e166de45c707c650bb2f (patch) | |
tree | e0f3e00bafe4c1674f8497e6832f292233d94821 /kvmd | |
parent | fe7c275d1a827f95d40d774e38b928aee493a41b (diff) |
improved info handler
Diffstat (limited to 'kvmd')
-rw-r--r-- | kvmd/apps/__init__.py | 6 | ||||
-rw-r--r-- | kvmd/apps/kvmd/__init__.py | 2 | ||||
-rw-r--r-- | kvmd/apps/kvmd/api/info.py | 41 | ||||
-rw-r--r-- | kvmd/apps/kvmd/info.py | 103 | ||||
-rw-r--r-- | kvmd/apps/kvmd/server.py | 24 | ||||
-rw-r--r-- | kvmd/apps/kvmd/streamer.py | 8 | ||||
-rw-r--r-- | kvmd/apps/vnc/server.py | 18 |
7 files changed, 138 insertions, 64 deletions
diff --git a/kvmd/apps/__init__.py b/kvmd/apps/__init__.py index ad28961b..e81e6656 100644 --- a/kvmd/apps/__init__.py +++ b/kvmd/apps/__init__.py @@ -214,9 +214,9 @@ def _get_config_scheme() -> Dict: }, }, - "info": { - "meta": Option("/etc/kvmd/meta.yaml", type=valid_abs_file, unpack_as="meta_path"), - "extras": Option("/usr/share/kvmd/extras", type=valid_abs_dir, unpack_as="extras_path"), + "info": { # Accessed via global config, see kvmd/info.py for details + "meta": Option("/etc/kvmd/meta.yaml", type=valid_abs_file), + "extras": Option("/usr/share/kvmd/extras", type=valid_abs_dir), }, "wol": { diff --git a/kvmd/apps/kvmd/__init__.py b/kvmd/apps/kvmd/__init__.py index 5d690840..dbad7a90 100644 --- a/kvmd/apps/kvmd/__init__.py +++ b/kvmd/apps/kvmd/__init__.py @@ -72,7 +72,7 @@ def main(argv: Optional[List[str]]=None) -> None: force_internal_users=config.auth.internal.force_users, enabled=config.auth.enabled, ), - info_manager=InfoManager(global_config, **config.info._unpack()), + info_manager=InfoManager(global_config), log_reader=LogReader(), wol=WakeOnLan(**config.wol._unpack()), diff --git a/kvmd/apps/kvmd/api/info.py b/kvmd/apps/kvmd/api/info.py new file mode 100644 index 00000000..3e0220a0 --- /dev/null +++ b/kvmd/apps/kvmd/api/info.py @@ -0,0 +1,41 @@ +# ========================================================================== # +# # +# KVMD - The main Pi-KVM daemon. # +# # +# Copyright (C) 2018 Maxim Devaev <[email protected]> # +# # +# 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/>. # +# # +# ========================================================================== # + + +from aiohttp.web import Request +from aiohttp.web import Response + +from ..info import InfoManager + +from ..http import exposed_http +from ..http import make_json_response + + +# ===== +class InfoApi: + def __init__(self, info_manager: InfoManager) -> None: + self.__info_manager = info_manager + + # ===== + + @exposed_http("GET", "/info") + async def __state_handler(self, _: Request) -> Response: + return make_json_response(await self.__info_manager.get_state()) diff --git a/kvmd/apps/kvmd/info.py b/kvmd/apps/kvmd/info.py index 50ea6eb1..f85a7656 100644 --- a/kvmd/apps/kvmd/info.py +++ b/kvmd/apps/kvmd/info.py @@ -21,9 +21,12 @@ import os +import asyncio +import platform import contextlib from typing import Dict +from typing import Optional import dbus # pylint: disable=import-error import dbus.exceptions @@ -34,35 +37,87 @@ from ...yamlconf import Section from ...yamlconf.loader import load_yaml_file from ... import aiotools +from ... import aioproc + +from ... import __version__ # ===== class InfoManager: - def __init__( - self, - global_config: Section, - meta_path: str, - extras_path: str, - ) -> None: - + def __init__(self, global_config: Section) -> None: self.__global_config = global_config - self.__meta_path = meta_path - self.__extras_path = extras_path - - async def get_meta(self) -> Dict: - return (await aiotools.run_async(load_yaml_file, self.__meta_path)) - - async def get_extras(self) -> Dict: - return (await aiotools.run_async(self.__inner_get_extras)) - - def __inner_get_extras(self) -> Dict: - extras: Dict[str, Dict] = {} - for app in os.listdir(self.__extras_path): - if app[0] != "." and os.path.isdir(os.path.join(self.__extras_path, app)): - extras[app] = load_yaml_file(os.path.join(self.__extras_path, app, "manifest.yaml")) - self.__rewrite_app_daemon(extras[app]) - self.__rewrite_app_port(extras[app]) - return extras + + async def get_state(self) -> Dict: + (streamer_info, meta_info, extras_info) = await asyncio.gather( + self.__get_streamer_info(), + self.__get_meta_info(), + self.__get_extras_info(), + ) + uname_info = platform.uname() # Uname using the internal cache + return { + "system": { + "kvmd": {"version": __version__}, + "streamer": streamer_info, + "kernel": { + field: getattr(uname_info, field) + for field in ["system", "release", "version", "machine"] + }, + }, + "meta": meta_info, + "extras": extras_info, + } + + # ===== + + async def __get_streamer_info(self) -> Dict: + version = "" + features: Dict[str, bool] = {} + try: + path = self.__global_config.kvmd.streamer.cmd[0] + ((_, version), (_, features_text)) = await asyncio.gather( + aioproc.read_process([path, "--version"], err_to_null=True), + aioproc.read_process([path, "--features"], err_to_null=True), + ) + except Exception: + get_logger(0).exception("Can't get streamer info") + else: + try: + for line in features_text.split("\n"): + (status, name) = map(str.strip, line.split(" ")) + features[name] = (status == "+") + except Exception: + get_logger(0).exception("Can't parse streamer features") + return { + "app": os.path.basename(path), + "version": version, + "features": features, + } + + async def __get_meta_info(self) -> Optional[Dict]: + try: + return ((await aiotools.run_async(load_yaml_file, self.__global_config.kvmd.info.meta)) or {}) + except Exception: + get_logger(0).exception("Can't parse meta") + return None + + async def __get_extras_info(self) -> Optional[Dict]: + return (await aiotools.run_async(self.__inner_get_extras_info)) + + # ===== + + def __inner_get_extras_info(self) -> Optional[Dict]: + try: + extras_path = self.__global_config.kvmd.info.extras + extras: Dict[str, Dict] = {} + for app in os.listdir(extras_path): + if app[0] != "." and os.path.isdir(os.path.join(extras_path, app)): + extras[app] = load_yaml_file(os.path.join(extras_path, app, "manifest.yaml")) + self.__rewrite_app_daemon(extras[app]) + self.__rewrite_app_port(extras[app]) + return extras + except Exception: + get_logger(0).exception("Can't parse extras") + return None def __rewrite_app_daemon(self, extras: Dict) -> None: daemon = extras.get("daemon", "") diff --git a/kvmd/apps/kvmd/server.py b/kvmd/apps/kvmd/server.py index 73200cd8..423e77dc 100644 --- a/kvmd/apps/kvmd/server.py +++ b/kvmd/apps/kvmd/server.py @@ -57,8 +57,6 @@ from ...validators.kvm import valid_stream_fps from ... import aiotools -from ... import __version__ - from .auth import AuthManager from .info import InfoManager from .logreader import LogReader @@ -78,6 +76,7 @@ from .http import HttpServer from .api.auth import AuthApi from .api.auth import check_request_auth +from .api.info import InfoApi from .api.log import LogApi from .api.wol import WolApi from .api.hid import HidApi @@ -129,6 +128,7 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins self.__apis: List[object] = [ self, AuthApi(auth_manager), + InfoApi(info_manager), LogApi(log_reader), WolApi(wol), HidApi(hid, keymap_path), @@ -148,24 +148,6 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins self.__reset_streamer = False self.__new_streamer_params: Dict = {} - async def __make_info(self) -> Dict: - streamer_info = await self.__streamer.get_info() - return { - "version": { - "kvmd": __version__, - "streamer": streamer_info["version"], - }, - "streamer": streamer_info["app"], - "meta": await self.__info_manager.get_meta(), - "extras": await self.__info_manager.get_extras(), - } - - # ===== SYSTEM - - @exposed_http("GET", "/info") - async def __info_handler(self, _: aiohttp.web.Request) -> aiohttp.web.Response: - return make_json_response(await self.__make_info()) - # ===== STREAMER CONTROLLER @exposed_http("POST", "/streamer/set_params") @@ -198,7 +180,7 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins await self.__register_socket(ws) try: await asyncio.gather(*[ - self.__broadcast_event(_Events.INFO_STATE, (await self.__make_info())), + self.__broadcast_event(_Events.INFO_STATE, (await self.__info_manager.get_state())), self.__broadcast_event(_Events.WOL_STATE, self.__wol.get_state()), self.__broadcast_event(_Events.HID_STATE, (await self.__hid.get_state())), self.__broadcast_event(_Events.ATX_STATE, self.__atx.get_state()), diff --git a/kvmd/apps/kvmd/streamer.py b/kvmd/apps/kvmd/streamer.py index 26d14b62..2ef1f72c 100644 --- a/kvmd/apps/kvmd/streamer.py +++ b/kvmd/apps/kvmd/streamer.py @@ -20,7 +20,6 @@ # ========================================================================== # -import os import signal import asyncio import asyncio.subprocess @@ -246,13 +245,6 @@ class Streamer: # pylint: disable=too-many-instance-attributes # ===== - async def get_info(self) -> Dict: - version = (await aioproc.read_process([self.__cmd[0], "--version"], err_to_null=True))[1] - return { - "app": os.path.basename(self.__cmd[0]), - "version": version, - } - async def make_snapshot(self, save: bool, load: bool, allow_offline: bool) -> Optional[StreamerSnapshot]: if load: return self.__snapshot diff --git a/kvmd/apps/vnc/server.py b/kvmd/apps/vnc/server.py index a5517c11..4d28555f 100644 --- a/kvmd/apps/vnc/server.py +++ b/kvmd/apps/vnc/server.py @@ -150,13 +150,17 @@ class _Client(RfbClient): # pylint: disable=too-many-instance-attributes async def __process_ws_event(self, event: Dict) -> None: if event["event_type"] == "info_state": - host = event["event"]["meta"].get("server", {}).get("host") - if isinstance(host, str): - name = f"Pi-KVM: {host}" - async with self.__lock: - if self._encodings.has_rename: - await self._send_rename(name) - self.__shared_params.name = name + try: + host = event["event"]["meta"]["server"]["host"] + except Exception: + host = None + else: + if isinstance(host, str): + name = f"Pi-KVM: {host}" + async with self.__lock: + if self._encodings.has_rename: + await self._send_rename(name) + self.__shared_params.name = name elif event["event_type"] == "hid_state": async with self.__lock: |