diff options
author | Maxim Devaev <[email protected]> | 2022-06-15 02:43:14 +0300 |
---|---|---|
committer | Maxim Devaev <[email protected]> | 2022-06-15 15:58:29 +0300 |
commit | 53e64fe1518d7dd8d53c6e04846cee1601c2c754 (patch) | |
tree | 41b0e86645293a99f9b20e32a9209c1bc2b1bd81 | |
parent | 88c7796551010faf30e7f7f843432af919ea7ce0 (diff) |
pst server
-rw-r--r-- | configs/os/services/kvmd-pst.service | 16 | ||||
-rw-r--r-- | kvmd/apps/__init__.py | 19 | ||||
-rw-r--r-- | kvmd/apps/pst/__init__.py | 46 | ||||
-rw-r--r-- | kvmd/apps/pst/__main__.py | 24 | ||||
-rw-r--r-- | kvmd/apps/pst/server.py | 126 | ||||
-rwxr-xr-x | setup.py | 4 | ||||
-rw-r--r-- | testenv/Dockerfile | 1 | ||||
-rw-r--r-- | testenv/v2-hdmi-rpi4.override.yaml | 3 | ||||
-rw-r--r-- | testenv/v2-hdmiusb-rpi4.override.yaml | 3 |
9 files changed, 241 insertions, 1 deletions
diff --git a/configs/os/services/kvmd-pst.service b/configs/os/services/kvmd-pst.service new file mode 100644 index 00000000..20985338 --- /dev/null +++ b/configs/os/services/kvmd-pst.service @@ -0,0 +1,16 @@ +[Unit] +Description=PiKVM - The KVMD persistent storage manager +Before=kvmd.service + +[Service] +User=kvmd-pst +Group=kvmd-pst +Type=simple +Restart=always +RestartSec=3 + +ExecStart=/usr/bin/kvmd-pst --run +TimeoutStopSec=3 + +[Install] +WantedBy=multi-user.target diff --git a/kvmd/apps/__init__.py b/kvmd/apps/__init__.py index a13bdeb0..89907437 100644 --- a/kvmd/apps/__init__.py +++ b/kvmd/apps/__init__.py @@ -485,6 +485,25 @@ def _get_config_scheme() -> Dict: }, }, + "pst": { + "server": { + "unix": Option("/run/kvmd/pst.sock", type=valid_abs_path, unpack_as="unix_path"), + "unix_rm": Option(True, type=valid_bool), + "unix_mode": Option(0o660, type=valid_unix_mode), + "heartbeat": Option(15.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'"), + }, + + "storage": Option("/var/lib/kvmd/pst", type=valid_abs_dir, unpack_as="storage_path"), + "ro_retries_delay": Option(10.0, type=valid_float_f01), + + "remount_cmd": Option([ + "/usr/bin/sudo", "--non-interactive", + "/usr/bin/kvmd-helper-pst-remount", "{mode}", + ], type=valid_command), + }, + "otg": { "vendor_id": Option(0x1D6B, type=valid_otg_id), # Linux Foundation "product_id": Option(0x0104, type=valid_otg_id), # Multifunction Composite Gadget diff --git a/kvmd/apps/pst/__init__.py b/kvmd/apps/pst/__init__.py new file mode 100644 index 00000000..2bb29a08 --- /dev/null +++ b/kvmd/apps/pst/__init__.py @@ -0,0 +1,46 @@ +# ========================================================================== # +# # +# KVMD - The main PiKVM daemon. # +# # +# Copyright (C) 2018-2022 Maxim Devaev <[email protected]> # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see <https://www.gnu.org/licenses/>. # +# # +# ========================================================================== # + + +from typing import List +from typing import Optional + +from ...logging import get_logger + +from .. import init + +from .server import PstServer + + +# ===== +def main(argv: Optional[List[str]]=None) -> None: + config = init( + prog="kvmd-pst", + description="The KVMD persistent storage manager", + argv=argv, + check_run=True, + )[2] + + PstServer( + **config.pst._unpack(ignore="server"), + ).run(**config.pst.server._unpack()) + + get_logger(0).info("Bye-bye") diff --git a/kvmd/apps/pst/__main__.py b/kvmd/apps/pst/__main__.py new file mode 100644 index 00000000..3849d1b9 --- /dev/null +++ b/kvmd/apps/pst/__main__.py @@ -0,0 +1,24 @@ +# ========================================================================== # +# # +# KVMD - The main PiKVM daemon. # +# # +# Copyright (C) 2018-2022 Maxim Devaev <[email protected]> # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see <https://www.gnu.org/licenses/>. # +# # +# ========================================================================== # + + +from . import main +main() diff --git a/kvmd/apps/pst/server.py b/kvmd/apps/pst/server.py new file mode 100644 index 00000000..84a62bda --- /dev/null +++ b/kvmd/apps/pst/server.py @@ -0,0 +1,126 @@ +# ========================================================================== # +# # +# KVMD - The main PiKVM daemon. # +# # +# Copyright (C) 2018-2022 Maxim Devaev <[email protected]> # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see <https://www.gnu.org/licenses/>. # +# # +# ========================================================================== # + + +import os +import asyncio + +from typing import List +from typing import Dict + +from aiohttp.web import Request +from aiohttp.web import WebSocketResponse + +from ...logging import get_logger + +from ... import aiotools +from ... import aiohelpers + +from ...htserver import exposed_http +from ...htserver import exposed_ws +from ...htserver import WsSession +from ...htserver import HttpServer + + +# ===== +class PstServer(HttpServer): # pylint: disable=too-many-arguments,too-many-instance-attributes + def __init__( # pylint: disable=too-many-arguments,too-many-locals + self, + storage_path: str, + ro_retries_delay: float, + remount_cmd: List[str], + ) -> None: + + super().__init__() + + self.__data_path = os.path.join(storage_path, "data") + self.__ro_retries_delay = ro_retries_delay + self.__remount_cmd = remount_cmd + + self.__notifier = aiotools.AioNotifier() + + # ===== WEBSOCKET + + @exposed_http("GET", "/ws") + async def __ws_handler(self, request: Request) -> WebSocketResponse: + async with self._ws_session(request) as ws: + await ws.send_event("loop", {}) + return (await self._ws_loop(ws)) + + @exposed_ws("ping") + async def __ws_ping_handler(self, ws: WsSession, _: Dict) -> None: + await ws.send_event("pong", {}) + + # ===== SYSTEM STUFF + + async def _init_app(self) -> None: + if (await self.__remount_storage(True)): + await self.__remount_storage(False) + aiotools.create_deadly_task("Controller", self.__controller()) + self._add_exposed(self) + + async def _on_shutdown(self) -> None: + logger = get_logger(0) + logger.info("Stopping system tasks ...") + await aiotools.stop_all_deadly_tasks() + logger.info("Disconnecting clients ...") + await self.__broadcast_storage_state(False) + await self._close_all_wss() + logger.info("On-Shutdown complete") + + async def _on_cleanup(self) -> None: + logger = get_logger(0) + await self.__remount_storage(False) + logger.info("On-Cleanup complete") + + async def _on_ws_opened(self) -> None: + await self.__notifier.notify() + + async def _on_ws_closed(self) -> None: + await self.__notifier.notify() + + # ===== SYSTEM TASKS + + async def __controller(self) -> None: + prev = False + while True: + cur = self.__has_clients() + if not prev and cur: + await self.__broadcast_storage_state(await self.__remount_storage(True)) + elif prev and not cur: + while not (await self.__remount_storage(False)): + if self.__has_clients(): + continue + await asyncio.sleep(self.__ro_retries_delay) + prev = cur + await self.__notifier.wait() + + def __has_clients(self) -> bool: + return bool(self._get_wss()) + + async def __broadcast_storage_state(self, write_allowed: bool) -> None: + await self._broadcast_ws_event("storage_state", { + "data": {"path": self.__data_path}, + "write_allowed": write_allowed, + }) + + async def __remount_storage(self, rw: bool) -> bool: + return (await aiohelpers.remount("PST", self.__remount_cmd, rw)) @@ -95,6 +95,7 @@ def main() -> None: "kvmd.apps.kvmd", "kvmd.apps.kvmd.info", "kvmd.apps.kvmd.api", + "kvmd.apps.pst", "kvmd.apps.otg", "kvmd.apps.otg.hid", "kvmd.apps.otgnet", @@ -119,6 +120,7 @@ def main() -> None: entry_points={ "console_scripts": [ "kvmd = kvmd.apps.kvmd:main", + "kvmd-pst = kvmd.apps.pst:main", "kvmd-otg = kvmd.apps.otg:main", "kvmd-otgnet = kvmd.apps.otgnet:main", "kvmd-otgmsd = kvmd.apps.otgmsd:main", @@ -130,8 +132,8 @@ def main() -> None: "kvmd-vnc = kvmd.apps.vnc:main", "kvmd-janus = kvmd.apps.janus:main", "kvmd-watchdog = kvmd.apps.watchdog:main", - "kvmd-helper-otgmsd-remount = kvmd.helpers.remount:main", "kvmd-helper-pst-remount = kvmd.helpers.remount:main", + "kvmd-helper-otgmsd-remount = kvmd.helpers.remount:main", ], }, diff --git a/testenv/Dockerfile b/testenv/Dockerfile index d8122fae..36d0fb3a 100644 --- a/testenv/Dockerfile +++ b/testenv/Dockerfile @@ -98,6 +98,7 @@ RUN git clone https://github.com/pikvm/ustreamer \ RUN mkdir -p \ /etc/kvmd/{nginx,vnc} \ /var/lib/kvmd/msd/{images,meta} \ + /var/lib/kvmd/pst/data \ /opt/vc/bin COPY testenv/fakes/vcgencmd /opt/vc/bin/ diff --git a/testenv/v2-hdmi-rpi4.override.yaml b/testenv/v2-hdmi-rpi4.override.yaml index 2625f081..adb67907 100644 --- a/testenv/v2-hdmi-rpi4.override.yaml +++ b/testenv/v2-hdmi-rpi4.override.yaml @@ -121,6 +121,9 @@ kvmd: - ["#Cmd #1:", "cmd1|confirm|Run 'sleep 5'"] - ["#Cmd #2:", "cmd2|Run 'ls -l'"] +pst: + remount_cmd: /bin/true + vnc: keymap: /usr/share/kvmd/keymaps/ru diff --git a/testenv/v2-hdmiusb-rpi4.override.yaml b/testenv/v2-hdmiusb-rpi4.override.yaml index 958bfd34..f1d57089 100644 --- a/testenv/v2-hdmiusb-rpi4.override.yaml +++ b/testenv/v2-hdmiusb-rpi4.override.yaml @@ -33,6 +33,9 @@ kvmd: - "--notify-parent" - "--no-log-colors" +pst: + remount_cmd: /bin/true + vnc: keymap: /usr/share/kvmd/keymaps/ru |