meta, refactoring

This commit is contained in:
Devaev Maxim 2018-11-16 06:54:27 +03:00
parent 4294eed14e
commit 17c6b9e31f
12 changed files with 97 additions and 57 deletions

View File

@ -7,7 +7,7 @@ TESTENV_CMD ?= /bin/bash -c " \
&& rm -rf /etc/nginx/* \ && rm -rf /etc/nginx/* \
&& cp -r /usr/share/kvmd/configs/nginx/* /etc/nginx \ && cp -r /usr/share/kvmd/configs/nginx/* /etc/nginx \
&& mkdir -p /etc/kvmd \ && 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 \ && cp /testenv/kvmd.yaml /etc/kvmd \
&& nginx -c /etc/nginx/nginx.conf \ && nginx -c /etc/nginx/nginx.conf \
&& ln -s $(TESTENV_VIDEO) /dev/kvmd-streamer \ && ln -s $(TESTENV_VIDEO) /dev/kvmd-streamer \

View File

@ -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: kvmd:
server: server:
host: 127.0.0.1 host: 127.0.0.1
port: 8081 port: 8081
heartbeat: 3.0 heartbeat: 3.0
info:
meta: /etc/kvmd/meta.yaml
log: log:
services: services:
- kvmd.service - kvmd.service
@ -26,7 +32,6 @@ kvmd:
click_delay: 0.1 click_delay: 0.1
long_click_delay: 5.5 long_click_delay: 5.5
state_poll: 0.1 state_poll: 0.1
msd: msd:
@ -49,6 +54,7 @@ kvmd:
init_delay: 1.0 init_delay: 1.0
init_restart_after: 0.0 init_restart_after: 0.0
shutdown_delay: 10.0 shutdown_delay: 10.0
state_poll: 1.0
quality: 80 quality: 80
desired_fps: 0 desired_fps: 0
@ -71,6 +77,4 @@ kvmd:
- "--port={port}" - "--port={port}"
- "--drop-same-frames=30" - "--drop-same-frames=30"
state_poll: 1.0
logging: !include logging.yaml logging: !include logging.yaml

View File

@ -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: kvmd:
server: server:
host: 127.0.0.1 host: 127.0.0.1
port: 8081 port: 8081
heartbeat: 3.0 heartbeat: 3.0
info:
meta: /etc/kvmd/meta.yaml
log: log:
services: services:
- kvmd.service - kvmd.service
@ -26,7 +32,6 @@ kvmd:
click_delay: 0.1 click_delay: 0.1
long_click_delay: 5.5 long_click_delay: 5.5
state_poll: 0.1 state_poll: 0.1
msd: msd:
@ -49,6 +54,7 @@ kvmd:
init_delay: 1.0 init_delay: 1.0
init_restart_after: 1.0 init_restart_after: 1.0
shutdown_delay: 10.0 shutdown_delay: 10.0
state_poll: 1.0
quality: 80 quality: 80
desired_fps: 0 desired_fps: 0
@ -72,6 +78,4 @@ kvmd:
- "--host={host}" - "--host={host}"
- "--port={port}" - "--port={port}"
state_poll: 1.0
logging: !include logging.yaml logging: !include logging.yaml

9
configs/kvmd/meta.yaml Normal file
View File

@ -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: {}

View File

@ -1,15 +1,10 @@
import os
import argparse import argparse
import logging import logging
import logging.config import logging.config
from typing import Dict from typing import Dict
from typing import IO
from typing import Any
import yaml from .yaml import load_yaml_file
import yaml.loader
import yaml.nodes
# ===== # =====
@ -18,32 +13,9 @@ def init() -> Dict:
parser.add_argument("-c", "--config", required=True, metavar="<path>") parser.add_argument("-c", "--config", required=True, metavar="<path>")
options = parser.parse_args() options = parser.parse_args()
config: Dict = _load_yaml_file(options.config) config: Dict = load_yaml_file(options.config)
logging.captureWarnings(True) logging.captureWarnings(True)
logging.config.dictConfig(config["logging"]) logging.config.dictConfig(config["logging"])
return config 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

View File

