Skip to content

Commit

Permalink
validators, tests
Browse files Browse the repository at this point in the history
  • Loading branch information
mdevaev committed Apr 6, 2019
1 parent 73e04b7 commit 1d75b73
Show file tree
Hide file tree
Showing 44 changed files with 1,619 additions and 314 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
/kvmd.egg-info/
/testenv/.tox/
/testenv/.mypy_cache/
/testenv/.coverage
/v*.tar.gz
/*.pkg.tar.xz
/*.egg-info
Expand Down
33 changes: 22 additions & 11 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
-include testenv/config.mk

TESTENV_IMAGE ?= kvmd-testenv
TESTENV_HID ?= /dev/ttyS10
TESTENV_VIDEO ?= /dev/video0
Expand All @@ -11,23 +13,32 @@ all:


tox: _testenv
- docker run --rm \
--volume `pwd`:/kvmd \
-it $(TESTENV_IMAGE) bash -c "cd kvmd && tox -c testenv/tox.ini"
docker run --rm \
--volume `pwd`:/src:ro \
--volume `pwd`/testenv:/src/testenv:rw \
--volume `pwd`/extras:/usr/share/kvmd/extras:ro \
--volume `pwd`/configs:/usr/share/kvmd/configs.default:ro \
-it $(TESTENV_IMAGE) bash -c " \
cp /usr/share/kvmd/configs.default/kvmd/*.yaml /etc/kvmd \
&& cp /usr/share/kvmd/configs.default/kvmd/htpasswd /etc/kvmd \
&& cp /src/testenv/main.yaml /etc/kvmd \
&& cd /src \
&& tox -c testenv/tox.ini \
"


run:
make _run TESTENV_CMD="python -m kvmd.apps.kvmd"
make _run_app TESTENV_CMD="python -m kvmd.apps.kvmd"
run-cleanup:
make _run TESTENV_CMD="python -m kvmd.apps.cleanup"
make _run_app TESTENV_CMD="python -m kvmd.apps.cleanup"
run-no-cache:
make _run TESTENV_CMD="python -m kvmd.apps.kvmd" TESTENV_OPTS=--no-cache
make _run_app TESTENV_CMD="python -m kvmd.apps.kvmd" TESTENV_OPTS=--no-cache


shell:
make _run
make _run_app
shell-no-cache:
make _run TESTENV_OPTS=--no-cache
make _run_app TESTENV_OPTS=--no-cache


regen:
Expand Down Expand Up @@ -61,15 +72,15 @@ clean:

clean-all: _testenv clean
- docker run --rm \
--volume `pwd`:/kvmd \
-it $(TESTENV_IMAGE) bash -c "cd kvmd && rm -rf testenv/{.tox,.mypy_cache}"
--volume `pwd`:/src \
-it $(TESTENV_IMAGE) bash -c "cd src && rm -rf testenv/{.tox,.mypy_cache,.coverage}"


_testenv:
docker build $(TESTENV_OPTS) --rm --tag $(TESTENV_IMAGE) -f testenv/Dockerfile .


_run: _testenv
_run_app: _testenv
sudo modprobe loop
- docker run --rm \
--volume `pwd`/kvmd:/kvmd:ro \
Expand Down
1 change: 1 addition & 0 deletions PKGBUILD
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ depends=(
python-systemd
python-dbus
python-pygments
psmisc
v4l-utils
nginx-mainline
openssl
Expand Down
197 changes: 91 additions & 106 deletions kvmd/apps/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,46 +29,73 @@
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
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)
Expand Down Expand Up @@ -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
},
},

Expand Down
9 changes: 6 additions & 3 deletions kvmd/apps/cleanup/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
import subprocess
import time

from typing import List
from typing import Optional

from ...logging import get_logger

from ... import gpio
Expand All @@ -32,8 +35,8 @@


# =====
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 ...")
Expand All @@ -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)

Expand Down
Loading

0 comments on commit 1d75b73

Please sign in to comment.