diff options
author | Devaev Maxim <[email protected]> | 2020-07-09 09:41:05 +0300 |
---|---|---|
committer | Devaev Maxim <[email protected]> | 2020-07-09 09:41:05 +0300 |
commit | 5f1733d0024c70ccaf31964980a6286a7951cbed (patch) | |
tree | b9074dc235835a214b66be2405faa777245a2e3f /kvmd | |
parent | 53eb74670d040c9e8745433a2641f18796da365c (diff) |
complex info handle
Diffstat (limited to 'kvmd')
-rw-r--r-- | kvmd/apps/__init__.py | 15 | ||||
-rw-r--r-- | kvmd/apps/kvmd/__init__.py | 2 | ||||
-rw-r--r-- | kvmd/apps/kvmd/api/info.py | 24 | ||||
-rw-r--r-- | kvmd/apps/kvmd/info/__init__.py | 48 | ||||
-rw-r--r-- | kvmd/apps/kvmd/info/base.py (renamed from kvmd/apps/kvmd/api/hw.py) | 21 | ||||
-rw-r--r-- | kvmd/apps/kvmd/info/extras.py (renamed from kvmd/apps/kvmd/info.py) | 76 | ||||
-rw-r--r-- | kvmd/apps/kvmd/info/hw.py (renamed from kvmd/apps/kvmd/hw.py) | 8 | ||||
-rw-r--r-- | kvmd/apps/kvmd/info/meta.py | 45 | ||||
-rw-r--r-- | kvmd/apps/kvmd/info/system.py | 80 | ||||
-rw-r--r-- | kvmd/apps/kvmd/server.py | 28 | ||||
-rw-r--r-- | kvmd/apps/vnc/server.py | 4 |
11 files changed, 239 insertions, 112 deletions
diff --git a/kvmd/apps/__init__.py b/kvmd/apps/__init__.py index 4b61a65e..b3bc98fc 100644 --- a/kvmd/apps/__init__.py +++ b/kvmd/apps/__init__.py @@ -216,16 +216,15 @@ def _get_config_scheme() -> Dict: }, }, - "info": { # Accessed via global config, see kvmd/info.py for details + "info": { # Accessed via global config, see kvmd/info for details "meta": Option("/etc/kvmd/meta.yaml", type=valid_abs_file), "extras": Option("/usr/share/kvmd/extras", type=valid_abs_dir), - }, - - "hw": { - "vcgencmd_cmd": Option(["/opt/vc/bin/vcgencmd"], type=valid_command), - "procfs_prefix": Option("", type=(lambda arg: str(arg).strip())), - "sysfs_prefix": Option("", type=(lambda arg: str(arg).strip())), - "state_poll": Option(10.0, type=valid_float_f01), + "hw": { + "vcgencmd_cmd": Option(["/opt/vc/bin/vcgencmd"], type=valid_command), + "procfs_prefix": Option("", type=(lambda arg: str(arg).strip())), + "sysfs_prefix": Option("", type=(lambda arg: str(arg).strip())), + "state_poll": Option(10.0, type=valid_float_f01), + }, }, "wol": { diff --git a/kvmd/apps/kvmd/__init__.py b/kvmd/apps/kvmd/__init__.py index dad0da42..1684339f 100644 --- a/kvmd/apps/kvmd/__init__.py +++ b/kvmd/apps/kvmd/__init__.py @@ -35,7 +35,6 @@ from .. import init from .auth import AuthManager from .info import InfoManager -from .hw import HwManager from .logreader import LogReader from .wol import WakeOnLan from .streamer import Streamer @@ -78,7 +77,6 @@ def main(argv: Optional[List[str]]=None) -> None: enabled=config.auth.enabled, ), info_manager=InfoManager(global_config), - hw_manager=HwManager(**config.hw._unpack()), log_reader=LogReader(), wol=WakeOnLan(**config.wol._unpack()), diff --git a/kvmd/apps/kvmd/api/info.py b/kvmd/apps/kvmd/api/info.py index 3e0220a0..b33a4b6c 100644 --- a/kvmd/apps/kvmd/api/info.py +++ b/kvmd/apps/kvmd/api/info.py @@ -20,9 +20,16 @@ # ========================================================================== # +import asyncio + +from typing import List + from aiohttp.web import Request from aiohttp.web import Response +from ....validators import check_string_in_list +from ....validators.basic import valid_string_list + from ..info import InfoManager from ..http import exposed_http @@ -37,5 +44,18 @@ class InfoApi: # ===== @exposed_http("GET", "/info") - async def __state_handler(self, _: Request) -> Response: - return make_json_response(await self.__info_manager.get_state()) + async def __common_state_handler(self, request: Request) -> Response: + fields = self.__valid_info_fields(request) + results = dict(zip(fields, await asyncio.gather(*[ + self.__info_manager.get_submanager(field).get_state() + for field in fields + ]))) + return make_json_response(results) + + def __valid_info_fields(self, request: Request) -> List[str]: + subs = self.__info_manager.get_subs() + return (sorted(set(valid_string_list( + arg=request.query.get("fields", ",".join(subs)), + subval=(lambda field: check_string_in_list(field, "info field", subs)), + name="info fields list", + ))) or subs) diff --git a/kvmd/apps/kvmd/info/__init__.py b/kvmd/apps/kvmd/info/__init__.py new file mode 100644 index 00000000..3e833aed --- /dev/null +++ b/kvmd/apps/kvmd/info/__init__.py @@ -0,0 +1,48 @@ +# ========================================================================== # +# # +# 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 typing import List + +from ....yamlconf import Section + +from .base import BaseInfoSubmanager +from .system import SystemInfoSubmanager +from .meta import MetaInfoSubmanager +from .extras import ExtrasInfoSubmanager +from .hw import HwInfoSubmanager + + +# ===== +class InfoManager: + def __init__(self, config: Section) -> None: + self.__subs = { + "system": SystemInfoSubmanager(config.kvmd.streamer.cmd), + "meta": MetaInfoSubmanager(config.kvmd.info.meta), + "extras": ExtrasInfoSubmanager(config), + "hw": HwInfoSubmanager(**config.kvmd.info.hw._unpack()), + } + + def get_subs(self) -> List[str]: + return list(self.__subs) + + def get_submanager(self, name: str) -> BaseInfoSubmanager: + return self.__subs[name] diff --git a/kvmd/apps/kvmd/api/hw.py b/kvmd/apps/kvmd/info/base.py index ff097c4f..6b3bc969 100644 --- a/kvmd/apps/kvmd/api/hw.py +++ b/kvmd/apps/kvmd/info/base.py @@ -20,22 +20,11 @@ # ========================================================================== # -from aiohttp.web import Request -from aiohttp.web import Response - -from ..hw import HwManager - -from ..http import exposed_http -from ..http import make_json_response +from typing import Dict +from typing import Optional # ===== -class HwApi: - def __init__(self, hw_manager: HwManager) -> None: - self.__hw_manager = hw_manager - - # ===== - - @exposed_http("GET", "/hw") - async def __state_handler(self, _: Request) -> Response: - return make_json_response(await self.__hw_manager.get_state()) +class BaseInfoSubmanager: + async def get_state(self) -> Optional[Dict]: + raise NotImplementedError diff --git a/kvmd/apps/kvmd/info.py b/kvmd/apps/kvmd/info/extras.py index f85a7656..2f066925 100644 --- a/kvmd/apps/kvmd/info.py +++ b/kvmd/apps/kvmd/info/extras.py @@ -21,8 +21,6 @@ import os -import asyncio -import platform import contextlib from typing import Dict @@ -31,81 +29,27 @@ from typing import Optional import dbus # pylint: disable=import-error import dbus.exceptions -from ...logging import get_logger +from ....logging import get_logger -from ...yamlconf import Section -from ...yamlconf.loader import load_yaml_file +from ....yamlconf import Section +from ....yamlconf.loader import load_yaml_file -from ... import aiotools -from ... import aioproc +from .... import aiotools -from ... import __version__ +from .base import BaseInfoSubmanager # ===== -class InfoManager: +class ExtrasInfoSubmanager(BaseInfoSubmanager): def __init__(self, global_config: Section) -> None: self.__global_config = global_config - 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_state(self) -> Optional[Dict]: + return (await aiotools.run_async(self.__inner_get_state)) # ===== - 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]: + def __inner_get_state(self) -> Optional[Dict]: try: extras_path = self.__global_config.kvmd.info.extras extras: Dict[str, Dict] = {} @@ -117,7 +61,7 @@ class InfoManager: return extras except Exception: get_logger(0).exception("Can't parse extras") - return None + return None def __rewrite_app_daemon(self, extras: Dict) -> None: daemon = extras.get("daemon", "") diff --git a/kvmd/apps/kvmd/hw.py b/kvmd/apps/kvmd/info/hw.py index 13f7e9de..04c28b29 100644 --- a/kvmd/apps/kvmd/hw.py +++ b/kvmd/apps/kvmd/info/hw.py @@ -31,9 +31,11 @@ from typing import Optional import aiofiles -from ...logging import get_logger +from ....logging import get_logger -from ... import aioproc +from .... import aioproc + +from .base import BaseInfoSubmanager # ===== @@ -41,7 +43,7 @@ _RetvalT = TypeVar("_RetvalT") # ===== -class HwManager: +class HwInfoSubmanager(BaseInfoSubmanager): def __init__( self, vcgencmd_cmd: List[str], diff --git a/kvmd/apps/kvmd/info/meta.py b/kvmd/apps/kvmd/info/meta.py new file mode 100644 index 00000000..2ed50d69 --- /dev/null +++ b/kvmd/apps/kvmd/info/meta.py @@ -0,0 +1,45 @@ +# ========================================================================== # +# # +# 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 typing import Dict +from typing import Optional + +from ....logging import get_logger + +from ....yamlconf.loader import load_yaml_file + +from .... import aiotools + +from .base import BaseInfoSubmanager + + +# ===== +class MetaInfoSubmanager(BaseInfoSubmanager): + def __init__(self, meta_path: str) -> None: + self.__meta_path = meta_path + + async def get_state(self) -> Optional[Dict]: + try: + return ((await aiotools.run_async(load_yaml_file, self.__meta_path)) or {}) + except Exception: + get_logger(0).exception("Can't parse meta") + return None diff --git a/kvmd/apps/kvmd/info/system.py b/kvmd/apps/kvmd/info/system.py new file mode 100644 index 00000000..0e66ec53 --- /dev/null +++ b/kvmd/apps/kvmd/info/system.py @@ -0,0 +1,80 @@ +# ========================================================================== # +# # +# 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/>. # +# # +# ========================================================================== # + + +import os +import asyncio +import platform + +from typing import List +from typing import Dict + +from ....logging import get_logger + +from .... import aioproc + +from .... import __version__ + +from .base import BaseInfoSubmanager + + +# ===== +class SystemInfoSubmanager(BaseInfoSubmanager): + def __init__(self, streamer_cmd: List[str]) -> None: + self.__streamer_cmd = streamer_cmd + + async def get_state(self) -> Dict: + streamer_info = await self.__get_streamer_info() + uname_info = platform.uname() # Uname using the internal cache + return { + "kvmd": {"version": __version__}, + "streamer": streamer_info, + "kernel": { + field: getattr(uname_info, field) + for field in ["system", "release", "version", "machine"] + }, + } + + # ===== + + async def __get_streamer_info(self) -> Dict: + version = "" + features: Dict[str, bool] = {} + try: + path = self.__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, + } diff --git a/kvmd/apps/kvmd/server.py b/kvmd/apps/kvmd/server.py index 2e5975c6..9e4c1122 100644 --- a/kvmd/apps/kvmd/server.py +++ b/kvmd/apps/kvmd/server.py @@ -62,7 +62,6 @@ from ... import aioproc from .auth import AuthManager from .info import InfoManager -from .hw import HwManager from .logreader import LogReader from .wol import WakeOnLan from .streamer import Streamer @@ -82,7 +81,6 @@ from .api.auth import AuthApi from .api.auth import check_request_auth from .api.info import InfoApi -from .api.hw import HwApi from .api.log import LogApi from .api.wol import WolApi from .api.hid import HidApi @@ -125,7 +123,6 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins self, auth_manager: AuthManager, info_manager: InfoManager, - hw_manager: HwManager, log_reader: LogReader, wol: WakeOnLan, @@ -149,21 +146,26 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins self.__heartbeat = heartbeat self.__components = [ - _Component("Auth manager", "", auth_manager), - _Component("Info manager", "info_state", info_manager), - _Component("HW manager", "hw_state", hw_manager), - _Component("Wake-on-LAN", "wol_state", wol), - _Component("HID", "hid_state", hid), - _Component("ATX", "atx_state", atx), - _Component("MSD", "msd_state", msd), - _Component("Streamer", "streamer_state", streamer), + *[ + _Component("Auth manager", "", auth_manager), + ], + *[ + _Component(f"Info manager ({sub})", f"info_{sub}_state", info_manager.get_submanager(sub)) + for sub in info_manager.get_subs() + ], + *[ + _Component("Wake-on-LAN", "wol_state", wol), + _Component("HID", "hid_state", hid), + _Component("ATX", "atx_state", atx), + _Component("MSD", "msd_state", msd), + _Component("Streamer", "streamer_state", streamer), + ], ] self.__apis: List[object] = [ self, AuthApi(auth_manager), InfoApi(info_manager), - HwApi(hw_manager), LogApi(log_reader), WolApi(wol), HidApi(hid, keymap_path), @@ -325,7 +327,7 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins except Exception: logger.exception("Cleanup error on %s", component.name) - async def __broadcast_event(self, event_type: str, event: Dict) -> None: + async def __broadcast_event(self, event_type: str, event: Optional[Dict]) -> None: if self.__ws_clients: await asyncio.gather(*[ client.ws.send_str(json.dumps({ diff --git a/kvmd/apps/vnc/server.py b/kvmd/apps/vnc/server.py index 539470a8..73c9c148 100644 --- a/kvmd/apps/vnc/server.py +++ b/kvmd/apps/vnc/server.py @@ -149,9 +149,9 @@ class _Client(RfbClient): # pylint: disable=too-many-instance-attributes self.__kvmd_ws = None async def __process_ws_event(self, event: Dict) -> None: - if event["event_type"] == "info_state": + if event["event_type"] == "info_meta_state": try: - host = event["event"]["meta"]["server"]["host"] + host = event["event"]["server"]["host"] except Exception: host = None else: |