diff options
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | configs/kvmd/kvmd.v1-hdmi.yaml | 10 | ||||
-rw-r--r-- | configs/kvmd/kvmd.v1-vga.yaml | 10 | ||||
-rw-r--r-- | configs/kvmd/meta.yaml | 9 | ||||
-rw-r--r-- | kvmd/application.py | 32 | ||||
-rw-r--r-- | kvmd/apps/kvmd/__init__.py | 5 | ||||
-rw-r--r-- | kvmd/apps/kvmd/atx.py | 13 | ||||
-rw-r--r-- | kvmd/apps/kvmd/server.py | 21 | ||||
-rw-r--r-- | kvmd/apps/kvmd/streamer.py | 12 | ||||
-rw-r--r-- | kvmd/logging.py | 2 | ||||
-rw-r--r-- | kvmd/yaml.py | 31 | ||||
-rw-r--r-- | testenv/kvmd.yaml | 7 |
12 files changed, 97 insertions, 57 deletions
@@ -7,7 +7,7 @@ TESTENV_CMD ?= /bin/bash -c " \ && rm -rf /etc/nginx/* \ && cp -r /usr/share/kvmd/configs/nginx/* /etc/nginx \ && mkdir -p /etc/kvmd \ - && cp /usr/share/kvmd/configs/kvmd/logging.yaml /etc/kvmd/logging.yaml \ + && cp /usr/share/kvmd/configs/kvmd/{meta.yaml,logging.yaml} /etc/kvmd \ && cp /testenv/kvmd.yaml /etc/kvmd \ && nginx -c /etc/nginx/nginx.conf \ && ln -s $(TESTENV_VIDEO) /dev/kvmd-streamer \ diff --git a/configs/kvmd/kvmd.v1-hdmi.yaml b/configs/kvmd/kvmd.v1-hdmi.yaml index 1ac41a12..43b3278e 100644 --- a/configs/kvmd/kvmd.v1-hdmi.yaml +++ b/configs/kvmd/kvmd.v1-hdmi.yaml @@ -1,9 +1,15 @@ +# Don't touch this file otherwise your device may stop working. +# You can find a workable configuration in /usr/share/kvmd/configs/kvmd. + kvmd: server: host: 127.0.0.1 port: 8081 heartbeat: 3.0 + info: + meta: /etc/kvmd/meta.yaml + log: services: - kvmd.service @@ -26,7 +32,6 @@ kvmd: click_delay: 0.1 long_click_delay: 5.5 - state_poll: 0.1 msd: @@ -49,6 +54,7 @@ kvmd: init_delay: 1.0 init_restart_after: 0.0 shutdown_delay: 10.0 + state_poll: 1.0 quality: 80 desired_fps: 0 @@ -71,6 +77,4 @@ kvmd: - "--port={port}" - "--drop-same-frames=30" - state_poll: 1.0 - logging: !include logging.yaml diff --git a/configs/kvmd/kvmd.v1-vga.yaml b/configs/kvmd/kvmd.v1-vga.yaml index 3065c262..5993176f 100644 --- a/configs/kvmd/kvmd.v1-vga.yaml +++ b/configs/kvmd/kvmd.v1-vga.yaml @@ -1,9 +1,15 @@ +# Don't touch this file otherwise your device may stop working. +# You can find a workable configuration in /usr/share/kvmd/configs/kvmd. + kvmd: server: host: 127.0.0.1 port: 8081 heartbeat: 3.0 + info: + meta: /etc/kvmd/meta.yaml + log: services: - kvmd.service @@ -26,7 +32,6 @@ kvmd: click_delay: 0.1 long_click_delay: 5.5 - state_poll: 0.1 msd: @@ -49,6 +54,7 @@ kvmd: init_delay: 1.0 init_restart_after: 1.0 shutdown_delay: 10.0 + state_poll: 1.0 quality: 80 desired_fps: 0 @@ -72,6 +78,4 @@ kvmd: - "--host={host}" - "--port={port}" - state_poll: 1.0 - logging: !include logging.yaml diff --git a/configs/kvmd/meta.yaml b/configs/kvmd/meta.yaml new file mode 100644 index 00000000..65524997 --- /dev/null +++ b/configs/kvmd/meta.yaml @@ -0,0 +1,9 @@ +# You can write down any information and it will be available +# at the address /kvmd/info (if you use default nginx config). +# If server.host (str) will be defained then this value +# will be displayed in the web interface. + +server: + host: localhost + +kvm: {} diff --git a/kvmd/application.py b/kvmd/application.py index 31a57925..3db8d20d 100644 --- a/kvmd/application.py +++ b/kvmd/application.py @@ -1,15 +1,10 @@ -import os import argparse import logging import logging.config from typing import Dict -from typing import IO -from typing import Any -import yaml -import yaml.loader -import yaml.nodes +from .yaml import load_yaml_file # ===== @@ -18,32 +13,9 @@ def init() -> Dict: parser.add_argument("-c", "--config", required=True, metavar="<path>") options = parser.parse_args() - config: Dict = _load_yaml_file(options.config) + config: Dict = load_yaml_file(options.config) logging.captureWarnings(True) logging.config.dictConfig(config["logging"]) return config - - -# ===== -def _load_yaml_file(path: str) -> Any: - with open(path) as yaml_file: - try: - return yaml.load(yaml_file, _YamlLoader) - except Exception: - # Reraise internal exception as standard ValueError and show the incorrect file - raise ValueError("Incorrect YAML syntax in file '{}'".format(path)) - - -class _YamlLoader(yaml.loader.Loader): # pylint: disable=too-many-ancestors - def __init__(self, yaml_file: IO) -> None: - yaml.loader.Loader.__init__(self, yaml_file) - self.__root = os.path.dirname(yaml_file.name) - - def include(self, node: yaml.nodes.Node) -> str: - path = os.path.join(self.__root, self.construct_scalar(node)) # pylint: disable=no-member - return _load_yaml_file(path) - - -_YamlLoader.add_constructor("!include", _YamlLoader.include) # pylint: disable=no-member diff --git a/kvmd/apps/kvmd/__init__.py b/kvmd/apps/kvmd/__init__.py index 099fd192..103d8be8 100644 --- a/kvmd/apps/kvmd/__init__.py +++ b/kvmd/apps/kvmd/__init__.py @@ -42,6 +42,7 @@ def main() -> None: reset_switch=int(config["atx"]["pinout"]["reset_switch"]), click_delay=float(config["atx"]["click_delay"]), long_click_delay=float(config["atx"]["long_click_delay"]), + state_poll=float(config["atx"]["state_poll"]), ) msd = MassStorageDevice( @@ -62,6 +63,7 @@ def main() -> None: sync_delay=float(config["streamer"]["sync_delay"]), init_delay=float(config["streamer"]["init_delay"]), init_restart_after=float(config["streamer"]["init_restart_after"]), + state_poll=float(config["streamer"]["state_poll"]), quality=int(config["streamer"]["quality"]), desired_fps=int(config["streamer"]["desired_fps"]), @@ -83,9 +85,8 @@ def main() -> None: msd=msd, streamer=streamer, + meta_path=str(config["info"]["meta"]), heartbeat=float(config["server"]["heartbeat"]), - atx_state_poll=float(config["atx"]["state_poll"]), - streamer_state_poll=float(config["streamer"]["state_poll"]), streamer_shutdown_delay=float(config["streamer"]["shutdown_delay"]), msd_chunk_size=int(config["msd"]["chunk_size"]), diff --git a/kvmd/apps/kvmd/atx.py b/kvmd/apps/kvmd/atx.py index d49708e0..8444c097 100644 --- a/kvmd/apps/kvmd/atx.py +++ b/kvmd/apps/kvmd/atx.py @@ -1,6 +1,7 @@ import asyncio from typing import Dict +from typing import AsyncGenerator from ...logging import get_logger @@ -13,16 +14,17 @@ class AtxIsBusy(aioregion.RegionIsBusyError): pass -class Atx: +class Atx: # pylint: disable=too-many-instance-attributes def __init__( self, power_led: int, hdd_led: int, - power_switch: int, reset_switch: int, + click_delay: float, long_click_delay: float, + state_poll: float, ) -> None: self.__power_led = gpio.set_input(power_led) @@ -33,6 +35,8 @@ class Atx: self.__click_delay = click_delay self.__long_click_delay = long_click_delay + self.__state_poll = state_poll + self.__region = aioregion.AioExclusiveRegion(AtxIsBusy) def get_state(self) -> Dict: @@ -44,6 +48,11 @@ class Atx: }, } + async def poll_state(self) -> AsyncGenerator[Dict, None]: + while True: + yield self.get_state() + await asyncio.sleep(self.__state_poll) + async def click_power(self) -> None: await self.__click(self.__power_switch, self.__click_delay) get_logger().info("Clicked power") diff --git a/kvmd/apps/kvmd/server.py b/kvmd/apps/kvmd/server.py index 04ce140f..ae66cbc4 100644 --- a/kvmd/apps/kvmd/server.py +++ b/kvmd/apps/kvmd/server.py @@ -18,6 +18,8 @@ from ...logging import Log from ...aioregion import RegionIsBusyError +from ...yaml import load_yaml_file + from ... import __version__ from .hid import Hid @@ -111,9 +113,8 @@ class Server: # pylint: disable=too-many-instance-attributes msd: MassStorageDevice, streamer: Streamer, + meta_path: str, heartbeat: float, - atx_state_poll: float, - streamer_state_poll: float, streamer_shutdown_delay: float, msd_chunk_size: int, @@ -126,9 +127,8 @@ class Server: # pylint: disable=too-many-instance-attributes self.__msd = msd self.__streamer = streamer + self.__meta_path = meta_path self.__heartbeat = heartbeat - self.__atx_state_poll = atx_state_poll - self.__streamer_state_poll = streamer_state_poll self.__streamer_shutdown_delay = streamer_shutdown_delay self.__msd_chunk_size = msd_chunk_size @@ -190,6 +190,7 @@ class Server: # pylint: disable=too-many-instance-attributes "streamer": await self.__streamer.get_version(), }, "streamer": self.__streamer.get_app(), + "meta": load_yaml_file(self.__meta_path), }) @_wrap_exceptions_for_web("Log error") @@ -198,7 +199,7 @@ class Server: # pylint: disable=too-many-instance-attributes follow = _valid_bool("follow", request.query.get("follow", "false")) response = aiohttp.web.StreamResponse(status=200, reason="OK", headers={"Content-Type": "text/plain"}) await response.prepare(request) - async for record in self.__log.log(seek, follow): + async for record in self.__log.poll_log(seek, follow): await response.write(("[%s %s] --- %s" % ( record["dt"].strftime("%Y-%m-%d %H:%M:%S"), record["service"], @@ -431,17 +432,15 @@ class Server: # pylint: disable=too-many-instance-attributes @_system_task async def __poll_atx_state(self) -> None: - while True: + async for state in self.__atx.poll_state(): if self.__sockets: - await self.__broadcast_event("atx_state", **self.__atx.get_state()) - await asyncio.sleep(self.__atx_state_poll) + await self.__broadcast_event("atx_state", **state) @_system_task async def __poll_streamer_state(self) -> None: - while True: + async for state in self.__streamer.poll_state(): if self.__sockets: - await self.__broadcast_event("streamer_state", **(await self.__streamer.get_state())) - await asyncio.sleep(self.__streamer_state_poll) + await self.__broadcast_event("streamer_state", **state) async def __broadcast_event(self, event: str, **kwargs: Dict) -> None: await asyncio.gather(*[ diff --git a/kvmd/apps/kvmd/streamer.py b/kvmd/apps/kvmd/streamer.py index da82ade1..25fe7527 100644 --- a/kvmd/apps/kvmd/streamer.py +++ b/kvmd/apps/kvmd/streamer.py @@ -4,6 +4,7 @@ import asyncio.subprocess from typing import List from typing import Dict +from typing import AsyncGenerator from typing import Optional import aiohttp @@ -15,13 +16,15 @@ from ... import gpio # ===== class Streamer: # pylint: disable=too-many-instance-attributes - def __init__( # pylint: disable=too-many-arguments + def __init__( # pylint: disable=too-many-arguments,too-many-locals self, cap_power: int, conv_power: int, + sync_delay: float, init_delay: float, init_restart_after: float, + state_poll: float, quality: int, desired_fps: int, @@ -38,9 +41,11 @@ class Streamer: # pylint: disable=too-many-instance-attributes self.__cap_power = (gpio.set_output(cap_power) if cap_power > 0 else cap_power) self.__conv_power = (gpio.set_output(conv_power) if conv_power > 0 else conv_power) + self.__sync_delay = sync_delay self.__init_delay = init_delay self.__init_restart_after = init_restart_after + self.__state_poll = state_poll self.__params = { "quality": quality, @@ -99,6 +104,11 @@ class Streamer: # pylint: disable=too-many-instance-attributes "state": state, } + async def poll_state(self) -> AsyncGenerator[Dict, None]: + while True: + yield (await self.get_state()) + await asyncio.sleep(self.__state_poll) + def get_app(self) -> str: return os.path.basename(self.__cmd[0]) diff --git a/kvmd/logging.py b/kvmd/logging.py index 0efb9cb4..11a8dcf1 100644 --- a/kvmd/logging.py +++ b/kvmd/logging.py @@ -33,7 +33,7 @@ class Log: self.__services = services self.__loop = loop - async def log(self, seek: int, follow: bool) -> AsyncGenerator[Dict, None]: + async def poll_log(self, seek: int, follow: bool) -> AsyncGenerator[Dict, None]: reader = systemd.journal.Reader() reader.this_boot() reader.this_machine() diff --git a/kvmd/yaml.py b/kvmd/yaml.py new file mode 100644 index 00000000..cd7ae4fd --- /dev/null +++ b/kvmd/yaml.py @@ -0,0 +1,31 @@ +import os + +from typing import IO +from typing import Any + +import yaml +import yaml.loader +import yaml.nodes + + +# ===== +def load_yaml_file(path: str) -> Any: + with open(path) as yaml_file: + try: + return yaml.load(yaml_file, _YamlLoader) + except Exception: + # Reraise internal exception as standard ValueError and show the incorrect file + raise ValueError("Incorrect YAML syntax in file '{}'".format(path)) + + +class _YamlLoader(yaml.loader.Loader): # pylint: disable=too-many-ancestors + def __init__(self, yaml_file: IO) -> None: + yaml.loader.Loader.__init__(self, yaml_file) + self.__root = os.path.dirname(yaml_file.name) + + def include(self, node: yaml.nodes.Node) -> str: + path = os.path.join(self.__root, self.construct_scalar(node)) # pylint: disable=no-member + return load_yaml_file(path) + + +_YamlLoader.add_constructor("!include", _YamlLoader.include) # pylint: disable=no-member diff --git a/testenv/kvmd.yaml b/testenv/kvmd.yaml index 34489afb..3ab0a25b 100644 --- a/testenv/kvmd.yaml +++ b/testenv/kvmd.yaml @@ -4,6 +4,9 @@ kvmd: port: 8081 heartbeat: 3.0 + info: + meta: /etc/kvmd/meta.yaml + log: services: - kvmd.service @@ -25,7 +28,6 @@ kvmd: click_delay: 0.1 long_click_delay: 5.5 - state_poll: 0.1 msd: @@ -48,6 +50,7 @@ kvmd: init_delay: 1.0 init_restart_after: 1.0 shutdown_delay: 10.0 + state_poll: 1.0 quality: 80 desired_fps: 0 @@ -66,6 +69,4 @@ kvmd: - "--host=0.0.0.0" - "--port={port}" - state_poll: 1.0 - logging: !include logging.yaml |