refactor code, ẩn thông tin ssh
This commit is contained in:
120
core/ssh_utils.py
Normal file
120
core/ssh_utils.py
Normal file
@@ -0,0 +1,120 @@
|
||||
"""
|
||||
SSH/SCP helper functions dùng chung cho cả 2 luồng flash.
|
||||
|
||||
Không chứa logic nghiệp vụ — chỉ là transport layer:
|
||||
_create_ssh_client() — kết nối SSH với retry
|
||||
_upload_firmware() — upload file qua SCP lên /tmp/
|
||||
_verify_firmware() — kiểm tra file tồn tại trên device
|
||||
_sync_and_sysupgrade() — sync + sysupgrade, trả về "DONE" / "FAIL: ..."
|
||||
"""
|
||||
|
||||
import os
|
||||
import time
|
||||
|
||||
import paramiko
|
||||
from scp import SCPClient
|
||||
|
||||
|
||||
def _create_ssh_client(ip, user, password, timeout=15):
|
||||
"""Tạo SSH client với AutoAddPolicy, có retry 3 lần."""
|
||||
last_err = None
|
||||
for attempt in range(3):
|
||||
try:
|
||||
client = paramiko.SSHClient()
|
||||
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
client.connect(
|
||||
ip, username=user, password=password,
|
||||
timeout=timeout, look_for_keys=False, allow_agent=False,
|
||||
)
|
||||
return client
|
||||
except paramiko.AuthenticationException:
|
||||
# Sai password — retry không giúp ích gì
|
||||
raise
|
||||
except Exception as e:
|
||||
last_err = e
|
||||
if attempt < 2:
|
||||
time.sleep(1)
|
||||
raise last_err
|
||||
|
||||
|
||||
def _upload_firmware(client, firmware_path, status_cb=None):
|
||||
"""
|
||||
Upload firmware qua SCP lên /tmp/<filename>.
|
||||
Trả về remote_path khi thành công, raise RuntimeError nếu thất bại.
|
||||
"""
|
||||
if status_cb:
|
||||
status_cb("Uploading firmware via SCP...")
|
||||
|
||||
filename = os.path.basename(firmware_path)
|
||||
remote_path = f"/tmp/{filename}"
|
||||
|
||||
last_err = None
|
||||
for attempt in range(3):
|
||||
try:
|
||||
scp = SCPClient(client.get_transport(), socket_timeout=350)
|
||||
scp.put(firmware_path, remote_path)
|
||||
scp.close()
|
||||
time.sleep(2)
|
||||
return remote_path
|
||||
except Exception as e:
|
||||
last_err = e
|
||||
time.sleep(1.5)
|
||||
|
||||
raise RuntimeError(f"SCP upload failed (Check Network) — {last_err}")
|
||||
|
||||
|
||||
def _verify_firmware(client, remote_path, status_cb=None):
|
||||
"""
|
||||
Xác nhận file firmware tồn tại trên thiết bị.
|
||||
Raise RuntimeError nếu file không có.
|
||||
"""
|
||||
if status_cb:
|
||||
status_cb("Verifying firmware...")
|
||||
|
||||
_, stdout, _ = client.exec_command(
|
||||
f"test -f {remote_path} && ls -lh {remote_path}", timeout=10
|
||||
)
|
||||
output = stdout.read().decode("utf-8", errors="ignore").strip()
|
||||
if not output:
|
||||
raise RuntimeError("Firmware file not found on device after upload")
|
||||
|
||||
|
||||
def _sync_and_sysupgrade(client, remote_path, status_cb=None):
|
||||
"""
|
||||
Sync filesystem rồi chạy sysupgrade.
|
||||
Trả về "DONE" khi thành công, "FAIL: ..." khi sysupgrade lỗi sớm.
|
||||
Connection drop trong lúc sysupgrade = thành công (thiết bị đang reboot).
|
||||
"""
|
||||
if status_cb:
|
||||
status_cb("Syncing filesystem...")
|
||||
client.exec_command("sync", timeout=10)
|
||||
time.sleep(2)
|
||||
|
||||
if status_cb:
|
||||
status_cb("Flashing firmware (sysupgrade)...")
|
||||
|
||||
try:
|
||||
# -F: bỏ qua kiểm tra metadata (cần cho uImage)
|
||||
# -v: verbose -n: không giữ cấu hình cũ
|
||||
_, stdout, _ = client.exec_command(
|
||||
f"sysupgrade -F -v -n {remote_path} > /tmp/sysup.log 2>&1"
|
||||
)
|
||||
# Chờ tối đa 4 giây — nếu exit quá sớm thì coi như lỗi
|
||||
time.sleep(4)
|
||||
|
||||
if stdout.channel.exit_status_ready():
|
||||
exit_code = stdout.channel.recv_exit_status()
|
||||
_, log_out, _ = client.exec_command("cat /tmp/sysup.log")
|
||||
err_msg = log_out.read().decode("utf-8", errors="ignore").strip()
|
||||
return (
|
||||
f"FAIL: sysupgrade terminated early "
|
||||
f"(Code {exit_code}). Details:\n{err_msg}"
|
||||
)
|
||||
except Exception:
|
||||
# Connection drop = device đang reboot = thành công
|
||||
pass
|
||||
|
||||
if status_cb:
|
||||
status_cb("Rebooting...")
|
||||
time.sleep(3)
|
||||
return "DONE"
|
||||
Reference in New Issue
Block a user