-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathsetup.sh
More file actions
executable file
·227 lines (203 loc) · 9.2 KB
/
Copy pathsetup.sh
File metadata and controls
executable file
·227 lines (203 loc) · 9.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
#!/bin/bash
# setup.sh: macOS 上一键部署 openvohive(UTM Ubuntu VM 方式)
#
# 流程: 检查 UTM → 启动 VM → USB 直通 → SSH 部署 → 设备发现
#
# 环境变量(可选):
# VOHIVE_VM_NAME UTM 虚拟机名称(默认 vohive)
# VOHIVE_VM_USER VM SSH 用户名(必填,或交互输入)
# VOHIVE_VM_IP VM IP 地址(必填,或交互输入)
# VOHIVE_VM_PASS VM SSH 密码(必填,或交互输入)
# VOHIVE_IMAGE openvohive 镜像(默认 ghcr.io/dannyge/openvohive:latest)
# PROXY_WEB_PASSWORD Web 密码(不设则随机生成)
# PROXY_TELEGRAM_* Telegram 转发配置(可选)
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
# shellcheck source=lib/common.sh
. "$SCRIPT_DIR/lib/common.sh"
VM_NAME="${VOHIVE_VM_NAME:-vohive}"
VM_USER="${VOHIVE_VM_USER:-}"
VM_IP="${VOHIVE_VM_IP:-}"
VM_PASS="${VOHIVE_VM_PASS:-}"
PLATFORM_IMAGE="${VOHIVE_IMAGE:-ghcr.io/dannyge/openvohive:latest}"
DEVICE_ID="${VOHIVE_DEVICE_ID:-quectel-1}"
# ── 1. 检查依赖 ──────────────────────────────────────────
log "检查依赖..."
need_cmd sshpass
need_cmd ssh
# 检查 UTM(可选——VM 可能已手动建好)
if command -v utmctl >/dev/null 2>&1; then
ok "UTM 已安装"
else
warn "UTM 未安装(如 VM 已在运行可忽略)"
fi
# ── 2. 确认 VM 连接信息 ──────────────────────────────────
if [ -z "$VM_USER" ]; then
printf "VM SSH 用户名: "
read -r VM_USER
fi
if [ -z "$VM_IP" ]; then
printf "VM IP 地址(如 192.168.64.6): "
read -r VM_IP
fi
if [ -z "$VM_PASS" ]; then
printf "VM SSH 密码: "
read -rs VM_PASS
printf "\n"
fi
# SSH 辅助函数(避免 eval 转义问题)
ssh_run() {
sshpass -p "$VM_PASS" ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 "$VM_USER@$VM_IP" "$@"
}
# 测试 SSH 连接
log "测试 SSH 连接 ${VM_USER}@${VM_IP}..."
if ssh_run 'echo ok' 2>/dev/null | grep -q ok; then
ok "SSH 连接成功"
else
err "SSH 连接失败。请确认 VM 在运行、IP/用户名/密码正确。"
exit 1
fi
# 检查内核(确认是完整 Linux 内核,不是 OrbStack 精简版)
KERNEL=$(ssh_run 'uname -r' 2>/dev/null)
log "VM 内核: $KERNEL"
if echo "$KERNEL" | grep -q "orbstack"; then
err "检测到 OrbStack 内核——openvohive 需要 UTM Ubuntu VM(完整内核)。"
exit 1
fi
ok "完整 Linux 内核确认"
# ── 3. USB 直通提示 ──────────────────────────────────────
log "检查 Quectel 模块是否在 VM 内可见..."
QUECTEL=$(ssh_run 'lsusb 2>/dev/null | grep -i quectel' 2>/dev/null || true)
if [ -n "$QUECTEL" ]; then
ok "Quectel 模块已在 VM 内: $QUECTEL"
else
warn "VM 内未检测到 Quectel 模块"
warn "请在 UTM 窗口工具栏点 USB 图标,勾选大疆/Quectel 设备(2C7C:0125)"
printf " 操作完成后按回车继续..."
read -r
# 再次检查
QUECTEL=$(ssh_run 'lsusb 2>/dev/null | grep -i quectel' 2>/dev/null || true)
if [ -z "$QUECTEL" ]; then
err "仍未检测到 Quectel 模块。请确认 USB 直通已启用。"
exit 1
fi
ok "Quectel 模块已直通: $QUECTEL"
fi
# 检查设备节点(option 驱动应自动生成)
TTYUSB=$(ssh_run 'ls /dev/ttyUSB* 2>/dev/null | wc -l' 2>/dev/null || echo 0)
CDCWDM=$(ssh_run 'ls /dev/cdc-wdm0 2>/dev/null' 2>/dev/null || true)
if [ "$TTYUSB" -gt 0 ] && [ -n "$CDCWDM" ]; then
ok "设备节点就绪: $TTYUSB 个 ttyUSB + cdc-wdm0"
else
warn "设备节点不完整(ttyUSB=$TTYUSB cdc-wdm=$CDCWDM)"
warn "可能需要物理拔插模块重新枚举。请拔插后按回车继续..."
read -r
TTYUSB=$(ssh_run 'ls /dev/ttyUSB* 2>/dev/null | wc -l' 2>/dev/null || echo 0)
CDCWDM=$(ssh_run 'ls /dev/cdc-wdm0 2>/dev/null' 2>/dev/null || true)
if [ "$TTYUSB" -eq 0 ] || [ -z "$CDCWDM" ]; then
err "设备节点仍不完整。请确认 option 和 qmi_wwan 驱动已加载。"
exit 1
fi
ok "设备节点就绪(拔插后恢复)"
fi
# ── 4. 在 VM 内安装 Docker + 部署 openvohive ─────────────
log "传输部署脚本到 VM..."
DEPLOY_DIR="/opt/vohive"
sshpass -p "$VM_PASS" ssh -o StrictHostKeyChecking=no "$VM_USER@$VM_IP" \
"echo '$VM_PASS' | sudo -S mkdir -p $DEPLOY_DIR/lib 2>/dev/null" 2>/dev/null || true
# 传 vm-init.sh
sshpass -p "$VM_PASS" scp -o StrictHostKeyChecking=no \
"$SCRIPT_DIR/vm-init.sh" "${VM_USER}@${VM_IP}:/tmp/vm-init.sh" 2>/dev/null
sshpass -p "$VM_PASS" ssh -o StrictHostKeyChecking=no "$VM_USER@$VM_IP" \
"echo '$VM_PASS' | sudo -S cp /tmp/vm-init.sh $DEPLOY_DIR/vm-init.sh 2>/dev/null && sudo chmod +x $DEPLOY_DIR/vm-init.sh" 2>/dev/null || true
# 传 lib/common.sh
sshpass -p "$VM_PASS" scp -o StrictHostKeyChecking=no \
"$SCRIPT_DIR/lib/common.sh" "${VM_USER}@${VM_IP}:/tmp/common.sh" 2>/dev/null
sshpass -p "$VM_PASS" ssh -o StrictHostKeyChecking=no "$VM_USER@$VM_IP" \
"echo '$VM_PASS' | sudo -S cp /tmp/common.sh $DEPLOY_DIR/lib/common.sh 2>/dev/null" 2>/dev/null || true
# 传 dji2quectel.sh(首次改身份用,已是 Quectel 则跳过)
sshpass -p "$VM_PASS" scp -o StrictHostKeyChecking=no \
"$PROJECT_ROOT/dji2quectel/dji2quectel.sh" "${VM_USER}@${VM_IP}:/tmp/dji2quectel.sh" 2>/dev/null
sshpass -p "$VM_PASS" ssh -o StrictHostKeyChecking=no "$VM_USER@$VM_IP" \
"echo '$VM_PASS' | sudo -S cp /tmp/dji2quectel.sh $DEPLOY_DIR/dji2quectel.sh 2>/dev/null && sudo chmod +x $DEPLOY_DIR/dji2quectel.sh" 2>/dev/null || true
# 在 VM 内执行初始化(装 docker、改身份、起容器)
# 把 PROXY_* 环境变量传到 VM 里(SSH 不自动继承本地环境)
log "在 VM 内执行部署..."
PROXY_ENV=""
while IFS= read -r line; do
PROXY_ENV="$PROXY_ENV $line"
done < <(env | grep '^PROXY_')
sshpass -p "$VM_PASS" ssh -o StrictHostKeyChecking=no "$VM_USER@$VM_IP" \
"echo '$VM_PASS' | sudo -S env $PROXY_ENV bash $DEPLOY_DIR/vm-init.sh" 2>&1 || true
# ── 5. 添加设备到 openvohive(API 调用)──────────────────
# 实测发现:openvohive 的 rescan 发现 QMI 但不自动注册设备,
# 需通过 API 手动添加(device_backend=qmi, control_device=/dev/cdc-wdm0)
log "添加设备到 openvohive..."
# 写一个临时脚本到 VM 里执行(避免 SSH 内联转义地狱)
ADD_DEVICE_SCRIPT="/tmp/add_device.sh"
cat > /tmp/add_device_local.sh << 'ADDEOF'
#!/bin/bash
set -eu
DEVICE_ID="${1:-quectel-1}"
WEB_PASS="${2:-}"
# 登录获取 token
if [ -n "$WEB_PASS" ]; then
TOKEN=$(curl -s -X POST http://localhost:7575/api/auth/login \
-H "Content-Type: application/json" \
-d "{\"username\":\"admin\",\"password\":\"$WEB_PASS\"}" \
| python3 -c "import sys,json; print(json.load(sys.stdin)['token'])" 2>/dev/null || echo "")
fi
if [ -z "${TOKEN:-}" ]; then
# 随机密码模式:从日志提取密码
PASS=$(docker logs vohive 2>&1 | grep "密码" | tail -1 | grep -oE '[A-Za-z0-9]{12}' | tail -1 || true)
[ -z "$PASS" ] && PASS=$(docker logs vohive 2>&1 | grep "password" | tail -1 | grep -oE '[A-Za-z0-9]{12}' | tail -1 || true)
if [ -z "$PASS" ]; then
echo "[!] 无法获取密码,跳过设备添加。请手动在后台添加设备。"
exit 0
fi
TOKEN=$(curl -s -X POST http://localhost:7575/api/auth/login \
-H "Content-Type: application/json" \
-d "{\"username\":\"admin\",\"password\":\"$PASS\"}" \
| python3 -c "import sys,json; print(json.load(sys.stdin)['token'])" 2>/dev/null || echo "")
fi
if [ -z "${TOKEN:-}" ]; then
echo "[!] 登录失败,跳过设备添加。"
exit 0
fi
# 检查设备是否已存在
EXISTING=$(curl -s http://localhost:7575/api/devices \
-H "Authorization: Bearer $TOKEN" \
| python3 -c "import sys,json; d=json.load(sys.stdin); print(len(d.get('devices',[])))" 2>/dev/null || echo "0")
if [ "$EXISTING" = "0" ]; then
echo "添加设备 $DEVICE_ID..."
curl -s -X POST http://localhost:7575/api/devices \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"config\":{\"id\":\"$DEVICE_ID\",\"device_backend\":\"qmi\",\"control_device\":\"/dev/cdc-wdm0\"}}"
echo ""
else
echo "设备已存在($EXISTING 个),跳过添加"
fi
ADDEOF
# 传到 VM 并执行
sshpass -p "$VM_PASS" scp -o StrictHostKeyChecking=no \
/tmp/add_device_local.sh "${VM_USER}@${VM_IP}:${ADD_DEVICE_SCRIPT}"
sshpass -p "$VM_PASS" ssh -o StrictHostKeyChecking=no "$VM_USER@$VM_IP" \
"echo '$VM_PASS' | sudo -S bash $ADD_DEVICE_SCRIPT '$DEVICE_ID' '${PROXY_WEB_PASSWORD:-}'" 2>&1 || true
# ── 6. 输出访问信息 ─────────────────────────────────────
printf "\n"
ok "部署完成!"
printf "\n"
printf " 后台地址: http://%s:7575\n" "$VM_IP"
printf " 登录账号: admin\n"
if [ -n "${PROXY_WEB_PASSWORD:-}" ]; then
printf " 登录密码: %s\n" "$PROXY_WEB_PASSWORD"
else
printf " 登录密码: 见 docker logs(随机生成)\n"
printf " ssh %s@%s 'sudo docker logs vohive 2>&1 | grep 密码'\n" "$VM_USER" "$VM_IP"
fi
printf "\n"
printf " 设备 ID: %s\n" "$DEVICE_ID"
printf " 管理接口: http://%s:7575\n" "$VM_IP"
printf "\n"