diff options
Diffstat (limited to 'kvmd')
-rw-r--r-- | kvmd/apps/kvmd/info/extras.py | 66 | ||||
-rw-r--r-- | kvmd/apps/kvmd/info/fan.py | 12 | ||||
-rw-r--r-- | kvmd/apps/kvmd/sysunit.py | 93 |
3 files changed, 119 insertions, 52 deletions
diff --git a/kvmd/apps/kvmd/info/extras.py b/kvmd/apps/kvmd/info/extras.py index 35265a82..37bf3ff7 100644 --- a/kvmd/apps/kvmd/info/extras.py +++ b/kvmd/apps/kvmd/info/extras.py @@ -22,6 +22,7 @@ import os import re +import asyncio from typing import Dict from typing import Optional @@ -31,9 +32,10 @@ from ....logging import get_logger from ....yamlconf import Section from ....yamlconf.loader import load_yaml_file +from .... import tools from .... import aiotools -from ..sysunit import get_service_status +from .. import sysunit from .base import BaseInfoSubmanager @@ -44,37 +46,57 @@ class ExtrasInfoSubmanager(BaseInfoSubmanager): self.__global_config = global_config async def get_state(self) -> Optional[Dict]: - return (await aiotools.run_async(self.__inner_get_state)) - - # ===== - - def __inner_get_state(self) -> Optional[Dict]: try: - extras_path = self.__global_config.kvmd.info.extras + sui = sysunit.SystemdUnitInfo() + await sui.open() + except Exception as err: + get_logger(0).error("Can't open systemd bus to get extras state: %s", tools.efmt(err)) + sui = None + try: extras: Dict[str, Dict] = {} - for name in os.listdir(extras_path): - if name[0] != "." and os.path.isdir(os.path.join(extras_path, name)): - app = re.sub(r"[^a-zA-Z0-9_]+", "_", name) - extras[app] = load_yaml_file(os.path.join(extras_path, name, "manifest.yaml")) - self.__rewrite_app_daemon(extras[app]) - self.__rewrite_app_port(extras[app]) + for extra in (await asyncio.gather(*[ + self.__read_extra(sui, name) + for name in os.listdir(self.__get_extras_path()) + if name[0] != "." and os.path.isdir(self.__get_extras_path(name)) + ])): + extras.update(extra) return extras except Exception: - get_logger(0).exception("Can't parse extras") + get_logger(0).exception("Can't read extras") return None + finally: + if sui is not None: + await sui.close() + + def __get_extras_path(self, *parts: str) -> str: + return os.path.join(self.__global_config.kvmd.info.extras, *parts) + + async def __read_extra(self, sui: Optional[sysunit.SystemdUnitInfo], name: str) -> Dict: + try: + extra = await aiotools.run_async(load_yaml_file, self.__get_extras_path(name, "manifest.yaml")) + await self.__rewrite_app_daemon(sui, extra) + self.__rewrite_app_port(extra) + return {re.sub(r"[^a-zA-Z0-9_]+", "_", name): extra} + except Exception: + get_logger(0).exception("Can't read extra %r", name) + return {} - def __rewrite_app_daemon(self, extras: Dict) -> None: - daemon = extras.get("daemon", "") + async def __rewrite_app_daemon(self, sui: Optional[sysunit.SystemdUnitInfo], extra: Dict) -> None: + daemon = extra.get("daemon", "") if isinstance(daemon, str) and daemon.strip(): - status = get_service_status(daemon) - (extras["enabled"], extras["started"]) = (status if status is not None else (False, False)) + extra["enabled"] = extra["started"] = False + if sui is not None: + try: + (extra["enabled"], extra["started"]) = await sui.get_status(daemon) + except Exception as err: + get_logger(0).error("Can't get info about the service %r: %s", daemon, tools.efmt(err)) - def __rewrite_app_port(self, extras: Dict) -> None: - port_path = extras.get("port", "") + def __rewrite_app_port(self, extra: Dict) -> None: + port_path = extra.get("port", "") if isinstance(port_path, str) and port_path.strip(): - extras["port"] = 0 + extra["port"] = 0 config = self.__global_config for item in filter(None, map(str.strip, port_path.split("/"))): config = getattr(config, item, None) # type: ignore if isinstance(config, int): - extras["port"] = config + extra["port"] = config diff --git a/kvmd/apps/kvmd/info/fan.py b/kvmd/apps/kvmd/info/fan.py index 1e323d04..afa7afce 100644 --- a/kvmd/apps/kvmd/info/fan.py +++ b/kvmd/apps/kvmd/info/fan.py @@ -31,10 +31,11 @@ import aiohttp from ....logging import get_logger +from .... import tools from .... import aiotools from .... import htclient -from ..sysunit import get_service_status +from .. import sysunit from .base import BaseInfoSubmanager @@ -84,9 +85,12 @@ class FanInfoSubmanager(BaseInfoSubmanager): async def __get_monitored(self) -> bool: if self.__unix_path: - status = await aiotools.run_async(get_service_status, self.__daemon) - if status is not None: - return (status[0] or status[1]) + try: + async with sysunit.SystemdUnitInfo() as sui: + status = await sui.get_status(self.__daemon) + return (status[0] or status[1]) + except Exception as err: + get_logger(0).error("Can't get info about the service %r: %s", self.__daemon, tools.efmt(err)) return False async def __get_fan_state(self) -> Optional[Dict]: diff --git a/kvmd/apps/kvmd/sysunit.py b/kvmd/apps/kvmd/sysunit.py index afd04705..dea5e961 100644 --- a/kvmd/apps/kvmd/sysunit.py +++ b/kvmd/apps/kvmd/sysunit.py @@ -20,37 +20,78 @@ # ========================================================================== # -import contextlib +import types from typing import Tuple +from typing import Type from typing import Optional -import dbus # pylint: disable=import-error -import dbus.exceptions +import dbus_next +import dbus_next.aio +import dbus_next.aio.proxy_object +import dbus_next.introspection +import dbus_next.errors -from ...logging import get_logger -from ... import tools +# ===== +class SystemdUnitInfo: + def __init__(self) -> None: + self.__bus: Optional[dbus_next.aio.MessageBus] = None + self.__intr: Optional[dbus_next.introspection.Node] = None + self.__manager: Optional[dbus_next.aio.proxy_object.ProxyInterface] = None + async def get_status(self, name: str) -> Tuple[bool, bool]: + assert self.__bus is not None + assert self.__intr is not None + assert self.__manager is not None -# ===== -def get_service_status(name: str) -> Optional[Tuple[bool, bool]]: - if not name.endswith(".service"): - name += ".service" - try: - with contextlib.closing(dbus.SystemBus()) as bus: - systemd = bus.get_object("org.freedesktop.systemd1", "/org/freedesktop/systemd1") # pylint: disable=no-member - manager = dbus.Interface(systemd, dbus_interface="org.freedesktop.systemd1.Manager") - try: - unit_proxy = bus.get_object("org.freedesktop.systemd1", manager.GetUnit(name)) # pylint: disable=no-member - unit_properties = dbus.Interface(unit_proxy, dbus_interface="org.freedesktop.DBus.Properties") - started = (unit_properties.Get("org.freedesktop.systemd1.Unit", "ActiveState") == "active") - except dbus.exceptions.DBusException as err: - if "NoSuchUnit" not in str(err): - raise - started = False - enabled = (manager.GetUnitFileState(name) in ["enabled", "enabled-runtime", "static", "indirect", "generated"]) - return (enabled, started) - except Exception as err: - get_logger(0).error("Can't get info about the service %r: %s", name, tools.efmt(err)) - return None + if not name.endswith(".service"): + name += ".service" + + try: + unit_p = await self.__manager.call_get_unit(name) # type: ignore + unit = self.__bus.get_proxy_object("org.freedesktop.systemd1", unit_p, self.__intr) + unit_props = unit.get_interface("org.freedesktop.DBus.Properties") + started = ((await unit_props.call_get("org.freedesktop.systemd1.Unit", "ActiveState")).value == "active") # type: ignore + except dbus_next.errors.DBusError as err: + if err.type != "org.freedesktop.systemd1.NoSuchUnit": + raise + started = False + enabled = ((await self.__manager.call_get_unit_file_state(name)) in [ # type: ignore + "enabled", + "enabled-runtime", + "static", + "indirect", + "generated", + ]) + return (enabled, started) + + async def open(self) -> None: + self.__bus = await dbus_next.aio.MessageBus(bus_type=dbus_next.BusType.SYSTEM).connect() + self.__intr = await self.__bus.introspect("org.freedesktop.systemd1", "/org/freedesktop/systemd1") + systemd = self.__bus.get_proxy_object("org.freedesktop.systemd1", "/org/freedesktop/systemd1", self.__intr) + self.__manager = systemd.get_interface("org.freedesktop.systemd1.Manager") + + async def __aenter__(self) -> "SystemdUnitInfo": + await self.open() + return self + + async def close(self) -> None: + try: + if self.__bus is not None: + self.__bus.disconnect() + await self.__bus.wait_for_disconnect() + except Exception: + pass + self.__manager = None + self.__intr = None + self.__bus = None + + async def __aexit__( + self, + _exc_type: Type[BaseException], + _exc: BaseException, + _tb: types.TracebackType, + ) -> None: + + await self.close() |