using dbus_next

This commit is contained in:
Maxim Devaev 2022-04-23 18:28:13 +03:00
parent e566364b75
commit d83e32fc51
5 changed files with 122 additions and 53 deletions

View File

@ -50,6 +50,7 @@ depends=(
python-netifaces python-netifaces
python-systemd python-systemd
python-dbus python-dbus
python-dbus-next
python-pygments python-pygments
python-pyghmi python-pyghmi
python-pam python-pam

View File

@ -22,6 +22,7 @@
import os import os
import re import re
import asyncio
from typing import Dict from typing import Dict
from typing import Optional from typing import Optional
@ -31,9 +32,10 @@ from ....logging import get_logger
from ....yamlconf import Section from ....yamlconf import Section
from ....yamlconf.loader import load_yaml_file from ....yamlconf.loader import load_yaml_file
from .... import tools
from .... import aiotools from .... import aiotools
from ..sysunit import get_service_status from .. import sysunit
from .base import BaseInfoSubmanager from .base import BaseInfoSubmanager
@ -44,37 +46,57 @@ class ExtrasInfoSubmanager(BaseInfoSubmanager):
self.__global_config = global_config self.__global_config = global_config
async def get_state(self) -> Optional[Dict]: async def get_state(self) -> Optional[Dict]:
return (await aiotools.run_async(self.__inner_get_state))
# =====
def __inner_get_state(self) -> Optional[Dict]:
try: 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] = {} extras: Dict[str, Dict] = {}
for name in os.listdir(extras_path): for extra in (await asyncio.gather(*[
if name[0] != "." and os.path.isdir(os.path.join(extras_path, name)): self.__read_extra(sui, name)
app = re.sub(r"[^a-zA-Z0-9_]+", "_", name) for name in os.listdir(self.__get_extras_path())
extras[app] = load_yaml_file(os.path.join(extras_path, name, "manifest.yaml")) if name[0] != "." and os.path.isdir(self.__get_extras_path(name))
self.__rewrite_app_daemon(extras[app]) ])):
self.__rewrite_app_port(extras[app]) extras.update(extra)
return extras return extras
except Exception: except Exception:
get_logger(0).exception("Can't parse extras") get_logger(0).exception("Can't read extras")
return None return None
finally:
if sui is not None:
await sui.close()
def __rewrite_app_daemon(self, extras: Dict) -> None: def __get_extras_path(self, *parts: str) -> str:
daemon = extras.get("daemon", "") 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 {}
async def __rewrite_app_daemon(self, sui: Optional[sysunit.SystemdUnitInfo], extra: Dict) -> None:
daemon = extra.get("daemon", "")
if isinstance(daemon, str) and daemon.strip(): if isinstance(daemon, str) and daemon.strip():
status = get_service_status(daemon) extra["enabled"] = extra["started"] = False
(extras["enabled"], extras["started"]) = (status if status is not None else (False, 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: def __rewrite_app_port(self, extra: Dict) -> None:
port_path = extras.get("port", "") port_path = extra.get("port", "")
if isinstance(port_path, str) and port_path.strip(): if isinstance(port_path, str) and port_path.strip():
extras["port"] = 0 extra["port"] = 0
config = self.__global_config config = self.__global_config
for item in filter(None, map(str.strip, port_path.split("/"))): for item in filter(None, map(str.strip, port_path.split("/"))):
config = getattr(config, item, None) # type: ignore config = getattr(config, item, None) # type: ignore
if isinstance(config, int): if isinstance(config, int):
extras["port"] = config extra["port"] = config

View File

@ -31,10 +31,11 @@ import aiohttp
from ....logging import get_logger from ....logging import get_logger
from .... import tools
from .... import aiotools from .... import aiotools
from .... import htclient from .... import htclient
from ..sysunit import get_service_status from .. import sysunit
from .base import BaseInfoSubmanager from .base import BaseInfoSubmanager
@ -84,9 +85,12 @@ class FanInfoSubmanager(BaseInfoSubmanager):
async def __get_monitored(self) -> bool: async def __get_monitored(self) -> bool:
if self.__unix_path: if self.__unix_path:
status = await aiotools.run_async(get_service_status, self.__daemon) try:
if status is not None: async with sysunit.SystemdUnitInfo() as sui:
status = await sui.get_status(self.__daemon)
return (status[0] or status[1]) 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 return False
async def __get_fan_state(self) -> Optional[Dict]: async def __get_fan_state(self) -> Optional[Dict]:

View File

@ -20,37 +20,78 @@
# ========================================================================== # # ========================================================================== #
import contextlib import types
from typing import Tuple from typing import Tuple
from typing import Type
from typing import Optional from typing import Optional
import dbus # pylint: disable=import-error import dbus_next
import dbus.exceptions import dbus_next.aio
import dbus_next.aio.proxy_object
from ...logging import get_logger import dbus_next.introspection
import dbus_next.errors
from ... import tools
# ===== # =====
def get_service_status(name: str) -> Optional[Tuple[bool, bool]]: 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
if not name.endswith(".service"): if not name.endswith(".service"):
name += ".service" name += ".service"
try: try:
with contextlib.closing(dbus.SystemBus()) as bus: unit_p = await self.__manager.call_get_unit(name) # type: ignore
systemd = bus.get_object("org.freedesktop.systemd1", "/org/freedesktop/systemd1") # pylint: disable=no-member unit = self.__bus.get_proxy_object("org.freedesktop.systemd1", unit_p, self.__intr)
manager = dbus.Interface(systemd, dbus_interface="org.freedesktop.systemd1.Manager") unit_props = unit.get_interface("org.freedesktop.DBus.Properties")
try: started = ((await unit_props.call_get("org.freedesktop.systemd1.Unit", "ActiveState")).value == "active") # type: ignore
unit_proxy = bus.get_object("org.freedesktop.systemd1", manager.GetUnit(name)) # pylint: disable=no-member except dbus_next.errors.DBusError as err:
unit_properties = dbus.Interface(unit_proxy, dbus_interface="org.freedesktop.DBus.Properties") if err.type != "org.freedesktop.systemd1.NoSuchUnit":
started = (unit_properties.Get("org.freedesktop.systemd1.Unit", "ActiveState") == "active")
except dbus.exceptions.DBusException as err:
if "NoSuchUnit" not in str(err):
raise raise
started = False started = False
enabled = (manager.GetUnitFileState(name) in ["enabled", "enabled-runtime", "static", "indirect", "generated"]) enabled = ((await self.__manager.call_get_unit_file_state(name)) in [ # type: ignore
"enabled",
"enabled-runtime",
"static",
"indirect",
"generated",
])
return (enabled, started) return (enabled, started)
except Exception as err:
get_logger(0).error("Can't get info about the service %r: %s", name, tools.efmt(err)) async def open(self) -> None:
return 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()

View File

@ -45,6 +45,7 @@ RUN pacman --noconfirm --ask=4 -Syy \
python-netifaces \ python-netifaces \
python-systemd \ python-systemd \
python-dbus \ python-dbus \
python-dbus-next \
python-pygments \ python-pygments \
python-pam \ python-pam \
python-pillow \ python-pillow \