@ -42,6 +42,7 @@ def main() -> None:
reset_switch=int(config["atx"]["pinout"]["reset_switch"]), reset_switch=int(config["atx"]["pinout"]["reset_switch"]),
click_delay=float(config["atx"]["click_delay"]), click_delay=float(config["atx"]["click_delay"]),
long_click_delay=float(config["atx"]["long_click_delay"]), long_click_delay=float(config["atx"]["long_click_delay"]),
state_poll=float(config["atx"]["state_poll"]),
) )
msd = MassStorageDevice( msd = MassStorageDevice(
@ -62,6 +63,7 @@ def main() -> None:
sync_delay=float(config["streamer"]["sync_delay"]), sync_delay=float(config["streamer"]["sync_delay"]),
init_delay=float(config["streamer"]["init_delay"]), init_delay=float(config["streamer"]["init_delay"]),
init_restart_after=float(config["streamer"]["init_restart_after"]), init_restart_after=float(config["streamer"]["init_restart_after"]),
state_poll=float(config["streamer"]["state_poll"]),
quality=int(config["streamer"]["quality"]), quality=int(config["streamer"]["quality"]),
desired_fps=int(config["streamer"]["desired_fps"]), desired_fps=int(config["streamer"]["desired_fps"]),
@ -83,9 +85,8 @@ def main() -> None:
msd=msd, msd=msd,
streamer=streamer, streamer=streamer,
meta_path=str(config["info"]["meta"]),
heartbeat=float(config["server"]["heartbeat"]), 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"]), streamer_shutdown_delay=float(config["streamer"]["shutdown_delay"]),
msd_chunk_size=int(config["msd"]["chunk_size"]), msd_chunk_size=int(config["msd"]["chunk_size"]),

View File

@ -1,6 +1,7 @@
import asyncio import asyncio
from typing import Dict from typing import Dict
from typing import AsyncGenerator
from ...logging import get_logger from ...logging import get_logger
@ -13,16 +14,17 @@ class AtxIsBusy(aioregion.RegionIsBusyError):
pass pass
class Atx: class Atx: # pylint: disable=too-many-instance-attributes
def __init__( def __init__(
self, self,
power_led: int, power_led: int,
hdd_led: int, hdd_led: int,
power_switch: int, power_switch: int,
reset_switch: int, reset_switch: int,
click_delay: float, click_delay: float,
long_click_delay: float, long_click_delay: float,
state_poll: float,
) -> None: ) -> None:
self.__power_led = gpio.set_input(power_led) self.__power_led = gpio.set_input(power_led)
@ -33,6 +35,8 @@ class Atx:
self.__click_delay = click_delay self.__click_delay = click_delay
self.__long_click_delay = long_click_delay self.__long_click_delay = long_click_delay
self.__state_poll = state_poll
self.__region = aioregion.AioExclusiveRegion(AtxIsBusy) self.__region = aioregion.AioExclusiveRegion(AtxIsBusy)
def get_state(self) -> Dict: 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: async def click_power(self) -> None:
await self.__click(self.__power_switch, self.__click_delay) await self.__click(self.__power_switch, self.__click_delay)
get_logger().info("Clicked power") get_logger().info("Clicked power")

View File

@ -18,6 +18,8 @@ from ...logging import Log
from ...aioregion import RegionIsBusyError from ...aioregion import RegionIsBusyError
from ...yaml import load_yaml_file
from ... import __version__ from ... import __version__
from .hid import Hid from .hid import Hid
@ -111,9 +113,8 @@ class Server: # pylint: disable=too-many-instance-attributes
msd: MassStorageDevice, msd: MassStorageDevice,
streamer: Streamer, streamer: Streamer,
meta_path: str,
heartbeat: float, heartbeat: float,
atx_state_poll: float,
streamer_state_poll: float,
streamer_shutdown_delay: float, streamer_shutdown_delay: float,
msd_chunk_size: int, msd_chunk_size: int,
@ -126,9 +127,8 @@ class Server: # pylint: disable=too-many-instance-attributes
self.__msd = msd self.__msd = msd
self.__streamer = streamer self.__streamer = streamer
self.__meta_path = meta_path
self.__heartbeat = heartbeat 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.__streamer_shutdown_delay = streamer_shutdown_delay
self.__msd_chunk_size = msd_chunk_size 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": await self.__streamer.get_version(),
}, },
"streamer": self.__streamer.get_app(), "streamer": self.__streamer.get_app(),
"meta": load_yaml_file(self.__meta_path),
}) })
@_wrap_exceptions_for_web("Log error") @_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")) follow = _valid_bool("follow", request.query.get("follow", "false"))
response = aiohttp.web.StreamResponse(status=200, reason="OK", headers={"Content-Type": "text/plain"}) response = aiohttp.web.StreamResponse(status=200, reason="OK", headers={"Content-Type": "text/plain"})
await response.prepare(request) 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" % ( await response.write(("[%s %s] --- %s" % (
record["dt"].strftime("%Y-%m-%d %H:%M:%S"), record["dt"].strftime("%Y-%m-%d %H:%M:%S"),
record["service"], record["service"],
@ -431,17 +432,15 @@ class Server: # pylint: disable=too-many-instance-attributes
@_system_task @_system_task
async def __poll_atx_state(self) -> None: async def __poll_atx_state(self) -> None:
while True: async for state in self.__atx.poll_state():
if self.__sockets: if self.__sockets:
await self.__broadcast_event("atx_state", **self.__atx.get_state()) await self.__broadcast_event("atx_state", **state)
await asyncio.sleep(self.__atx_state_poll)
@_system_task @_system_task
async def __poll_streamer_state(self) -> None: async def __poll_streamer_state(self) -> None:
while True: async for state in self.__streamer.poll_state():
if self.__sockets: if self.__sockets:
await self.__broadcast_event("streamer_state", **(await self.__streamer.get_state())) await self.__broadcast_event("streamer_state", **state)
await asyncio.sleep(self.__streamer_state_poll)
async def __broadcast_event(self, event: str, **kwargs: Dict) -> None: async def __broadcast_event(self, event: str, **kwargs: Dict) -> None:
await asyncio.gather(*[ await asyncio.gather(*[

View File

@ -4,6 +4,7 @@ import asyncio.subprocess
from typing import List from typing import List
from typing import Dict from typing import Dict
from typing import AsyncGenerator
from typing import Optional from typing import Optional
import aiohttp import aiohttp
@ -15,13 +16,15 @@ from ... import gpio
# ===== # =====
class Streamer: # pylint: disable=too-many-instance-attributes 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, self,
cap_power: int, cap_power: int,
conv_power: int, conv_power: int,
sync_delay: float, sync_delay: float,
init_delay: float, init_delay: float,
init_restart_after: float, init_restart_after: float,
state_poll: float,
quality: int, quality: int,
desired_fps: 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.__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.__conv_power = (gpio.set_output(conv_power) if conv_power > 0 else conv_power)
self.__sync_delay = sync_delay self.__sync_delay = sync_delay
self.__init_delay = init_delay self.__init_delay = init_delay
self.__init_restart_after = init_restart_after self.__init_restart_after = init_restart_after
self.__state_poll = state_poll
self.__params = { self.__params = {
"quality": quality, "quality": quality,
@ -99,6 +104,11 @@ class Streamer: # pylint: disable=too-many-instance-attributes
"state": state, "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: def get_app(self) -> str:
return os.path.basename(self.__cmd[0]) return os.path.basename(self.__cmd[0])

View File

@ -33,7 +33,7 @@ class Log:
self.__services = services self.__services = services
self.__loop = loop 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 = systemd.journal.Reader()
reader.this_boot() reader.this_boot()
reader.this_machine() reader.this_machine()

31
kvmd/yaml.py Normal file
View File

@ -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

View File

@ -4,6 +4,9 @@ kvmd:
port: 8081 port: 8081
heartbeat: 3.0 heartbeat: 3.0
info:
meta: /etc/kvmd/meta.yaml
log: log:
services: services:
- kvmd.service - kvmd.service
@ -25,7 +28,6 @@ kvmd:
click_delay: 0.1 click_delay: 0.1
long_click_delay: 5.5 long_click_delay: 5.5
state_poll: 0.1 state_poll: 0.1
msd: msd:
@ -48,6 +50,7 @@ kvmd:
init_delay: 1.0 init_delay: 1.0
init_restart_after: 1.0 init_restart_after: 1.0
shutdown_delay: 10.0 shutdown_delay: 10.0
state_poll: 1.0
quality: 80 quality: 80
desired_fps: 0 desired_fps: 0
@ -66,6 +69,4 @@ kvmd:
- "--host=0.0.0.0" - "--host=0.0.0.0"
- "--port={port}" - "--port={port}"
state_poll: 1.0
logging: !include logging.yaml logging: !include logging.yaml