From b9ff755f6d1b3d4096465afc81794dc8e2ef3802 Mon Sep 17 00:00:00 2001 From: mofeng-git Date: Mon, 2 Sep 2024 13:41:00 +0000 Subject: [PATCH] =?UTF-8?q?=E9=80=82=E5=BA=94=E7=9A=84=20docker=20?= =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .dockerignore | 1 - .gitignore | 1 - build/Dockerfile | 76 +++++++++++++++++++++++++++++ build/supervisord.conf | 58 ++++++++++++++++++++++ build/v2-hdmiusb-rpi4.override.yaml | 65 ++++++++++++++++++++++++ configs/nginx/nginx.conf.mako | 5 +- extras/hw_info/model | 1 + extras/hw_info/serial-number | 1 + kvmd/apps/kvmd/api/log.py | 16 +++--- kvmd/apps/kvmd/info/hw.py | 6 ++- kvmd/apps/kvmd/logreader.py | 60 ++++++++++++++--------- scripts/kvmd-gencert | 2 +- 12 files changed, 254 insertions(+), 38 deletions(-) create mode 100644 build/Dockerfile create mode 100644 build/supervisord.conf create mode 100644 build/v2-hdmiusb-rpi4.override.yaml create mode 100644 extras/hw_info/model create mode 100644 extras/hw_info/serial-number diff --git a/.dockerignore b/.dockerignore index ae2e9675..cce3f836 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,7 +1,6 @@ /pkg/ /src/ /site/ -/build/ /dist/ /kvmd.egg-info/ /testenv/run/ diff --git a/.gitignore b/.gitignore index 3127051e..508aa944 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ /pkg/ /src/ /site/ -/build/ /dist/ /kvmd.egg-info/ /config.mk diff --git a/build/Dockerfile b/build/Dockerfile new file mode 100644 index 00000000..a4c26c58 --- /dev/null +++ b/build/Dockerfile @@ -0,0 +1,76 @@ +FROM hub.atomgit.com/amd64/python:3.12.0rc2-slim-bookworm as builder + +RUN sed -i 's/deb.debian.org/mirrors.tuna.tsinghua.edu.cn/' /etc/apt/sources.list.d/debian.sources \ + && apt-get update \ + && apt-get install -y --no-install-recommends build-essential libevent-dev libjpeg-dev libbsd-dev git pkg-config \ + && rm -rf /var/lib/apt/lists/* + +RUN pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple \ + && pip install --no-cache-dir --root-user-action=ignore --disable-pip-version-check build \ + && pip wheel --wheel-dir=/tmp/wheel/ aiofiles aiohttp appdirs asn1crypto async_lru async-timeout bottle cffi chardet click colorama cryptography \ + dbus_next gpiod hidapi idna mako marshmallow more-itertools multidict netifaces packaging passlib pillow ply psutil pycparser \ + pyelftools pyghmi pygments pyparsing pyotp qrcode requests semantic-version setproctitle setuptools six spidev \ + tabulate urllib3 wrapt xlib yarl pyserial pyyaml zstandard supervisor \ + && pip cache purge + +RUN git clone --depth=1 https://gh.xmly.dev/https://github.com/pikvm/ustreamer /tmp/ustreamer \ + #&& git clone --depth=1 https://github.com/pikvm/ustreamer /tmp/ustreamer \ + && make -j WITH_PYTHON=1 -C /tmp/ustreamer \ + && /tmp/ustreamer/ustreamer -v + +RUN mkdir /tmp/lib \ + && cp /lib/*-linux-gnu/libevent_core-*.so.7 /lib/*-linux-gnu/libbsd.so.0 /lib/*-linux-gnu/libevent_pthreads-*.so.7 \ + /lib/*-linux-gnu/libevent-*.so.7 /lib/*-linux-gnu/libjpeg.so.62 /tmp/lib/ \ + && cp /tmp/ustreamer/python/dist/*.whl /tmp/wheel/ + +FROM hub.atomgit.com/amd64/python:3.12.0rc2-slim-bookworm + +LABEL maintainer="mofeng654321@hotmail.com" + +COPY --from=builder /tmp/lib/* /tmp/lib/ +COPY --from=builder /tmp/ustreamer/ustreamer /tmp/ustreamer/ustreamer-dump /usr/local/bin/ +COPY --from=builder /tmp/wheel/*.whl /tmp/wheel/ + +ENV PYTHONDONTWRITEBYTECODE 1 +ENV PYTHONUNBUFFERED 1 + + +RUN cp /tmp/lib/* /lib/*-linux-gnu/ \ + && pip install --no-cache-dir --root-user-action=ignore --disable-pip-version-check /tmp/wheel/*.whl \ + && rm -rf /tmp/lib /tmp/wheel + +RUN sed -i 's/deb.debian.org/mirrors.tuna.tsinghua.edu.cn/' /etc/apt/sources.list.d/debian.sources \ + && apt-get update \ + && apt-get install -y --no-install-recommends libxkbcommon-x11-0 nginx tesseract-ocr tesseract-ocr-eng tesseract-ocr-chi-sim iptables sudo \ + && rm -rf /var/lib/apt/lists/* + +RUN mkdir -p \ + /etc/kvmd/nginx \ + /etc/kvmd/vnc \ + /etc/kvmd/override.d\ + /var/lib/kvmd/msd \ + /var/lib/kvmd/pst/data \ + /opt/vc/bin \ + /run/kvmd \ + /tmp/kvmd-nginx + +COPY testenv/fakes/vcgencmd /usr/bin/ +COPY extras/ /usr/share/kvmd/extras/ +COPY web/ /usr/share/kvmd/web/ +COPY testenv/platform/ scripts/kvmd-gencert /usr/share/kvmd/ +COPY contrib/keymaps /usr/share/kvmd/keymaps +COPY kvmd/ /kvmd +COPY configs/kvmd/*.yaml configs/kvmd/*passwd build/supervisord.conf /etc/kvmd/ +COPY configs/nginx/* /etc/kvmd/nginx/ +COPY build/v2-hdmiusb-rpi4.override.yaml /etc/kvmd/override.yaml +COPY configs/kvmd/main/v2-hdmiusb-rpi4.yaml /etc/kvmd/main.yaml + +RUN touch /run/kvmd/ustreamer.sock \ + && touch /etc/kvmd/totp.secret \ + && /usr/share/kvmd/kvmd-gencert --do-the-thing \ + && /usr/share/kvmd/kvmd-gencert --do-the-thing --vnc \ + && ln -sf /usr/share/tesseract-ocr/*/tessdata /usr/share/tessdata \ + && python -m kvmd.apps.ngxmkconf /etc/kvmd/nginx/nginx.conf.mako /etc/kvmd/nginx/nginx.conf + +EXPOSE 4430 +CMD ["supervisord", "-c", "/etc/kvmd/supervisord.conf"] \ No newline at end of file diff --git a/build/supervisord.conf b/build/supervisord.conf new file mode 100644 index 00000000..bb902a4e --- /dev/null +++ b/build/supervisord.conf @@ -0,0 +1,58 @@ +[unix_http_server] +file=/tmp/supervisor.sock + +[supervisord] +nodaemon=true +user=root + +[rpcinterface:supervisor] +supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface + +[inet_http_server] +port=127.0.0.1:9001 + +[supervisorctl] +serverurl=unix:///tmp/supervisor.sock + +[program:kvmd] +command=python -m kvmd.apps.kvmd --run +directory=/ +autostart=true +autorestart=true +priority=10 +stopasgroup=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes = 0 +redirect_stderr=true + +[program:kvmd-vnc] +command=python -m kvmd.apps.vnc --run +directory=/ +autostart=true +autorestart=true +priority=11 +stopasgroup=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes = 0 +redirect_stderr=true + +[program:kvmd-ipmi] +command=python -m kvmd.apps.ipmi --run +directory=/ +autostart=true +autorestart=true +priority=12 +stopasgroup=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes = 0 +redirect_stderr=true + +[program:kvmd-nginx] +command=nginx -c /etc/kvmd/nginx/nginx.conf -g 'daemon off;user root; error_log stderr;' +autostart=true +autorestart=true +priority=100 +stopasgroup=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes = 0 +redirect_stderr=true \ No newline at end of file diff --git a/build/v2-hdmiusb-rpi4.override.yaml b/build/v2-hdmiusb-rpi4.override.yaml new file mode 100644 index 00000000..fe4a31a9 --- /dev/null +++ b/build/v2-hdmiusb-rpi4.override.yaml @@ -0,0 +1,65 @@ +kvmd: + server: + unix_mode: 0666 + + atx: + type: disabled + + hid: + type: ch9329 + device: /dev/kvmd-hid + + msd: + type: disabled + + streamer: + cmd: + - "/usr/local/bin/ustreamer" + - "--device=/dev/kvmd-video" + - "--persistent" + - "--format=mjpeg" + - "--resolution={resolution}" + - "--desired-fps={desired_fps}" + - "--drop-same-frames=30" + - "--last-as-blank=0" + - "--unix={unix}" + - "--unix-rm" + - "--unix-mode=0666" + - "--exit-on-parent-death" + - "--process-name-prefix={process_name_prefix}" + - "--notify-parent" + - "--no-log-colors" + +vnc: + keymap: /usr/share/kvmd/keymaps/ru + + auth: + vncauth: + enabled: true + + memsink: + jpeg: + sink: "" + h264: + sink: "" + +otgnet: + commands: + post_start_cmd: + - "/bin/true" + pre_stop_cmd: + - "/bin/true" + +nginx: + http: + port: 8080 + https: + port: 4430 + +janus: + cmd: + - "/bin/true" + +languages: + console: zh + web: zh diff --git a/configs/nginx/nginx.conf.mako b/configs/nginx/nginx.conf.mako index 65b46db1..9158eda2 100644 --- a/configs/nginx/nginx.conf.mako +++ b/configs/nginx/nginx.conf.mako @@ -54,11 +54,10 @@ http { } server { - listen ${https_port} ssl; + listen ${https_port} ssl http2; % if ipv6_enabled: - listen [::]:${https_port} ssl; + listen [::]:${https_port} ssl http2; % endif - http2 on; include /etc/kvmd/nginx/ssl.conf; include /etc/kvmd/nginx/kvmd.ctx-server.conf; include /usr/share/kvmd/extras/*/nginx.ctx-server.conf; diff --git a/extras/hw_info/model b/extras/hw_info/model new file mode 100644 index 00000000..f9142eb3 --- /dev/null +++ b/extras/hw_info/model @@ -0,0 +1 @@ +Docker diff --git a/extras/hw_info/serial-number b/extras/hw_info/serial-number new file mode 100644 index 00000000..3c8f4b31 --- /dev/null +++ b/extras/hw_info/serial-number @@ -0,0 +1 @@ +docker1000000000 \ No newline at end of file diff --git a/kvmd/apps/kvmd/api/log.py b/kvmd/apps/kvmd/api/log.py index 5bead7f7..7eb3204d 100644 --- a/kvmd/apps/kvmd/api/log.py +++ b/kvmd/apps/kvmd/api/log.py @@ -53,10 +53,14 @@ class LogApi: seek = valid_log_seek(request.query.get("seek", 0)) follow = valid_bool(request.query.get("follow", False)) response = await start_streaming(request, "text/plain") - async for record in self.__log_reader.poll_log(seek, follow): - await response.write(("[%s %s] --- %s" % ( - record["dt"].strftime("%Y-%m-%d %H:%M:%S"), - record["service"], - record["msg"], - )).encode("utf-8") + b"\r\n") + try: + async for record in self.__log_reader.poll_log(seek, follow): + await response.write(("[%s %s] --- %s" % ( + record["dt"].strftime("%Y-%m-%d %H:%M:%S"), + record["service"], + record["msg"], + )).encode("utf-8") + b"\r\n") + except Exception as e: + await response.write(f"Module systemd.journal unavailable, switch to supervisord.\n{record}".encode("utf-8")) + return response return response diff --git a/kvmd/apps/kvmd/info/hw.py b/kvmd/apps/kvmd/info/hw.py index 1ff61145..2c843173 100644 --- a/kvmd/apps/kvmd/info/hw.py +++ b/kvmd/apps/kvmd/info/hw.py @@ -111,10 +111,12 @@ class HwInfoSubmanager(BaseInfoSubmanager): async def __read_dt_file(self, name: str) -> (str | None): if name not in self.__dt_cache: path = os.path.join(f"{env.PROCFS_PREFIX}/proc/device-tree", name) + if not os.path.exists(path): + path = os.path.join(f"{env.PROCFS_PREFIX}/usr/share/kvmd/extras/hw_info/", name) try: self.__dt_cache[name] = (await aiotools.read_file(path)).strip(" \t\r\n\0") except Exception as err: - get_logger(0).error("Can't read DT %s from %s: %s", name, path, err) + get_logger(0).warn("Can't read DT %s from %s: %s", name, path, err) return None return self.__dt_cache[name] @@ -141,7 +143,7 @@ class HwInfoSubmanager(BaseInfoSubmanager): try: return int((await aiotools.read_file(temp_path)).strip()) / 1000 except Exception as err: - get_logger(0).error("Can't read CPU temp from %s: %s", temp_path, err) + get_logger(0).warn("Can't read CPU temp from %s: %s", temp_path, err) return None async def __get_cpu_percent(self) -> (float | None): diff --git a/kvmd/apps/kvmd/logreader.py b/kvmd/apps/kvmd/logreader.py index 6de2819b..9b1c4a8c 100644 --- a/kvmd/apps/kvmd/logreader.py +++ b/kvmd/apps/kvmd/logreader.py @@ -25,39 +25,51 @@ import asyncio import time from typing import AsyncGenerator +from xmlrpc.client import ServerProxy -import systemd.journal +from ...logging import get_logger +try: + module_name = "systemd.journal" + module = __import__(module_name) +except ImportError: + us_systemd_journal = False + get_logger(0).error("Failed to import module: %s", module_name) # ===== class LogReader: async def poll_log(self, seek: int, follow: bool) -> AsyncGenerator[dict, None]: - reader = systemd.journal.Reader() - reader.this_boot() - # XXX: Из-за смены ID машины в bootconfig это не работает при первой загрузке. - # reader.this_machine() - reader.log_level(systemd.journal.LOG_DEBUG) + if us_systemd_journal: + reader = systemd.journal.Reader() # type: ignore + reader.this_boot() + # XXX: Из-за смены ID машины в bootconfig это не работает при первой загрузке. + # reader.this_machine() + reader.log_level(systemd.journal.LOG_DEBUG) # type: ignore + services = set( + service + for service in systemd.journal.Reader().query_unique("_SYSTEMD_UNIT") # type: ignore + if re.match(r"kvmd(-\w+)*\.service", service) + ).union(["kvmd.service"]) - services = set( - service - for service in systemd.journal.Reader().query_unique("_SYSTEMD_UNIT") - if re.match(r"kvmd(-\w+)*\.service", service) - ).union(["kvmd.service"]) + for service in services: + reader.add_match(_SYSTEMD_UNIT=service) + if seek > 0: + reader.seek_realtime(float(time.time() - seek)) - for service in services: - reader.add_match(_SYSTEMD_UNIT=service) - if seek > 0: - reader.seek_realtime(float(time.time() - seek)) - - for entry in reader: - yield self.__entry_to_record(entry) - - while follow: - entry = reader.get_next() - if entry: + for entry in reader: yield self.__entry_to_record(entry) - else: - await asyncio.sleep(1) + + while follow: + entry = reader.get_next() + if entry: + yield self.__entry_to_record(entry) + else: + await asyncio.sleep(1) + else: + server = ServerProxy('http://127.0.0.1:9001/RPC2') + log_entries = server.supervisor.readLog(0,0) + yield log_entries + def __entry_to_record(self, entry: dict) -> dict[str, dict]: return { diff --git a/scripts/kvmd-gencert b/scripts/kvmd-gencert index 7bd95cf2..b9c6e4f0 100755 --- a/scripts/kvmd-gencert +++ b/scripts/kvmd-gencert @@ -57,7 +57,7 @@ openssl ecparam -out server.key -name prime256v1 -genkey openssl req -new -x509 -sha256 -nodes -key server.key -out server.crt -days 3650 \ -subj "/C=RU/ST=Moscow/L=Moscow/O=PiKVM/OU=PiKVM/CN=localhost" -chown "root:kvmd-$target" "$path"/* +#chown "root:kvmd-$target" "$path"/* chmod 440 "$path/server.key" chmod 444 "$path/server.crt" chmod 755 "$path"