124 lines
5.5 KiB
Markdown
124 lines
5.5 KiB
Markdown
# Tài liệu Kỹ thuật: Flash Firmware qua LuCI API (`core/api_flash.py`)
|
|
|
|
Module `api_flash.py` tự động hoá quá trình nạp firmware cho thiết bị **OpenWrt** thông qua giao diện web **LuCI HTTP**. Được dùng trong chế độ **Nạp Mới FW** với method `"api"`.
|
|
|
|
---
|
|
|
|
## 1. Kiến Trúc — Vai Trò File
|
|
|
|
| File | Vai trò |
|
|
| -------------------------- | ----------------------------------------------------------------------- |
|
|
| `core/api_flash.py` | Logic flash 3 bước (login → upload → proceed) qua LuCI |
|
|
| `core/flash_new_worker.py` | `NewFlashThread` — dispatch tới `flash_device_api()` khi `method="api"` |
|
|
| `main.py` | UI PyQt6, chọn mode/method, truyền tham số vào worker |
|
|
|
|
---
|
|
|
|
## 2. Sơ Đồ Luồng
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
A[NewFlashThread - method=api] --> B[flash_device_api]
|
|
|
|
B --> S1["STEP 1: Login\nGET /cgi-bin/luci — phát hiện field name\nPOST username=root & password="]
|
|
S1 --> C1{Thành công?}
|
|
C1 -->|sysauth cookie + stok| S2
|
|
C1 -->|HTTP 403| F1["FAIL: Login denied (403)"]
|
|
C1 -->|Không có session| F2["FAIL: Login failed — no session"]
|
|
|
|
S2["STEP 2: Upload Firmware\nPOST /flashops\nmultipart: image=firmware.bin"] --> C2{Response?}
|
|
C2 -->|Trang Verify + Proceed| S3
|
|
C2 -->|HTTP ≠ 200| F3["FAIL: Upload HTTP xxx"]
|
|
C2 -->|invalid image| F4["FAIL: Invalid firmware image"]
|
|
C2 -->|unsupported| F5["FAIL: Firmware not compatible"]
|
|
C2 -->|Vẫn hiện form upload| F6["FAIL: Upload ignored by server"]
|
|
|
|
S3["STEP 3: Proceed\nPOST step=2 & keep=empty"] --> C3{Response?}
|
|
C3 -->|Connection dropped / Timeout| R["DONE ✅ — Device đang reboot"]
|
|
C3 -->|200 OK| R
|
|
```
|
|
|
|
---
|
|
|
|
## 3. Chi Tiết Kỹ Thuật HTTP
|
|
|
|
### Step 1 — Login
|
|
|
|
```
|
|
GET http://{IP}/cgi-bin/luci → Lấy HTML login, phát hiện field name
|
|
POST http://{IP}/cgi-bin/luci → Đăng nhập
|
|
```
|
|
|
|
**Tự động phát hiện field name tương thích:**
|
|
|
|
| Phiên bản | Field |
|
|
| --------------------- | --------------------------------- |
|
|
| Barrier Breaker 14.07 | `username` / `password` |
|
|
| OpenWrt mới hơn | `luci_username` / `luci_password` |
|
|
|
|
**Lấy session:** `stok` token được tìm tuần tự trong URL → body HTML → redirect history. Cookie `sysauth` là fallback nếu không có `stok`.
|
|
|
|
### Step 2 — Upload Firmware
|
|
|
|
```
|
|
POST http://{IP}/cgi-bin/luci/;stok={TOKEN}/admin/system/flashops
|
|
Content-Type: multipart/form-data
|
|
|
|
image = firmware.bin (file upload)
|
|
keep = (không gửi) → bỏ tích "Keep Settings" = Clean Flash
|
|
```
|
|
|
|
Thành công khi response trả về trang **"Flash Firmware - Verify"** chứa từ khoá `verify` + `proceed`.
|
|
|
|
### Step 3 — Confirm (Proceed)
|
|
|
|
```
|
|
POST http://{IP}/cgi-bin/luci/;stok={TOKEN}/admin/system/flashops
|
|
|
|
step = 2
|
|
keep = (empty)
|
|
```
|
|
|
|
**Đứt kết nối = Thành công:** Device bắt đầu flash → SSH/HTTP request bị drop là hành vi mong muốn. `ConnectionError` và `ReadTimeout` đều được bắt và trả về `"DONE"`.
|
|
|
|
---
|
|
|
|
## 4. Bảng Status UI
|
|
|
|
| Icon | Status | Điều kiện |
|
|
| ---- | ---------------------------------------- | ------------------------------------------ |
|
|
| ⏳ | `Logging in...` | Đang POST login vào LuCI |
|
|
| ⏳ | `Uploading firmware...` | Đang upload file .bin |
|
|
| ⏳ | `Confirming (Proceed)...` | Đang gửi lệnh xác nhận flash |
|
|
| ⏳ | `Rebooting...` | Chờ device khởi động lại |
|
|
| ✅ | `DONE` | Flash thành công |
|
|
| ✅ | `DONE (rebooting)` | Flash thành công, timeout khi chờ response |
|
|
| ❌ | `FAIL: Cannot connect` | Không kết nối được (sai IP / khác mạng) |
|
|
| ❌ | `FAIL: Login denied (403)` | Sai mật khẩu LuCI |
|
|
| ❌ | `FAIL: Login failed — no session` | Không có cookie/token sau login |
|
|
| ❌ | `FAIL: Upload HTTP xxx` | Server trả lỗi HTTP khi upload |
|
|
| ❌ | `FAIL: Invalid firmware image` | File firmware không hợp lệ |
|
|
| ❌ | `FAIL: Firmware not compatible` | Firmware không tương thích thiết bị |
|
|
| ❌ | `FAIL: Upload ignored by server` | Server không xử lý file (sai form field) |
|
|
| ❌ | `FAIL: Unexpected response after upload` | Không nhận được trang Verify |
|
|
|
|
---
|
|
|
|
## 5. Xử Lý Song Song
|
|
|
|
```
|
|
NewFlashThread (QThread)
|
|
└── ThreadPoolExecutor (max_workers = N)
|
|
├── Thread 1 → flash_device_api(IP_1)
|
|
├── Thread 2 → flash_device_api(IP_2)
|
|
└── Thread N → flash_device_api(IP_N)
|
|
```
|
|
|
|
| Concurrent devices | Ý nghĩa |
|
|
| ------------------ | --------------------------------- |
|
|
| `10` (mặc định) | Flash 10 thiết bị song song |
|
|
| `0` | Unlimited — flash tất cả cùng lúc |
|
|
| `1` | Tuần tự — từng thiết bị một |
|
|
|
|
Mỗi thiết bị có `requests.Session()` riêng — không bị lẫn cookie/token.
|