Files
DCTS/scripts/push_import_results.sh
T
fmq 1bfa240cb0 feat(all): 源精度命名体系、工作流可观测台、节点停用管理与白名单归档
核心变更:

  1. GridAxisValue 源精度命名
     - 新增 GridAxisValue 类型,携带 f64 数值 + YAML 源书写文本(Deref 透明兼容算术)
     - config.rs 绕过 serde_yaml 归一化,逐 token 捕获轴值原文(logg: 5.0 → g5.0)
     - runner/executor/scheduler 全链路改用 DB TEXT 列权威 point_name,
       修复 REAL 列回读丢精度导致的 model_name 错配

  2. 工作流执行可观测台
     - 新增 stats/progress/points 三组 API(进度时间序列、经验速率 ETA、
       停滞预警、逐点明细分页、收敛性热力图数据)
     - 新增 workflow_progress_snapshots 表 + tasks/grid_points 耗时列
     - runner 携带 last_iter/worst_depth/n_depths 进 conv.json
     - 前端新增 hash 路由、工作流详情页(概览/网格点/收敛分析三 Tab)、YAML 编辑器

  3. 节点停用/启用管理
     - 新增 disabled 状态 + disable/enable API;停用节点保持心跳但停止分发,
       worker 空闲待命而非退出;移除 revoke API,token 失效统一走重发覆盖;
       移除 host_name 字段

  4. 白名单结果归档
     - 新增 result_filter 模块,只归档有语义产物,丢弃 Tlusty 中间单元(~2MB/模型)
     - executor 原子写入归档 + 200 点 LRU 上限

  5. 历史数据导入
     - sync_seeds 重写为 import_results:经 /admin/import_seed 标记 converged +
       按新版命名迁移产物树

  6. 部署与目录重规划
     - data/results→seeds、data/archive→result + migrate_data_dirs.sh
     - deploy.sh 增强(SSH 复用、Profile、远程 env);Dockerfile 瘦身

  7. 文档同步更新 api/database/architecture/deployment
2026-07-31 01:34:05 +08:00

