summaryrefslogtreecommitdiff
path: root/kvmd/apps
diff options
context:
space:
mode:
authorDevaev Maxim <[email protected]>2019-04-06 05:32:02 +0300
committerDevaev Maxim <[email protected]>2019-04-06 08:04:26 +0300
commit1d75b738a08c98a5d3d8ac3c685e77360f4c1267 (patch)
tree3aa89dc7fd0ab737e9332714a784e9d4dde0a362 /kvmd/apps
parent73e04b71ed55a46c939f12548b31746617af2bca (diff)
validators, tests
Diffstat (limited to 'kvmd/apps')
-rw-r--r--kvmd/apps/__init__.py197
-rw-r--r--kvmd/apps/cleanup/__init__.py9
-rw-r--r--kvmd/apps/htpasswd/__init__.py29
-rw-r--r--kvmd/apps/kvmd/__init__.py14
-rw-r--r--kvmd/apps/kvmd/hid.py2
-rw-r--r--kvmd/apps/kvmd/server.py106
-rw-r--r--kvmd/apps/kvmd/streamer.py8
7 files changed, 154 insertions, 211 deletions
diff --git a/kvmd/apps/__init__.py b/kvmd/apps/__init__.py
index d2007f7d..062e9bfe 100644
--- a/kvmd/apps/__init__.py
+++ b/kvmd/apps/__init__.py
@@ -29,14 +29,13 @@ import logging.config
from typing import Tuple
from typing import List
from typing import Dict
-from typing import Sequence
from typing import Optional
-from typing import Union
import pygments
import pygments.lexers.data
import pygments.formatters
+from ..yamlconf import ConfigError
from ..yamlconf import make_config
from ..yamlconf import Section
from ..yamlconf import Option
@@ -44,31 +43,59 @@ from ..yamlconf import build_raw_from_options
from ..yamlconf.dumper import make_config_dump
from ..yamlconf.loader import load_yaml_file
+from ..validators.basic import valid_bool
+from ..validators.basic import valid_number
+from ..validators.basic import valid_int_f1
+from ..validators.basic import valid_float_f01
+
+from ..validators.fs import valid_abs_path
+from ..validators.fs import valid_abs_path_exists
+from ..validators.fs import valid_unix_mode
+
+from ..validators.net import valid_ip_or_host
+from ..validators.net import valid_port
+
+from ..validators.auth import valid_auth_type
+
+from ..validators.kvm import valid_stream_quality
+from ..validators.kvm import valid_stream_fps
+
+from ..validators.hw import valid_tty_speed
+from ..validators.hw import valid_gpio_pin
+from ..validators.hw import valid_gpio_pin_optional
+
# =====
def init(
- prog: str=sys.argv[0],
+ prog: Optional[str]=None,
description: Optional[str]=None,
add_help: bool=True,
+ argv: Optional[List[str]]=None,
) -> Tuple[argparse.ArgumentParser, List[str], Section]:
- args_parser = argparse.ArgumentParser(prog=prog, description=description, add_help=add_help)
+ argv = (argv or sys.argv)
+ assert len(argv) > 0
+
+ args_parser = argparse.ArgumentParser(prog=(prog or argv[0]), description=description, add_help=add_help)
args_parser.add_argument("-c", "--config", dest="config_path", default="/etc/kvmd/main.yaml", metavar="<file>",
- help="Set config file path")
+ type=valid_abs_path_exists, help="Set config file path")
args_parser.add_argument("-o", "--set-options", dest="set_options", default=[], nargs="+",
help="Override config options list (like sec/sub/opt=value)")
args_parser.add_argument("-m", "--dump-config", dest="dump_config", action="store_true",
help="View current configuration (include all overrides)")
- (options, remaining) = args_parser.parse_known_args(sys.argv)
+ (options, remaining) = args_parser.parse_known_args(argv)
+ raw_config: Dict = {}
- options.config_path = os.path.expanduser(options.config_path)
- if os.path.exists(options.config_path):
+ if options.config_path:
+ options.config_path = os.path.expanduser(options.config_path)
raw_config = load_yaml_file(options.config_path)
- else:
- raw_config = {}
- _merge_dicts(raw_config, build_raw_from_options(options.set_options))
+
scheme = _get_config_scheme()
- config = make_config(raw_config, scheme)
+ try:
+ _merge_dicts(raw_config, build_raw_from_options(options.set_options))
+ config = make_config(raw_config, scheme)
+ except ConfigError as err:
+ raise SystemExit("Config error: " + str(err))
if options.dump_config:
dump = make_config_dump(config)
@@ -96,135 +123,93 @@ def _merge_dicts(dest: Dict, src: Dict) -> None:
dest[key] = src[key]
-def _as_pin(pin: int) -> int:
- if not isinstance(pin, int) or pin <= 0:
- raise ValueError("Invalid pin number")
- return pin
-
-
-def _as_optional_pin(pin: int) -> int:
- if not isinstance(pin, int) or pin < -1:
- raise ValueError("Invalid optional pin number")
- return pin
-
-
-def _as_path(path: str) -> str:
- if not isinstance(path, str):
- raise ValueError("Invalid path")
- path = str(path).strip()
- if not path:
- raise ValueError("Invalid path")
- return path
-
-
-def _as_optional_path(path: str) -> str:
- if not isinstance(path, str):
- raise ValueError("Invalid path")
- return str(path).strip()
-
-
-def _as_string_list(values: Union[str, Sequence]) -> List[str]:
- if isinstance(values, str):
- values = [values]
- return list(map(str, values))
-
-
-def _as_auth_type(auth_type: str) -> str:
- if not isinstance(auth_type, str):
- raise ValueError("Invalid auth type")
- auth_type = str(auth_type).strip()
- if auth_type not in ["basic"]:
- raise ValueError("Invalid auth type")
- return auth_type
-
-
def _get_config_scheme() -> Dict:
return {
"kvmd": {
"server": {
- "host": Option("localhost"),
- "port": Option(0),
- "unix": Option("", type=_as_optional_path, rename="unix_path"),
- "unix_rm": Option(False),
- "unix_mode": Option(0),
- "heartbeat": Option(3.0),
+ "host": Option("localhost", type=valid_ip_or_host),
+ "port": Option(0, type=valid_port),
+ "unix": Option("", type=valid_abs_path, only_if="!port", unpack_as="unix_path"),
+ "unix_rm": Option(False, type=valid_bool),
+ "unix_mode": Option(0, type=valid_unix_mode),
+ "heartbeat": Option(3.0, type=valid_float_f01),
"access_log_format": Option("[%P / %{X-Real-IP}i] '%r' => %s; size=%b ---"
" referer='%{Referer}i'; user_agent='%{User-Agent}i'"),
},
"auth": {
- "type": Option("basic", type=_as_auth_type, rename="auth_type"),
+ "type": Option("basic", type=valid_auth_type, unpack_as="auth_type"),
"basic": {
- "htpasswd": Option("/etc/kvmd/htpasswd", type=_as_path, rename="htpasswd_path"),
+ "htpasswd": Option("/etc/kvmd/htpasswd", type=valid_abs_path_exists, unpack_as="htpasswd_path"),
},
},
"info": {
- "meta": Option("/etc/kvmd/meta.yaml", type=_as_path, rename="meta_path"),
- "extras": Option("/usr/share/kvmd/extras", type=_as_path, rename="extras_path"),
+ "meta": Option("/etc/kvmd/meta.yaml", type=valid_abs_path_exists, unpack_as="meta_path"),
+ "extras": Option("/usr/share/kvmd/extras", type=valid_abs_path_exists, unpack_as="extras_path"),
},
"hid": {
- "reset_pin": Option(0, type=_as_pin),
- "reset_delay": Option(0.1),
-
- "device": Option("", type=_as_path, rename="device_path"),
- "speed": Option(115200),
- "read_timeout": Option(2.0),
- "read_retries": Option(10),
- "common_retries": Option(100),
- "retries_delay": Option(0.1),
- "noop": Option(False),
-
- "state_poll": Option(0.1),
+ "reset_pin": Option(-1, type=valid_gpio_pin),
+ "reset_delay": Option(0.1, type=valid_float_f01),
+
+ "device": Option("", type=valid_abs_path_exists, unpack_as="device_path"),
+ "speed": Option(115200, type=valid_tty_speed),
+ "read_timeout": Option(2.0, type=valid_float_f01),
+ "read_retries": Option(10, type=valid_int_f1),
+ "common_retries": Option(100, type=valid_int_f1),
+ "retries_delay": Option(0.1, type=valid_float_f01),
+ "noop": Option(False, type=valid_bool),
+
+ "state_poll": Option(0.1, type=valid_float_f01),
},
"atx": {
- "enabled": Option(True),
+ "enabled": Option(True, type=valid_bool),
- "power_led_pin": Option(-1, type=_as_optional_pin),
- "hdd_led_pin": Option(-1, type=_as_optional_pin),
- "power_switch_pin": Option(-1, type=_as_optional_pin),
- "reset_switch_pin": Option(-1, type=_as_optional_pin),
+ "power_led_pin": Option(-1, type=valid_gpio_pin, only_if="enabled"),
+ "hdd_led_pin": Option(-1, type=valid_gpio_pin, only_if="enabled"),
+ "power_switch_pin": Option(-1, type=valid_gpio_pin, only_if="enabled"),
+ "reset_switch_pin": Option(-1, type=valid_gpio_pin, only_if="enabled"),
- "click_delay": Option(0.1),
- "long_click_delay": Option(5.5),
+ "click_delay": Option(0.1, type=valid_float_f01),
+ "long_click_delay": Option(5.5, type=valid_float_f01),
- "state_poll": Option(0.1),
+ "state_poll": Option(0.1, type=valid_float_f01),
},
"msd": {
- "enabled": Option(True),
+ "enabled": Option(True, type=valid_bool),
- "target_pin": Option(-1, type=_as_optional_pin),
- "reset_pin": Option(-1, type=_as_optional_pin),
+ "target_pin": Option(-1, type=valid_gpio_pin, only_if="enabled"),
+ "reset_pin": Option(-1, type=valid_gpio_pin, only_if="enabled"),
- "device": Option("", type=_as_optional_path, rename="device_path"),
- "init_delay": Option(2.0),
- "reset_delay": Option(1.0),
- "write_meta": Option(True),
- "chunk_size": Option(65536),
+ "device": Option("", type=valid_abs_path, only_if="enabled", unpack_as="device_path"),
+ "init_delay": Option(2.0, type=valid_float_f01),
+ "reset_delay": Option(1.0, type=valid_float_f01),
+ "write_meta": Option(True, type=valid_bool),
+ "chunk_size": Option(65536, type=(lambda arg: valid_number(arg, min=1024))),
},
"streamer": {
- "cap_pin": Option(0, type=_as_optional_pin),
- "conv_pin": Option(0, type=_as_optional_pin),
+ "cap_pin": Option(-1, type=valid_gpio_pin_optional),
+ "conv_pin": Option(-1, type=valid_gpio_pin_optional),
- "sync_delay": Option(1.0),
- "init_delay": Option(1.0),
- "init_restart_after": Option(0.0),
- "shutdown_delay": Option(10.0),
- "state_poll": Option(1.0),
+ "sync_delay": Option(1.0, type=valid_float_f01),
+ "init_delay": Option(1.0, type=valid_float_f01),
+ "init_restart_after": Option(0.0, type=(lambda arg: valid_number(arg, min=0.0, type=float))),
+ "shutdown_delay": Option(10.0, type=valid_float_f01),
+ "state_poll": Option(1.0, type=valid_float_f01),
- "quality": Option(80),
- "desired_fps": Option(0),
+ "quality": Option(80, type=valid_stream_quality),
+ "desired_fps": Option(0, type=valid_stream_fps),
- "host": Option("localhost"),
- "port": Option(0),
- "unix": Option("", type=_as_optional_path, rename="unix_path"),
- "timeout": Option(2.0),
+ "host": Option("localhost", type=valid_ip_or_host),
+ "port": Option(0, type=valid_port),
+ "unix": Option("", type=valid_abs_path, only_if="!port", unpack_as="unix_path"),
+ "timeout": Option(2.0, type=valid_float_f01),
- "cmd": Option(["/bin/true"], type=_as_string_list),
+ "cmd": Option(["/bin/true"]), # TODO: Validator
},
},
diff --git a/kvmd/apps/cleanup/__init__.py b/kvmd/apps/cleanup/__init__.py
index b39559f6..39bd7a71 100644
--- a/kvmd/apps/cleanup/__init__.py
+++ b/kvmd/apps/cleanup/__init__.py
@@ -24,6 +24,9 @@ import os
import subprocess
import time
+from typing import List
+from typing import Optional
+
from ...logging import get_logger
from ... import gpio
@@ -32,8 +35,8 @@ from .. import init
# =====
-def main() -> None:
- config = init("kvmd-cleanup", description="Kill KVMD and clear resources")[2].kvmd
+def main(argv: Optional[List[str]]=None) -> None:
+ config = init("kvmd-cleanup", description="Kill KVMD and clear resources", argv=argv)[2].kvmd
logger = get_logger(0)
logger.info("Cleaning up ...")
@@ -47,7 +50,7 @@ def main() -> None:
("streamer_cap_pin", config.streamer.cap_pin),
("streamer_conv_pin", config.streamer.conv_pin),
]:
- if pin > 0:
+ if pin >= 0:
logger.info("Writing value=0 to pin=%d (%s)", pin, name)
gpio.set_output(pin, initial=False)
diff --git a/kvmd/apps/htpasswd/__init__.py b/kvmd/apps/htpasswd/__init__.py
index bc77c0e8..27f940b9 100644
--- a/kvmd/apps/htpasswd/__init__.py
+++ b/kvmd/apps/htpasswd/__init__.py
@@ -22,7 +22,6 @@
import sys
import os
-import re
import getpass
import tempfile
import contextlib
@@ -34,12 +33,16 @@ import passlib.apache
from ...yamlconf import Section
+from ...validators import ValidatorError
+from ...validators.auth import valid_user
+from ...validators.auth import valid_passwd
+
from .. import init
# =====
def _get_htpasswd_path(config: Section) -> str:
- if config.kvmd.auth.auth_type != "basic":
+ if config.kvmd.auth.type != "basic":
print("Warning: KVMD does not use basic auth", file=sys.stderr)
return config.kvmd.auth.basic.htpasswd
@@ -69,13 +72,6 @@ def _get_htpasswd_for_write(config: Section) -> Generator[passlib.apache.Htpassw
os.remove(tmp_path)
-def _valid_user(user: str) -> str:
- stripped = user.strip()
- if re.match(r"^[a-z_][a-z0-9_-]*$", stripped):
- return stripped
- raise SystemExit("Invalid user %r" % (user))
-
-
# ====
def _cmd_list(config: Section, _: argparse.Namespace) -> None:
for user in passlib.apache.HtpasswdFile(_get_htpasswd_path(config)).users():
@@ -85,10 +81,10 @@ def _cmd_list(config: Section, _: argparse.Namespace) -> None:
def _cmd_set(config: Section, options: argparse.Namespace) -> None:
with _get_htpasswd_for_write(config) as htpasswd:
if options.read_stdin:
- passwd = input()
+ passwd = valid_passwd(input())
else:
- passwd = getpass.getpass("Password: ", stream=sys.stderr)
- if getpass.getpass("Repeat: ", stream=sys.stderr) != passwd:
+ passwd = valid_passwd(getpass.getpass("Password: ", stream=sys.stderr))
+ if valid_passwd(getpass.getpass("Repeat: ", stream=sys.stderr)) != passwd:
raise SystemExit("Sorry, passwords do not match")
htpasswd.set_password(options.user, passwd)
@@ -113,13 +109,16 @@ def main() -> None:
cmd_list_parser.set_defaults(cmd=_cmd_list)
cmd_set_parser = subparsers.add_parser("set", help="Create user or change password")
- cmd_set_parser.add_argument("user", type=_valid_user)
+ cmd_set_parser.add_argument("user", type=valid_user)
cmd_set_parser.add_argument("-i", "--read-stdin", action="store_true", help="Read password from stdin")
cmd_set_parser.set_defaults(cmd=_cmd_set)
cmd_delete_parser = subparsers.add_parser("del", help="Delete user")
- cmd_delete_parser.add_argument("user", type=_valid_user)
+ cmd_delete_parser.add_argument("user", type=valid_user)
cmd_delete_parser.set_defaults(cmd=_cmd_delete)
options = parser.parse_args(argv[1:])
- options.cmd(config, options)
+ try:
+ options.cmd(config, options)
+ except ValidatorError as err:
+ raise SystemExit(str(err))
diff --git a/kvmd/apps/kvmd/__init__.py b/kvmd/apps/kvmd/__init__.py
index 3f2431d7..1ab14fd6 100644
--- a/kvmd/apps/kvmd/__init__.py
+++ b/kvmd/apps/kvmd/__init__.py
@@ -45,15 +45,15 @@ def main() -> None:
# pylint: disable=protected-access
loop = asyncio.get_event_loop()
Server(
- auth_manager=AuthManager(**config.auth._unpack_renamed()),
- info_manager=InfoManager(loop=loop, **config.info._unpack_renamed()),
+ auth_manager=AuthManager(**config.auth._unpack()),
+ info_manager=InfoManager(loop=loop, **config.info._unpack()),
log_reader=LogReader(loop=loop),
- hid=Hid(**config.hid._unpack_renamed()),
- atx=Atx(**config.atx._unpack_renamed()),
- msd=MassStorageDevice(loop=loop, **config.msd._unpack_renamed()),
- streamer=Streamer(loop=loop, **config.streamer._unpack_renamed()),
+ hid=Hid(**config.hid._unpack()),
+ atx=Atx(**config.atx._unpack()),
+ msd=MassStorageDevice(loop=loop, **config.msd._unpack()),
+ streamer=Streamer(loop=loop, **config.streamer._unpack()),
loop=loop,
- ).run(**config.server._unpack_renamed())
+ ).run(**config.server._unpack())
get_logger().info("Bye-bye")
diff --git a/kvmd/apps/kvmd/hid.py b/kvmd/apps/kvmd/hid.py
index 22d4acf3..2b41eda0 100644
--- a/kvmd/apps/kvmd/hid.py
+++ b/kvmd/apps/kvmd/hid.py
@@ -46,7 +46,7 @@ from ... import gpio
# =====
def _get_keymap() -> Dict[str, int]:
- return yaml.load(pkgutil.get_data("kvmd", "data/keymap.yaml").decode()) # type: ignore
+ return yaml.safe_load(pkgutil.get_data("kvmd", "data/keymap.yaml").decode()) # type: ignore
_KEYMAP = _get_keymap()
diff --git a/kvmd/apps/kvmd/server.py b/kvmd/apps/kvmd/server.py
index fc701c94..ae82015e 100644
--- a/kvmd/apps/kvmd/server.py
+++ b/kvmd/apps/kvmd/server.py
@@ -21,7 +21,6 @@
import os
-import re
import signal
import socket
import asyncio
@@ -36,7 +35,6 @@ from typing import Dict
from typing import Set
from typing import Callable
from typing import Optional
-from typing import Any
import aiohttp.web
import setproctitle
@@ -45,6 +43,18 @@ from ...logging import get_logger
from ...aioregion import RegionIsBusyError
+from ...validators import ValidatorError
+
+from ...validators.basic import valid_bool
+from ...validators.auth import valid_user
+from ...validators.auth import valid_passwd
+from ...validators.auth import valid_auth_token
+from ...validators.kvm import valid_atx_button
+from ...validators.kvm import valid_kvm_target
+from ...validators.kvm import valid_log_seek
+from ...validators.kvm import valid_stream_quality
+from ...validators.kvm import valid_stream_fps
+
from ... import __version__
from .auth import AuthManager
@@ -80,10 +90,6 @@ class HttpError(Exception):
pass
-class BadRequestError(HttpError):
- pass
-
-
class UnauthorizedError(HttpError):
pass
@@ -138,7 +144,7 @@ def _exposed(http_method: str, path: str, auth_required: bool=True) -> Callable:
if auth_required:
token = request.cookies.get(_COOKIE_AUTH_TOKEN, "")
if token:
- user = self._auth_manager.check(_valid_token(token))
+ user = self._auth_manager.check(valid_auth_token(token))
if not user:
raise ForbiddenError("Forbidden")
setattr(request, _ATTR_KVMD_USER, user)
@@ -149,7 +155,7 @@ def _exposed(http_method: str, path: str, auth_required: bool=True) -> Callable:
except RegionIsBusyError as err:
return _json_exception(err, 409)
- except (BadRequestError, AtxOperationError, MsdOperationError) as err:
+ except (ValidatorError, AtxOperationError, MsdOperationError) as err:
return _json_exception(err, 400)
except UnauthorizedError as err:
return _json_exception(err, 401)
@@ -178,51 +184,6 @@ def _system_task(method: Callable) -> Callable:
return wrap
-def _valid_user(user: Any) -> str:
- if isinstance(user, str):
- stripped = user.strip()
- if re.match(r"^[a-z_][a-z0-9_-]*$", stripped):
- return stripped
- raise BadRequestError("Invalid user characters %r" % (user))
-
-
-def _valid_passwd(passwd: Any) -> str:
- if isinstance(passwd, str):
- if re.match(r"[\x20-\x7e]*$", passwd):
- return passwd
- raise BadRequestError("Invalid password characters")
-
-
-def _valid_token(token: Optional[str]) -> str:
- if isinstance(token, str):
- token = token.strip().lower()
- if re.match(r"^[0-9a-f]{64}$", token):
- return token
- raise BadRequestError("Invalid auth token characters")
-
-
-def _valid_bool(name: str, flag: Optional[str]) -> bool:
- flag = str(flag).strip().lower()
- if flag in ["1", "true", "yes"]:
- return True
- elif flag in ["0", "false", "no"]:
- return False
- raise BadRequestError("Invalid param '%s'" % (name))
-
-
-def _valid_int(name: str, value: Optional[str], min_value: Optional[int]=None, max_value: Optional[int]=None) -> int:
- try:
- value_int = int(value) # type: ignore
- if (
- (min_value is not None and value_int < min_value)
- or (max_value is not None and value_int > max_value)
- ):
- raise ValueError()
- return value_int
- except Exception:
- raise BadRequestError("Invalid param %r" % (name))
-
-
class _Events(Enum):
INFO_STATE = "info_state"
HID_STATE = "hid_state"
@@ -337,8 +298,8 @@ class Server: # pylint: disable=too-many-instance-attributes
async def __auth_login_handler(self, request: aiohttp.web.Request) -> aiohttp.web.Response:
credentials = await request.post()
token = self._auth_manager.login(
- user=_valid_user(credentials.get("user", "")),
- passwd=_valid_passwd(credentials.get("passwd", "")),
+ user=valid_user(credentials.get("user", "")),
+ passwd=valid_passwd(credentials.get("passwd", "")),
)
if token:
return _json({}, set_cookies={_COOKIE_AUTH_TOKEN: token})
@@ -346,7 +307,7 @@ class Server: # pylint: disable=too-many-instance-attributes
@_exposed("POST", "/auth/logout")
async def __auth_logout_handler(self, request: aiohttp.web.Request) -> aiohttp.web.Response:
- token = _valid_token(request.cookies.get(_COOKIE_AUTH_TOKEN, ""))
+ token = valid_auth_token(request.cookies.get(_COOKIE_AUTH_TOKEN, ""))
self._auth_manager.logout(token)
return _json({})
@@ -362,8 +323,8 @@ class Server: # pylint: disable=too-many-instance-attributes
@_exposed("GET", "/log")
async def __log_handler(self, request: aiohttp.web.Request) -> aiohttp.web.StreamResponse:
- seek = _valid_int("seek", request.query.get("seek", "0"), 0)
- follow = _valid_bool("follow", request.query.get("follow", "false"))
+ seek = valid_log_seek(request.query.get("seek", "0"))
+ follow = valid_bool(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_reader.poll_log(seek, follow):
@@ -460,15 +421,12 @@ class Server: # pylint: disable=too-many-instance-attributes
@_exposed("POST", "/atx/click")
async def __atx_click_handler(self, request: aiohttp.web.Request) -> aiohttp.web.Response:
- button = request.query.get("button")
- clicker = {
+ button = valid_atx_button(request.query.get("button"))
+ await ({
"power": self.__atx.click_power,
"power_long": self.__atx.click_power_long,
"reset": self.__atx.click_reset,
- }.get(button)
- if not clicker:
- raise BadRequestError("Invalid param 'button'")
- await clicker()
+ }[button])()
return _json({"clicked": button})
# ===== MSD
@@ -479,13 +437,11 @@ class Server: # pylint: disable=too-many-instance-attributes
@_exposed("POST", "/msd/connect")
async def __msd_connect_handler(self, request: aiohttp.web.Request) -> aiohttp.web.Response:
- to = request.query.get("to")
- if to == "kvm":
- return _json(await self.__msd.connect_to_kvm())
- elif to == "server":
- return _json(await self.__msd.connect_to_pc())
- else:
- raise BadRequestError("Invalid param 'to'")
+ to = valid_kvm_target(request.query.get("to"))
+ return _json(await ({
+ "kvm": self.__msd.connect_to_kvm,
+ "server": self.__msd.connect_to_pc,
+ }[to])())
@_exposed("POST", "/msd/write")
async def __msd_write_handler(self, request: aiohttp.web.Request) -> aiohttp.web.Response:
@@ -496,12 +452,12 @@ class Server: # pylint: disable=too-many-instance-attributes
async with self.__msd:
field = await reader.next()
if not field or field.name != "image_name":
- raise BadRequestError("Missing 'image_name' field")
+ raise ValidatorError("Missing 'image_name' field")
image_name = (await field.read()).decode("utf-8")[:256]
field = await reader.next()
if not field or field.name != "image_data":
- raise BadRequestError("Missing 'image_data' field")
+ raise ValidatorError("Missing 'image_data' field")
logger.info("Writing image %r to mass-storage device ...", image_name)
await self.__msd.write_image_info(image_name, False)
@@ -530,8 +486,8 @@ class Server: # pylint: disable=too-many-instance-attributes
@_exposed("POST", "/streamer/set_params")
async def __streamer_set_params_handler(self, request: aiohttp.web.Request) -> aiohttp.web.Response:
for (name, validator) in [
- ("quality", lambda arg: _valid_int("quality", arg, 1, 100)),
- ("desired_fps", lambda arg: _valid_int("desired_fps", arg, 0, 30)),
+ ("quality", valid_stream_quality),
+ ("desired_fps", valid_stream_fps),
]:
value = request.query.get(name)
if value:
diff --git a/kvmd/apps/kvmd/streamer.py b/kvmd/apps/kvmd/streamer.py
index 71662959..771d5be3 100644
--- a/kvmd/apps/kvmd/streamer.py
+++ b/kvmd/apps/kvmd/streamer.py
@@ -63,8 +63,8 @@ class Streamer: # pylint: disable=too-many-instance-attributes
loop: asyncio.AbstractEventLoop,
) -> None:
- self.__cap_pin = (gpio.set_output(cap_pin) if cap_pin > 0 else 0)
- self.__conv_pin = (gpio.set_output(conv_pin) if conv_pin > 0 else 0)
+ self.__cap_pin = (gpio.set_output(cap_pin) if cap_pin >= 0 else -1)
+ self.__conv_pin = (gpio.set_output(conv_pin) if conv_pin >= 0 else -1)
self.__sync_delay = sync_delay
self.__init_delay = init_delay
@@ -179,9 +179,9 @@ class Streamer: # pylint: disable=too-many-instance-attributes
async def __set_hw_enabled(self, enabled: bool) -> None:
# XXX: This sequence is very important to enable converter and cap board
- if self.__cap_pin > 0:
+ if self.__cap_pin >= 0:
gpio.write(self.__cap_pin, enabled)
- if self.__conv_pin > 0:
+ if self.__conv_pin >= 0:
if enabled:
await asyncio.sleep(self.__sync_delay)
gpio.write(self.__conv_pin, enabled)