修 devssh:定义了 HOST_FALLBACKS 却从来没用过

随身 WiFi 跟着教室网络走,地址会在 192.168.2.156 和
192.168.10.105 之间变。devssh.py 里明明定义了 HOST_FALLBACKS,
但 run / upload / download 三个函数**全都直接用 HOST** ——
于是设备一换网段就全部连不上,而且报的是连接超时,
看起来像设备挂了。

现在改成按候选列表顺序试:
    环境变量 YKXMZ_DEV_HOST 优先
    -> 192.168.2.156(当前)
    -> 192.168.10.105(换网前用过)
并记住上次通的那个,后续调用不用每次从头试。
连上时打一行 `[devssh] 连上 x.x.x.x`,免得以后又搞不清连的是哪台。

还加了 `--probe` 子命令,一句话看现在哪个地址能用:

    python _build/devssh.py --probe

实测:192.168.2.156 通、192.168.10.105 不通,自动选中前者。
This commit is contained in:
xmz7891
2026-10-07 20:29:37 +08:00
parent 75c15f793c
commit 41bf110e22
4 changed files with 169 additions and 27 deletions
+20
View File
@@ -0,0 +1,20 @@
修 devssh:定义了 HOST_FALLBACKS 却从来没用过
随身 WiFi 跟着教室网络走,地址会在 192.168.2.156 和
192.168.10.105 之间变。devssh.py 里明明定义了 HOST_FALLBACKS,
但 run / upload / download 三个函数**全都直接用 HOST** ——
于是设备一换网段就全部连不上,而且报的是连接超时,
看起来像设备挂了。
现在改成按候选列表顺序试:
环境变量 YKXMZ_DEV_HOST 优先
-> 192.168.2.156(当前)
-> 192.168.10.105(换网前用过)
并记住上次通的那个,后续调用不用每次从头试。
连上时打一行 `[devssh] 连上 x.x.x.x`,免得以后又搞不清连的是哪台。
还加了 `--probe` 子命令,一句话看现在哪个地址能用:
python _build/devssh.py --probe
实测:192.168.2.156 通、192.168.10.105 不通,自动选中前者。
+89 -27
View File
@@ -4,38 +4,100 @@
用法:
python _build/devssh.py "命令" # 跑一条命令
python _build/devssh.py --script 文件 # 跑一段脚本
== 地址会自动试 ==
这台设备跟着教室网络走,地址会变:
192.168.2.156 <-> 192.168.10.105
所以这里**按顺序试多个地址**,谁先连通就用谁,不用改代码。
想强制指定就用环境变量:
$env:YKXMZ_DEV_HOST = "192.168.10.105"
⚠ 以前的坑:定义了 HOST_FALLBACKS 但**三个函数都直接用 HOST**,
那个常量根本没被用过。于是设备一换网段就全部连不上,
而且报的是「Connection refused / timed out」,看起来像设备挂了。
"""
from __future__ import annotations
import os
import sys
import time
from pathlib import Path
import paramiko
# 随身 WiFi(Debian)的地址。
#
# 这台设备跟着教室网络走,地址会变:
# 192.168.2.156 -> 192.168.10.105(换网络了)
# 所以优先读环境变量 YKXMZ_DEV_HOST,没有才用下面的默认值。
# 换网络时不用改代码:
# $env:YKXMZ_DEV_HOST = "192.168.10.105"
import os
HOST = os.environ.get("YKXMZ_DEV_HOST", "192.168.10.105")
USER = "root"
PASSWORD = os.environ.get("YKXMZ_DEV_PASS", "1313144")
# 旧地址留一份,方便回退或同时探测
HOST_FALLBACKS = ("192.168.10.105", "192.168.2.156")
# 候选地址,按顺序试。
# 环境变量优先(想锁定时用),然后是最近见过的两个地址。
_CANDIDATES: list[str] = []
def _build_candidates() -> list[str]:
out = []
env_host = (os.environ.get("YKXMZ_DEV_HOST") or "").strip()
if env_host:
out.append(env_host)
for host in ("192.168.2.156", # 当前地址
"192.168.10.105"): # 换网络前用过
if host not in out:
out.append(host)
return out
CANDIDATES = _build_candidates()
HOST = CANDIDATES[0] # 兼容老代码:默认取第一个
HOST_FALLBACKS = tuple(CANDIDATES[1:])
# 记住这次连通的是哪个,后续调用不用每次从头试
_CONNECTED: str | None = None
def _connect(timeout: int = 15):
"""按候选顺序连,返回 (client, host)。全都不通才抛异常。"""
global _CONNECTED
order = list(CANDIDATES)
if _CONNECTED and _CONNECTED in order:
# 上次通的那个排最前,省一次失败尝试
order.remove(_CONNECTED)
order.insert(0, _CONNECTED)
errors = []
for host in order:
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
client.connect(host, username=USER, password=PASSWORD,
timeout=timeout, banner_timeout=20,
auth_timeout=20, look_for_keys=False,
allow_agent=False)
if _CONNECTED != host:
print(f" [devssh] 连上 {host}")
_CONNECTED = host
return client, host
except Exception as exc: # noqa: BLE001
errors.append(f"{host}: {type(exc).__name__} {exc}")
try:
client.close()
except Exception: # noqa: BLE001
pass
raise RuntimeError("所有候选地址都连不上:\n " + "\n ".join(errors))
def probe() -> str | None:
"""探一下现在哪个地址能用(不抛异常)。"""
try:
client, host = _connect(timeout=8)
client.close()
return host
except Exception: # noqa: BLE001
return None
def run(command: str, timeout: int = 600, quiet: bool = False) -> tuple:
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(HOST, username=USER, password=PASSWORD, timeout=15,
banner_timeout=20, auth_timeout=20,
look_for_keys=False, allow_agent=False)
client, _host = _connect()
try:
_stdin, stdout, stderr = client.exec_command(command, timeout=timeout)
out = stdout.read().decode("utf-8", "replace")
@@ -52,10 +114,7 @@ def run(command: str, timeout: int = 600, quiet: bool = False) -> tuple:
def upload(local: str, remote: str, mode: int = 0o644) -> None:
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(HOST, username=USER, password=PASSWORD, timeout=15,
look_for_keys=False, allow_agent=False)
client, _host = _connect()
try:
sftp = client.open_sftp()
sftp.put(local, remote)
@@ -67,10 +126,7 @@ def upload(local: str, remote: str, mode: int = 0o644) -> None:
def download(remote: str, local: str) -> None:
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(HOST, username=USER, password=PASSWORD, timeout=15,
look_for_keys=False, allow_agent=False)
client, _host = _connect()
try:
sftp = client.open_sftp()
sftp.get(remote, local)
@@ -83,13 +139,19 @@ def download(remote: str, local: str) -> None:
if __name__ == "__main__":
if len(sys.argv) < 2:
print(__doc__)
print(" 候选地址: %s" % ", ".join(CANDIDATES))
print(" 现在能连的: %s" % (probe() or "(一个都不通)"))
sys.exit(1)
if sys.argv[1] == "--script":
run(Path(sys.argv[2]).read_text(encoding="utf-8"), timeout=600)
elif sys.argv[1] == "--probe":
got = probe()
print(" 能连的地址: %s" % (got or "(一个都不通)"))
sys.exit(0 if got else 1)
else:
args = sys.argv[1:]
timeout = 600
if args and args[0] == '--timeout':
if args and args[0] == "--timeout":
timeout = int(args[1])
args = args[2:]
run(" ".join(args), timeout=timeout)
sys.exit(run(" ".join(args), timeout=timeout)[0])
+21
View File
@@ -309,3 +309,24 @@ time code ms
2026-10-07 19:00:44 200 1199
2026-10-07 19:01:00 200 1335
2026-10-07 19:01:17 200 1437
2026-10-07 19:01:33 200 1430
2026-10-07 19:01:50 200 1414
2026-10-07 19:02:06 200 1146
2026-10-07 19:02:22 200 1079
2026-10-07 19:02:38 200 1429
2026-10-07 19:02:54 200 1057
2026-10-07 19:03:11 200 1479
2026-10-07 19:03:27 200 1129
2026-10-07 19:03:44 200 1790
2026-10-07 19:04:00 200 1420
2026-10-07 19:04:18 200 2459
2026-10-07 19:04:34 200 1148
2026-10-07 19:04:50 200 1420
2026-10-07 19:05:06 200 1085
2026-10-07 19:05:23 200 1428
2026-10-07 19:05:39 200 1017
2026-10-07 19:05:55 200 1427
2026-10-07 19:06:11 200 1085
2026-10-07 19:06:27 200 1139
2026-10-07 19:06:44 200 1036
2026-10-07 19:06:59 200 833
1 time code ms
309 2026-10-07 19:00:44 200 1199
310 2026-10-07 19:01:00 200 1335
311 2026-10-07 19:01:17 200 1437
312 2026-10-07 19:01:33 200 1430
313 2026-10-07 19:01:50 200 1414
314 2026-10-07 19:02:06 200 1146
315 2026-10-07 19:02:22 200 1079
316 2026-10-07 19:02:38 200 1429
317 2026-10-07 19:02:54 200 1057
318 2026-10-07 19:03:11 200 1479
319 2026-10-07 19:03:27 200 1129
320 2026-10-07 19:03:44 200 1790
321 2026-10-07 19:04:00 200 1420
322 2026-10-07 19:04:18 200 2459
323 2026-10-07 19:04:34 200 1148
324 2026-10-07 19:04:50 200 1420
325 2026-10-07 19:05:06 200 1085
326 2026-10-07 19:05:23 200 1428
327 2026-10-07 19:05:39 200 1017
328 2026-10-07 19:05:55 200 1427
329 2026-10-07 19:06:11 200 1085
330 2026-10-07 19:06:27 200 1139
331 2026-10-07 19:06:44 200 1036
332 2026-10-07 19:06:59 200 833
+39
View File
@@ -0,0 +1,39 @@
#!/bin/bash
# relay-stability 停了,看它留下了什么
set -u
echo " ── 还有没有它的进程 ──"
pgrep -af 'rp_|long2h|ab_long|collapse' 2>/dev/null | head -5 | sed 's/^/ /' || echo " (没有)"
echo
echo " ── 2 小时长测:最后状态 ──"
if [ -f /root/long2h_health.tsv ]; then
echo " 样本数: $(wc -l < /root/long2h_health.tsv)"
echo " 最后 3 条:"
tail -3 /root/long2h_health.tsv | sed 's/^/ /'
else
echo " (没有文件)"
fi
echo
echo " ── 配比对照跑了吗 ──"
for s in lanremote-tunnel lanremote-tunnel-h2; do
printf " %-24s %s (%s)\n" "$s" \
"$(grep -oE -- '--protocol [a-z0-9]+|--ha-connections [0-9]+' \
"/etc/systemd/system/$s.service" 2>/dev/null | tr '\n' ' ')" \
"$(systemctl is-active $s 2>/dev/null)"
done
ls -la --time-style=+%H:%M /root/*ab*.tsv /root/*ab*.out /root/*ratio* 2>/dev/null | sed 's/^/ /' || echo " (没有对照的产物)"
echo
echo " ── 它最后改的脚本(看它做到哪一步)──"
ls -la --time-style=+%H:%M:%S /root/rp_ab_long.sh /root/rp_collapse.py /root/rp_collapse.tsv 2>/dev/null | sed 's/^/ /'
echo
echo " ── 公网自检 ──"
curl -s --max-time 20 -o /dev/null -w ' HTTP %{http_code} %{time_total}s\n' \
-H 'User-Agent: LanRemote/1.8.2' https://relay.xxmxxs.top/v1/health
echo
echo " ── 设备时间 ──"
date '+ %H:%M:%S'