274 lines
13 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# ==============================================================================
# push_import_results.sh — 推送 import_results 工具到 Windows 计算节点并编译
# ==============================================================================
# 把 import_results 工具的源码推送到 deploy.env.d/<profile> 指定的 Windows 节点,
# 在该节点原生编译(Windows 上 cargo build),并打印确切的运行命令。
#
# 设计前提:
# - 旧版 Python run_grid.py 的结果在 Windows 机的 E:/fmq/grid(工具必须在该机运行才能读到)
# - 该机已部署 nodeDocker Compose),data 目录在 E:/fmq/dcts/data
# - DCTS 服务端地址来自 profile 的 DCTS_SERVER_URL
# - Windows 机需已装 Rust 工具链(cargo/rustc);脚本会自检并给出安装提示
#
# 用法:
# ./scripts/push_import_results.sh # 默认用 node-dckj-win-01.env
# ./scripts/push_import_results.sh -f deploy.env.d/other-node.env # 指定其他 profile
# ./scripts/push_import_results.sh --no-build # 只推源码不编译(远端已有缓存时加速)
# ./scripts/push_import_results.sh --archive-dir E:/fmq/dcts/data/result # 覆盖归档目录
#
# 运行(推完编译后,在 Windows 机上执行,或本脚本末尾会打印):
# E:\fmq\dcts-src\target\release\import_results.exe \
# -d E:/fmq/grid -c E:/fmq/dcts-src/workflows/sdB_cno.yaml \
# -r E:/fmq/dcts/data/result -s http://100.66.1.2:8090 \
# -w sdB_cno -t <admin_token>
# ==============================================================================
set -eo pipefail
GREEN='\033[0;32m'
BLUE='\033[0;34m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
WORK_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
cd "${WORK_DIR}"
# 默认值(可被 profile 覆盖)
PROFILE_FILE="deploy.env.d/node-dckj-win-01.env"
REMOTE_USER="fmq"
REMOTE_IP="192.168.6.102"
REMOTE_PORT="22"
REMOTE_SRC_DIR="E:/fmq/dcts-src"
DCTS_SERVER_URL="http://100.66.1.2:8090"
RESULT_DIR="" # 空 → 用 profile 推导(默认 E:/fmq/dcts/data/result
DO_BUILD=true
# SSH ControlMaster 复用目录(与 deploy.sh 一致,便于共享连接)
SSH_CONTROL_DIR="${SSH_CONTROL_DIR:-$HOME/.ssh/cm}"
# =============================================================================
# 解析命令行参数
# =============================================================================
while [[ $# -gt 0 ]]; do
case "$1" in
-f|--profile) PROFILE_FILE="$2"; shift 2 ;;
--no-build) DO_BUILD=false; shift ;;
--archive-dir) RESULT_DIR="$2"; shift 2 ;;
-h|--help)
sed -n '2,30p' "$0"
exit 0 ;;
*) echo -e "${RED}未知参数: $1${NC}"; exit 1 ;;
esac
done
# =============================================================================
# 加载 profile(SSH 连接信息 + 服务端地址)
# =============================================================================
if [ ! -f "$PROFILE_FILE" ]; then
echo -e "${RED}错误: 找不到 profile '${PROFILE_FILE}'${NC}"
exit 1
fi
echo -e "${CYAN}[i] 加载 profile: ${PROFILE_FILE}${NC}"
# shellcheck disable=SC1090
set -a; source "$PROFILE_FILE"; set +a
# profile 提供的变量优先(REMOTE_* / DCTS_SERVER_URL
[ -n "${REMOTE_USER:-}" ] && REMOTE_USER="${REMOTE_USER}"
[ -n "${REMOTE_IP:-}" ] && REMOTE_IP="${REMOTE_IP}"
[ -n "${REMOTE_PORT:-}" ] && REMOTE_PORT="${REMOTE_PORT}"
# 源码目录默认放到部署目录旁(E:/fmq/dcts-src),与运行中的 nodeE:/fmq/dcts)隔离
[ -n "${REMOTE_DIR:-}" ] && REMOTE_SRC_DIR="${REMOTE_DIR}-src"
# 结果目录:命令行 > 参数 > 默认(部署目录下的 data/resultnode 的 DCTS_RESULT_DIR 卷映射到此)
if [ -z "${RESULT_DIR}" ]; then
RESULT_DIR="${REMOTE_DIR}/data/result"
fi
echo -e "${CYAN}📢 目标节点: ${YELLOW}${REMOTE_USER}@${REMOTE_IP}:${REMOTE_PORT}${NC}"
echo -e "${CYAN}📢 源码推送: ${YELLOW}${REMOTE_SRC_DIR}${NC}"
echo -e "${CYAN}📢 结果目录: ${YELLOW}${RESULT_DIR}${NC}"
echo -e "${CYAN}📢 服务端: ${YELLOW}${DCTS_SERVER_URL}${NC}"
echo ""
# =============================================================================
# 建立 SSH ControlMaster 连接复用(一次性输密码,后续 scp/ssh 复用)
# =============================================================================
mkdir -p "${SSH_CONTROL_DIR}"
SOCK="${SSH_CONTROL_DIR}/${REMOTE_USER}@${REMOTE_IP}:${REMOTE_PORT}"
SSH_OPTS="-o ControlMaster=auto -o ControlPath=${SOCK} -o ControlPersist=1800 -o StrictHostKeyChecking=no"
if ssh -p "${REMOTE_PORT}" -o ControlPath="${SOCK}" -O check "${REMOTE_USER}@${REMOTE_IP}" >/dev/null 2>&1; then
echo -e "${GREEN}[√] 复用已有 SSH 连接${NC}"
else
echo -e "${CYAN}=== 建立 SSH 连接(请输入 ${REMOTE_USER}@${REMOTE_IP} 的密码)===${NC}"
if ! ssh -p "${REMOTE_PORT}" ${SSH_OPTS} -fN "${REMOTE_USER}@${REMOTE_IP}"; then
echo -e "${RED}SSH 连接失败${NC}"
exit 1
fi
echo -e "${GREEN}[√] SSH 连接已建立${NC}"
fi
trap 'ssh -p "${REMOTE_PORT}" -o ControlPath="${SOCK}" "${REMOTE_USER}@${REMOTE_IP}" -O exit 2>/dev/null || true' EXIT
# =============================================================================
# 自检远端 Rust 工具链
# =============================================================================
echo -e "\n${CYAN}=== 检查远端 Rust 工具链 ===${NC}"
HAS_CARGO=$(ssh -p "${REMOTE_PORT}" ${SSH_OPTS} "${REMOTE_USER}@${REMOTE_IP}" \
"powershell -NoProfile -Command \"if (Get-Command cargo -ErrorAction SilentlyContinue) { 'yes' } else { 'no' }\"" 2>/dev/null | tr -d '[:space:]')
if [ "$HAS_CARGO" != "yes" ]; then
echo -e "${RED}[✗] 远端 ${REMOTE_IP} 未检测到 cargo/rustc${NC}"
echo -e "${YELLOW}请先在该 Windows 机上安装 Rust 工具链:${NC}"
echo -e " 方式1PowerShell,需联网): winget install Rustlang.Rustup"
echo -e " 方式2(浏览器): 访问 https://rustup.rs 下载 rustup-init.exe 并运行"
echo -e " 安装后重开 PowerShell 再跑本脚本。"
exit 1
fi
echo -e "${GREEN}[√] 远端已安装 Rust 工具链${NC}"
# =============================================================================
# 同步源码到远端(仅 git 跟踪文件,排除 target/data/assets 等大目录)
# =============================================================================
echo -e "\n${CYAN}=== 同步源码到 ${REMOTE_SRC_DIR} ===${NC}"
# 远端先建目录
ssh -p "${REMOTE_PORT}" ${SSH_OPTS} "${REMOTE_USER}@${REMOTE_IP}" \
"powershell -NoProfile -Command \"New-Item -ItemType Directory -Force -Path '${REMOTE_SRC_DIR}' | Out-Null\""
# 单文件 scp 辅助:失败时打印诊断并退出(不静默吞错)。
# Windows OpenSSH scp 对带正斜杠的远端路径需用 ':' 分隔主机,路径部分正斜杠 OK。
scp_one() {
local src="$1" dst="$2"
if ! scp -P "${REMOTE_PORT}" ${SSH_OPTS} "$src" "${REMOTE_USER}@${REMOTE_IP}:${dst}"; then
echo -e "${RED}[✗] 传输失败: ${src} -> ${dst}${NC}"
exit 1
fi
}
# 递归目录 scp:先建/清远端目标目录再传,避免 Windows scp -r 创建子目录失败。
scp_dir() {
local sub="$1"
[ -d "$sub" ] || return 0
# 远端先删除旧目录并重建空目录(确保 scp -r 能写入)
ssh -p "${REMOTE_PORT}" ${SSH_OPTS} "${REMOTE_USER}@${REMOTE_IP}" \
"powershell -NoProfile -Command \"Remove-Item -Recurse -Force '${REMOTE_SRC_DIR}/${sub}' -ErrorAction SilentlyContinue; New-Item -ItemType Directory -Force -Path '${REMOTE_SRC_DIR}/${sub}' | Out-Null\"" || true
echo -e " -> ${sub}/"
if ! scp -P "${REMOTE_PORT}" ${SSH_OPTS} -r "$sub" "${REMOTE_USER}@${REMOTE_IP}:${REMOTE_SRC_DIR}/"; then
echo -e "${RED}[✗] 传输目录失败: ${sub}${NC}"
echo -e "${YELLOW} 若反复失败,可在 Windows 机上手动 git clone 后仅同步增量,或检查磁盘空间/权限${NC}"
exit 1
fi
}
echo -e "${CYAN} -> 传输顶层文件 + crates/tools/workflows ...${NC}"
scp_one Cargo.toml "${REMOTE_SRC_DIR}/"
[ -f Cargo.lock ] && scp_one Cargo.lock "${REMOTE_SRC_DIR}/"
# import_results 依赖 common crate,二者都要同步;workflows 含配置(决定命名精度)。
scp_dir crates
scp_dir tools
scp_dir workflows
echo -e "${GREEN}[√] 源码同步完成${NC}"
# =============================================================================
# 远端编译 import_resultsrelease
# =============================================================================
if [ "$DO_BUILD" = true ]; then
echo -e "\n${CYAN}=== 远端编译 import_results (release) ===${NC}"
echo -e "${YELLOW} (首次编译会拉 crate 依赖,可能耗时数分钟,请耐心等待...)${NC}"
ssh -p "${REMOTE_PORT}" ${SSH_OPTS} "${REMOTE_USER}@${REMOTE_IP}" \
"powershell -NoProfile -Command \"Set-Location '${REMOTE_SRC_DIR}'; cargo build --release -p import_results 2>&1; \$LASTEXITCODE\"" 2>&1 | tee /tmp/import_build.log | tail -20
BUILD_RC=$(tail -1 /tmp/import_build.log | tr -d '[:space:]')
rm -f /tmp/import_build.log
if [ "$BUILD_RC" != "0" ]; then
echo -e "${RED}[✗] 远端编译失败 (exit=${BUILD_RC})${NC}"
exit 1
fi
echo -e "${GREEN}[√] 编译成功${NC}"
else
echo -e "${YELLOW}[i] 跳过编译(--no-build${NC}"
fi
# =============================================================================
# 在远端生成 run_import.batWindows 批处理),用户双击或在 cmd 里运行即可,无需手敲命令
# =============================================================================
EXE_PATH_WIN=$(echo "${REMOTE_SRC_DIR}/target/release/import_results.exe" | sed 's|/|\\|g')
CONFIG_PATH="${REMOTE_SRC_DIR}/workflows/sdB_cno.yaml"
WORK_DIR_WIN=$(echo "${REMOTE_SRC_DIR}" | sed 's|/|\\|g')
# .bat 内容:用 quoted heredoc 避免反引号/% 被 bash 解释,占位符再替换。
# .bat 用 set /p 交互读取 admin token(用户无需手动编辑文件),空回车则跳过入库只迁移 result。
BAT_CONTENT=$(cat <<'BATEOF'
@echo off
chcp 65001 >nul
setlocal
cd /d "__WORK_DIR__"
echo ============================================================
echo DCTS 历史计算结果导入工具
echo ============================================================
echo 旧数据目录 : E:\fmq\grid
echo workflow : __CONFIG__
echo 结果目录 : __RESULT__
echo 服务端 : __SERVER__
echo 工作流名 : sdB_cno
echo ============================================================
echo.
set /p ADMIN_TOKEN="请输入 Admin Token (DCTS_ADMIN_TOKEN,直接回车则只迁移 result 不入库): "
if "%ADMIN_TOKEN%"=="" (
echo [i] 未输入 token,仅迁移产物树到 result (入库会因无鉴权失败但不阻断迁移)
"__EXE__" -d E:/fmq/grid -c "__CONFIG__" -r "__RESULT__" -s __SERVER__ -w sdB_cno
) else (
"__EXE__" -d E:/fmq/grid -c "__CONFIG__" -r "__RESULT__" -s __SERVER__ -w sdB_cno -t "%ADMIN_TOKEN%"
)
echo.
echo ============================================================
echo 执行结束。按任意键退出...
pause >nul
BATEOF
)
BAT_CONTENT="${BAT_CONTENT//__WORK_DIR__/${WORK_DIR_WIN}}"
BAT_CONTENT="${BAT_CONTENT//__EXE__/${EXE_PATH_WIN}}"
BAT_CONTENT="${BAT_CONTENT//__CONFIG__/${CONFIG_PATH}}"
BAT_CONTENT="${BAT_CONTENT//__RESULT__/${RESULT_DIR}}"
BAT_CONTENT="${BAT_CONTENT//__SERVER__/${DCTS_SERVER_URL}}"
# 经 SSH stdin 管道写入远端 run_import.batUTF-8 with BOM 由 chcp 65001 处理中文)
BAT_REMOTE_PATH="${REMOTE_SRC_DIR}/run_import.bat"
echo -e "\n${CYAN}=== 在远端生成 run_import.bat ===${NC}"
printf '%s\r\n' "$BAT_CONTENT" | ssh -p "${REMOTE_PORT}" ${SSH_OPTS} "${REMOTE_USER}@${REMOTE_IP}" \
"powershell -NoProfile -Command \"\$input = [Console]::In.ReadToEnd(); [System.IO.File]::WriteAllText('${BAT_REMOTE_PATH}', \$input, [System.Text.Encoding]::UTF8)\"" \
&& echo -e "${GREEN}[√] 已生成: ${BAT_REMOTE_PATH}${NC}" \
|| { echo -e "${RED}[✗] 生成 run_import.bat 失败${NC}"; exit 1; }
# =============================================================================
# 输出最终提示
# =============================================================================
echo ""
echo -e "${BLUE}==============================================================${NC}"
echo -e "${GREEN}🎉 推送 + 编译完成!${NC}"
echo -e "${BLUE}==============================================================${NC}"
echo ""
echo -e "${CYAN}在 Windows 机 ${REMOTE_IP} 上启动导入(任选其一):${NC}"
echo ""
echo -e " 方式1(推荐):双击运行 ${YELLOW}${WORK_DIR_WIN}\\run_import.bat${NC}"
echo -e " 会交互提示输入 Admin Token(回车则只迁移 result 不入库)"
echo ""
echo -e " 方式2cmd): cd /d ${WORK_DIR_WIN} 然后 run_import.bat"
echo ""
echo -e "${YELLOW}参数(已内置在 .bat 里,无需手填):${NC}"
echo -e " -d E:/fmq/grid 旧版 Python run_grid.py 的 results 根目录"
echo -e " -c ${CONFIG_PATH}"
echo -e " -r ${RESULT_DIR} 结果写入目标(node 容器卷映射处)"
echo -e " -s ${DCTS_SERVER_URL} 服务端地址(来自 profile"
echo -e " -w sdB_cno 工作流名(必须等于将来要启动的工作流名)"
echo ""
echo -e "${YELLOW}⚠️ 注意:${NC}"
echo -e " - -w sdB_cno 必须匹配将来在 Dashboard 创建/启动的工作流名,否则 converged 标记被隔离看不到、导致重算"
echo -e " - 工具可重复运行(DB 幂等 + result 原子覆盖),网络中断后重跑 run_import.bat 即可补齐"
echo ""