# 192.168.1.37 监控与定时任务技术说明 版本:v1.3,2026 年 11 月 9 日。新增显卡数量设置和夜间节电计划,详见第 12 节。 ## 1. 访问与登录 设备监控页面:**[http://192.168.1.37:8090](http://192.168.1.37:8090)** 定时任务入口在“问题与维护”下方,也可通过左侧“定时任务”菜单定位。手机底部有同名入口。 硬件监控仍可直接查看。定时任务能够实际执行服务器操作,因此使用独立的管理员账号登录: - 账号:`admin`。 - 初始密码:见交付的 `定时任务管理员登录信息.txt`,本说明不包含密码。 - 登录最长有效 8 小时;修改密码后所有任务管理登录均失效。 - 此密码与 SSH root 密码相互独立,本次没有修改 SSH 密码。 页面只开放在原有局域网访问范围内。当前使用局域网 HTTP,适用于可信局域网;不应将这个具备 root 任务执行能力的入口直接映射到公网。将来如需远程公开访问,应先配置 HTTPS 和适当的访问控制。 ## 2. 本次新增功能 “定时任务”支持: - 新增、编辑、启用、停用和删除任务。 - 立即执行一次;这不会自动启用任务的定时计划。 - 停止本次执行;与停用后续调度分开处理。 - 查看下次计划时间、最近执行结果和是否正在执行。 - 查看单任务执行记录,以及包含已删除任务的全局执行记录。 - 查看开始/结束时间、耗时、退出码、标准输出和错误输出。 - 管理员登录、退出及修改密码。 执行方式有三种: | 执行方式 | 如何填写 | 说明 | |---|---|---| | 直接输入命令 | 例如 `/usr/bin/nvidia-smi` | 使用 Bash 执行,支持管道和多行命令。 | | 粘贴脚本内容 | 选择 Bash 或 Python 3,粘贴代码 | 可以导入本地 `.sh`、`.py` 或文本脚本到编辑框;保存后才会传给服务器。 | | 服务器上的脚本文件 | 填写绝对路径和可选参数 | 可选择 Bash、Python 3 或按文件自身方式执行;最后一种要求文件具有执行权限。 | 执行身份固定为 **root**,因为重启服务器和维护证书通常需要管理员权限。它不是隔离的脚本沙箱:应只运行自己理解并信任的命令。 工作目录默认 `/root`,使用非交互执行环境,不继承某个 SSH 窗口的虚拟环境。PATH 包含常用系统目录和 `/snap/bin`;如需 Conda、venv 或自定义环境变量,应在脚本内明确设置。 ## 3. 第一次添加任务 1. 刷新页面,打开“定时任务”,点击“登录管理”。 2. 使用交付文件中的 `admin` 与初始密码登录。 3. 点击“新增任务”,填写任务名称。 4. 选择执行方式,输入命令、脚本或服务器脚本路径。 5. 设置执行周期,点击“检查执行周期”核对下次时间。 6. 保持“保存后启用”不勾选,先保存为停用状态。 7. 如适合测试,点击“执行一次”,确认执行内容,查看执行日志。 8. 核对结果后,再启用计划。 保存任务本身不会立即执行脚本。新任务默认停用。退出登录、关闭网页或重启网页服务不会停止已经启用的系统计划。 ### 常用模板 新增窗口提供: - **显卡状态检查**:读取 `nvidia-smi`,结果保存在执行日志中。 - **服务器定时重启**:示例命令为 `systemctl reboot`,默认周期是每周日 03:00,默认停用。启用或立即执行前都会提示可能中断计算任务。 - **SSL 证书续签示例**:检查已有的 Certbot/acme.sh 工具并调用其续签入口。它不会安装工具、申请新域名或替用户配置证书。先确认现有续签配置,以及是否已经有其他自动续签任务;Web 服务的重新加载应按实际服务配置。 本次部署没有创建或启用实际的服务器重启任务和证书续签任务,也没有执行这两类操作。 ## 4. 执行周期 所有日历时间按 **Asia/Shanghai,北京时间 UTC+8** 处理,页面显示服务器当前时间。 | 周期 | 规则 | |---|---| | 每天 | 指定时、分。 | | 每周 | 可选择一个或多个星期及时间。 | | 每月 | 选择 1 至 31 日和时间;当月不存在该日期时跳过。 | | 每隔若干分钟/小时 | 启用后先等待指定间隔;每次执行结束后重新计时,避免长任务造成重叠。 | | 指定日期,仅执行一次 | 选择未来的日期和时间,精确到分钟;至少晚于保存时刻 30 秒。 | | 自定义 Cron | 标准五段:分、时、日、月、周。支持 `*`、逗号、范围、步长及常见英文月/星期名。 | Cron 示例: ```text 0 3 * * * 每天 03:00 0 3 * * 0 每周日 03:00 0 3 1 * * 每月 1 日 03:00 */30 * * * * 每小时的 00、30 分 ``` 不支持 `L`、`W`、`#`、`?` 等扩展语法,也不接受带秒的六段 Cron。日和星期同时指定时,按传统 Cron 的“任一满足”语义处理。 Cron 是日历触发,和“上次结束后再间隔 N 分钟”不同。例如长任务执行 10 分钟,再间隔 5 分钟,下一次大约在本次开始 15 分钟后。 ### 开机、错过与长任务 - 日历任务设置 `Persistent=false`,服务器关机期间错过的执行时间不补跑,避免开机后补执行重启类任务。 - 同一个任务由固定 systemd 服务承载,并有文件锁,不会同时重叠执行两份。 - 一个任务执行超时后会被停止。默认最长 1800 秒,可在高级设置中改为 10 至 86400 秒。 - 关闭网页只会关闭查看界面,不会停止系统定时器。 - 本机历史上出现过时钟回拨。启用重启等任务前,应核对页面显示的服务器时间;定时调度依赖服务器时钟和系统校时状态。 部署验证时,服务器时区为 `Asia/Shanghai`,系统报告 `NTPSynchronized=yes`。 ## 5. 看懂执行结果 | 显示 | 意义 | |---|---| | 尚未执行 | 没有记录到执行。 | | 执行中 | 本次进程仍在运行。 | | 执行成功 | 命令正常退出,退出码为 0。 | | 执行失败 | 命令退出码非 0,或启动过程中发生错误。 | | 超时已停止 | 达到设置的最长执行时间,已停止本次进程。 | | 已停止 | 本次进程被停止,可能是手动操作或系统停止服务。 | | 执行被中断 | 执行期间系统重启或进程中断,未取得完整结果。 | 成功只表示脚本返回了 0,不保证某个证书一定已更新、备份一定可恢复或某个业务结果一定正确;这些应结合脚本输出核对。 “停用”只取消将来的调度,不会强制中止已经开始的执行。“停止本次”会终止执行进程,脚本可能尚未完成修改,操作前会提示确认。 目前单任务窗口展示最近 40 次执行,全局窗口展示最近 80 次。服务器执行记录按 90 天清理;每次输出上限约 512 KiB,网页每次读取最后 128 KiB。输出与脚本快照还有合计 256 MiB 的空间限制,较旧文件可能提前清理,执行状态元数据仍保留至期限。 删除任务会停用计划并移除模块生成的定时器,保留已有执行记录。历史记录可在“全部执行记录”查看。 ## 6. 参考与技术栈 界面参考 [宝塔 Linux 面板](https://www.bt.cn/) 计划任务的组织方式:任务列表、执行周期、Shell 脚本、启停、手动执行和执行日志。本次功能自行编写,沿用已有监控页面的深色风格,没有安装宝塔,也没有复制其面板源码。 原监控布局仍参考: - [Zabbix](https://github.com/zabbix/zabbix):设备总览和告警分级。 - [Netdata](https://github.com/netdata/netdata):实时指标展示。 - [Grafana](https://github.com/grafana/grafana):历史曲线、图例和时间范围。 | 部分 | 使用技术 | |---|---| | 监控与任务前端 | HTML、CSS、原生 JavaScript、原生 `` 对话框。 | | 曲线 | 原生 SVG,保留原有 CPU、内存、GPU 温度、功耗和利用率历史。 | | 网页后端 | Python 3.12 标准库,`ThreadingHTTPServer`。 | | 数据库 | SQLite,WAL 模式;监控数据与任务数据分库存储。 | | 系统调度 | systemd `.timer` 和 `.service`,不是浏览器内的倒计时。 | | 网页与特权服务通信 | 本机 Unix socket,限制连接进程身份。 | | 管理密码 | 随机盐、PBKDF2-HMAC-SHA256,240000 次迭代。 | | 登录态 | 内存会话、HttpOnly/SameSite=Strict Cookie;写操作同时检查 Origin 与 CSRF。 | | 执行控制 | 独立进程组、文件锁、超时、停止信号、限量输出、执行审计。 | 没有新增 npm、pip 或外部 CDN 依赖。服务器不需要 Node.js;开发阶段使用 Node.js 检查 JavaScript 语法。 ## 7. 为什么要拆成两个服务 ```mermaid flowchart LR A[浏览器:监控与任务界面] --> B[hardware-dashboard\n普通账号\nHTTP 8090] B --> C[(原监控数据库)] B -->|本机 Unix socket\n验证会话与 CSRF| D[hardware-taskd\nroot 任务管理服务] D --> E[(独立任务数据库\nroot 专用目录)] D --> F[systemd 定时器] F --> G[task_exec\n执行指定任务] G --> H[执行日志与结果] H --> D ``` 硬件网页继续使用原来的 `hardware-dashboard` 普通账号。新增 root 服务只通过本机 Unix socket 接收经验证的任务操作,不额外开放网络端口。 任务内容不插入 systemd 的 `ExecStart`。生成的服务只启动固定的执行器和经过验证的任务 ID;执行器从 root 专用数据库读取任务内容。这避免任务名称、换行或参数变成 systemd 单元配置。 上传或粘贴的脚本文本会统一换行为 Linux 格式。服务器脚本路径和参数以独立参数列表传递;命令内容模式则按管理员输入交给 Bash 执行。 root 服务不是恶意脚本隔离环境。管理员既然有权输入 root 命令,就应当视为具有服务器管理员权限。 ## 8. 文件与数据库位置 以下均在 `192.168.1.37`: | 路径 | 内容 | |---|---| | `/opt/hardware-dashboard/server.py` | 网页 HTTP 和监控 API;新增任务 API 代理。 | | `/opt/hardware-dashboard/static/tasks.js` | 任务表单、登录、启停、执行和日志交互。 | | `/opt/hardware-dashboard/task_core.py` | 校验、周期转换、任务数据库和 systemd 管理。 | | `/opt/hardware-dashboard/taskd.py` | 本机任务服务、会话验证、审计和清理。 | | `/opt/hardware-dashboard/task_exec.py` | 单次执行、输出、超时与退出结果。 | | `/etc/systemd/system/hardware-taskd.service` | 新增任务管理常驻服务。 | | `/etc/systemd/system/hwjob-任务ID.timer` | 本模块为已创建任务生成的定时器。 | | `/etc/systemd/system/hwjob-任务ID.service` | 对应的执行服务。 | | `/run/hardware-taskd/control.sock` | 普通网页与 root 服务之间的本机通信入口。 | | `/etc/hardware-task-admin.json` | 管理密码的盐与散列,仅 root 可读。 | | `/var/lib/hardware-taskd/tasks.sqlite3` | 任务定义、执行元数据和审计。 | | `/var/lib/hardware-taskd/scripts/` | 脚本内容模式的执行快照。 | | `/var/log/hardware-taskd/` | 每次执行的输出日志,仅经管理登录读取。 | | `/var/lib/hardware-dashboard/monitor.sqlite3` | 原来的性能和硬件日志数据库,继续保留。 | | `/root/hardware-task-admin-login.txt` | 初始登录信息的服务器副本,仅 root 可读。 | 原有 `hwlog` 命令、硬件采集器及性能历史继续运行。本模块只管理自己创建的 `hwjob-*` 单元,不接管现有 crontab、系统 timer 或其他面板任务。 ## 9. 常用维护命令 ```bash # 查看网页服务与任务服务 systemctl status hardware-dashboard.service hardware-taskd.service # 查看任务管理服务自身报错 journalctl -u hardware-taskd.service -n 50 --no-pager # 查看本模块的全部系统定时器 systemctl list-timers --all 'hwjob-*.timer' # 查看某个任务的系统层日志,任务 ID 可从页面或数据库查出 # 将下方 32 位 ID 替换成实际值,不要把说明文字直接输入命令 systemctl status hwjob-0123456789abcdef0123456789abcdef.service # 重启网页服务:不会取消已启用的任务 systemctl restart hardware-dashboard.service # 重启任务管理服务:已有网页登录会失效,系统定时器仍独立运行 systemctl restart hardware-taskd.service ``` 忘记任务管理密码时,可通过 SSH 以 root 执行以下命令。新密码在交互输入,不放进命令行参数: ```bash python3 /opt/hardware-dashboard/taskd.py password systemctl restart hardware-taskd.service ``` 只停止网页或任务管理进程,不等于停用计划。要停止后续执行,应先在页面将对应任务停用;需要立即中断时再使用“停止本次”。 ## 10. 备份与回退 本次更新前的网页代码备份:`/opt/hardware-dashboard-backup.vNWXhLKD/`。 任务数据库与监控数据库分开。备份正在运行的 SQLite 应使用 SQLite 的备份接口,不能只复制主文件而忽略 WAL。备份任务数据库、管理密码散列和脚本/日志时,应继续保持 root 专用权限。 如需回退界面,先在页面停用所有不再需要的任务,再停止任务管理服务并恢复代码。只恢复旧网页代码不会自动删除或停用已经生成的系统定时器。 ## 11. 已完成的验证 ### 自动化测试 16 项本地和服务器测试通过,覆盖: - 原监控数据、问题合并、时钟回拨及过期状态。 - 每日/每周/每月/间隔周期与 Cron 字段解析。 - Cron 日期和星期的 OR 语义、非法范围和步长。 - 任务名称与脚本内容不会成为 systemd 单元配置指令。 - 未登录和错误 CSRF 不会调用特权任务操作。 - 修改密码使所有旧会话失效。 - 日志读取量上限与路径编号验证。 ### 服务器真实执行验证 使用明确命名的“部署验证”任务,确认了: | 验证内容 | 实际结果 | |---|---| | 手动打印命令 | 成功,退出码 0。 | | 指定日期自动触发 | 在计划时间由 systemd 自动执行,记录为 `scheduled`,退出码 0。 | | Python 脚本内容 | 成功,输出 `PYTHON_TASK_OK`。 | | 服务器已有文件执行 | 成功,输出 `EXISTING_FILE_OK`。 | | 主动返回退出码 7 | 正确显示失败,保留错误输出。 | | 10 秒超时 | 约 10 秒后停止,显示超时。 | | 手动停止等待任务 | 正确停止并记录状态。 | | 编辑、启用和停用 | 页面与系统定时器状态一致。 | | 未登录、错误来源、错误 CSRF | 分别被 HTTP 401/403 拒绝。 | 这些测试仅打印信息或短暂等待,没有执行重启、关机、证书续签或修改现有业务配置。测试计划在交付前移除,历史中保留有“部署验证”字样的执行记录。 ## 12. 显卡数量与夜间节电(2026 年 11 月 9 日) ### 显卡数量 在“设备总览”的“计算显卡”卡片中,选择“1 张显卡”或“2 张显卡”,再点击“保存”。使用已有任务管理员账号登录后,设置保存到 `/etc/hardware-watch.json`,刷新网页、重启服务和重启服务器后仍有效。多个浏览器共用服务器上的设置。 保存会更新 `expected_gpu_count` 并清空 `expected_gpu_uuids`。监控随后按 NVIDIA 显卡数量检查,不再要求原来的 P100 UUID;可更换为 V100 等受当前驱动支持的 NVIDIA 显卡。温度、供电、驱动异常、PCIe 通信错误继续检测。选择 2 张而实际仅检测到 1 张时,仍会报缺卡。 页面通过 `/api/tasks/gpu-count` 调用已有 root 任务代理,沿用管理员会话、CSRF 和来源检查。普通网页进程仍无权写入 `/etc`。配置变更记录到任务管理审计表,同时触发一次硬件采集;页面每 10 秒刷新。 配置变更不会删除历史告警。旧缺卡和旧 UUID 告警随采集恢复保留在历史中,不应再作为当前故障显示。 ### 每天 00:00 关机、06:00 开机 页面“定时任务”中使用名称“夜间节电:00:00 关机,06:00 开机”。执行周期为北京时间每天 `00:00`,命令为: ```bash /usr/bin/python3 /opt/hardware-dashboard/scripts/night-power-cycle.py ``` 脚本先用 `rtcwake --utc --mode no` 设置当天北京时间 06:00 的 RTC 唤醒,并回读 `/sys/class/rtc/rtc0/wakealarm` 核对时间。确认成功后调用 `systemctl --no-block poweroff` 正常关机。服务器的硬件时钟采用 UTC;不要求存在 `/etc/adjtime`。 脚本仅允许在北京时间 00:00 至 00:04 执行关机,避免白天点击“执行一次”时意外关机。若缺少 RTC 接口、设置闹钟失败或回读时间不符,本轮任务报错并保持开机。每天的任务均重新设置闹钟。系统定时器使用 `Persistent=false`,开机后不会补执行错过的关机任务。 仅检查环境和下次唤醒时间: ```bash /usr/bin/python3 /opt/hardware-dashboard/scripts/night-power-cycle.py --check ``` 这个检查不设置闹钟,也不执行关机。真正开机由主板完成,需要 BIOS 支持并启用关机状态 RTC 唤醒,且电源持续接通。00:00 正在运行的计算任务会中断;关机期间无法访问监控网页。RTC 闹钟设置成功不能证明主板一定能从关机状态唤醒,完整效果应在首次夜间周期后确认。 需要全天运行时,在页面停用这项计划。停用会取消后续关机调度,已设置的 RTC 闹钟仍可能保留。新增任务的“夜间节电”模板默认停用;如已有启用的同名任务,应编辑已有任务,避免重复添加。 本次在 192.168.1.37 已创建并启用一项夜间节电任务。首次关机计划为北京时间 `2026-11-10 00:00`,RTC 闹钟已设置并回读确认为 `2026-11-10 06:00`。未在部署时执行实际关机。原代码、原显卡配置和任务数据库的备份位于 `/root/hardware-dashboard-backups/20261109-183339/`。 ### 新增文件与验证 - `scripts/night-power-cycle.py`:RTC 设置、时间回读、关机及只读检查。 - `tests/test_power_cycle.py`:验证时区、跨日、白天禁止关机、闹钟失败或不匹配时不关机。 - `tests/test_tasks.py`:增加显卡数量输入校验、登录与 CSRF、其他配置保留及关机任务识别检查。 技术栈仍为 Python 标准库、HTML、CSS、原生 JavaScript、SQLite 和 systemd;RTC 唤醒使用系统已有的 util-linux `rtcwake`。部署时需同步新增的 `scripts/` 目录,无需安装前端构建工具或第三方 Python 包。 本次本地和服务器端均通过 22 项自动化测试,前端脚本语法检查通过。实际网页已验证单卡显示 `1/1`、无缺卡告警、仅显示一张显卡卡片;390 像素手机视口无横向溢出。登录后保存 1/2 张显卡及夜间模板填写流程使用浏览器模拟接口验证,没有执行真实关机。RTC 闹钟已在真实设备上设置并回读,完整关机唤醒周期尚待首次计划执行后验证。