521 lines
13 KiB
Markdown
521 lines
13 KiB
Markdown
# GS Mock Mode 模擬模式分析
|
|
|
|
## 概述
|
|
|
|
為 GS (DtrGuiSrv) 新增 Mock 模擬模式,無需 RAID Driver 即可運行,用於:
|
|
- 開發測試
|
|
- CI/CD 自動化測試
|
|
- 離線 Demo
|
|
- 功能驗證
|
|
|
|
## 需要模擬的關鍵函數
|
|
|
|
### 1. Driver I/O 層
|
|
|
|
| 函數 | 用途 | 模擬難度 |
|
|
|------|------|----------|
|
|
| `DevIoControl()` | I/O Control 與 Driver 通訊 | 高 |
|
|
| `QuaryDTR()` | 查詢 RAID 控制器資訊 | 中 |
|
|
| `GetCtlrEvent()` | 取得控制器事件 | 中 |
|
|
|
|
### 2. 共享記憶體層
|
|
|
|
| 函數 | 用途 | 模擬難度 |
|
|
|------|------|----------|
|
|
| `InitialSHMForAllCtlr()` | 初始化共享記憶體 | 高 |
|
|
| 相關 SHM 函數 | 共享記憶體操作 | 高 |
|
|
|
|
## 模擬模式架構
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────────┐
|
|
│ GS Mock Mode 架構 │
|
|
├─────────────────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ ┌─────────────────────────────────────────────────────────────┐ │
|
|
│ │ GS (DtrGuiSrv) │ │
|
|
│ │ │ │
|
|
│ │ ┌─────────────────────────────────────────────────────┐ │ │
|
|
│ │ │ Mock Mode 開關 │ │ │
|
|
│ │ │ #define ENABLE_MOCK_MODE │ │ │
|
|
│ │ └─────────────────────────────────────────────────────┘ │ │
|
|
│ │ │ │ │
|
|
│ │ ┌──────────────────┼──────────────────┐ │ │
|
|
│ │ ▼ ▼ ▼ │ │
|
|
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
|
|
│ │ │ Real Driver │ │ Mock Driver │ │ Mock Data │ │ │
|
|
│ │ │ (Production)│ │ (Dev/Mock) │ │ (Static) │ │ │
|
|
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
|
|
│ └─────────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
## 實現方案
|
|
|
|
### 方案 1: 編譯時開關 (推薦)
|
|
|
|
```c
|
|
// hInclude.h
|
|
#ifdef ENABLE_MOCK_MODE
|
|
#include "mock_driver.h"
|
|
#define DevIoControl mock_DevIoControl
|
|
#define QuaryDTR mock_QuaryDTR
|
|
#define GetCtlrEvent mock_GetCtlrEvent
|
|
#endif
|
|
```
|
|
|
|
### 方案 2: 執行時開關
|
|
|
|
```c
|
|
// runtime_config.h
|
|
extern BOOL g_bMockMode;
|
|
|
|
// 初始化時檢查
|
|
if (g_bMockMode) {
|
|
initMockDriver();
|
|
} else {
|
|
initRealDriver();
|
|
}
|
|
```
|
|
|
|
### 方案 3: 環境變數
|
|
|
|
```bash
|
|
# 啟動時設定
|
|
export DTR_GUI_MOCK_MODE=1
|
|
./DtrGuiSrv01
|
|
```
|
|
|
|
## 需要實現的 Mock 函數
|
|
|
|
### 1. mock_driver.h
|
|
|
|
```c
|
|
#ifndef MOCK_DRIVER_H
|
|
#define MOCK_DRIVER_H
|
|
|
|
#include "hInclude.h"
|
|
|
|
// Mock 初始化
|
|
void mock_init(void);
|
|
void mock_cleanup(void);
|
|
|
|
// Mock 控制器數據
|
|
typedef struct {
|
|
UCHAR bStatus;
|
|
UCHAR bCtlrSN[16];
|
|
UCHAR bModelName[32];
|
|
UCHAR bPage0Data[1024]; // Controller Info Page
|
|
UCHAR bPage1Data[1024]; // RAID Snapshot
|
|
// ...
|
|
} MOCK_CTRL_INFO;
|
|
|
|
// Mock DevIoControl
|
|
UCHAR mock_DevIoControl(
|
|
UCHAR bDevNo,
|
|
ULONG lControlCode,
|
|
ULONG lInSize,
|
|
PCHAR pInBuf,
|
|
ULONG lOutSize,
|
|
PCHAR pOutBuf,
|
|
USHORT *pusInBandErrCode
|
|
);
|
|
|
|
// Mock QuaryDTR
|
|
CHAR mock_QuaryDTR(
|
|
UCHAR ucCtlrIndex,
|
|
UCHAR *bPageBuf,
|
|
UCHAR *pbHeatsink
|
|
);
|
|
|
|
// Mock GetCtlrEvent
|
|
UCHAR mock_GetCtlrEvent(
|
|
UCHAR bCtlrNo,
|
|
PEVENT_INFO pEvent_info
|
|
);
|
|
|
|
// 設定模擬事件
|
|
void mock_set_event(UCHAR *eventData, int length);
|
|
void mock_clear_events(void);
|
|
|
|
// 控制器狀態控制
|
|
void mock_set_controller_online(BOOL online);
|
|
void mock_set_controller_count(int count);
|
|
|
|
#endif
|
|
```
|
|
|
|
### 2. mock_driver.c
|
|
|
|
```c
|
|
#include "mock_driver.h"
|
|
|
|
// 單例 Mock 控制器資訊
|
|
static MOCK_CTRL_INFO g_mockCtrl[MAX_DTR_IN_HOST];
|
|
static BOOL g_bMockInitialized = FALSE;
|
|
|
|
// 模擬的事件佇列
|
|
static UCHAR g_mockEventQueue[100][512];
|
|
static int g_mockEventCount = 0;
|
|
|
|
void mock_init(void) {
|
|
if (g_bMockInitialized) return;
|
|
|
|
// 初始化 Mock 控制器
|
|
memset(g_mockCtrl, 0, sizeof(g_mockCtrl));
|
|
|
|
// 預設控制器 1 台,狀態上線
|
|
g_mockCtrl[0].bStatus = CTLR_STATUS_ALIVE;
|
|
memcpy(g_mockCtrl[0].bCtlrSN, "MOCK00000001", 16);
|
|
memcpy(g_mockCtrl[0].bModelName, "Accusys Mock RAID", 32);
|
|
|
|
// 填充 Page 0 資料 (Controller Info)
|
|
fill_mock_page0(g_mockCtrl[0].bPage0Data);
|
|
|
|
g_bMockInitialized = TRUE;
|
|
|
|
printf("[MOCK] Driver initialized\n");
|
|
}
|
|
|
|
UCHAR mock_DevIoControl(...) {
|
|
if (!g_bMockInitialized) mock_init();
|
|
|
|
// 根據 Control Code 處理
|
|
switch (lControlCode) {
|
|
case IOCTL_DTR_GET_CONTROLLER_INFO:
|
|
// 回傳控制器資訊
|
|
break;
|
|
|
|
case IOCTL_DTR_GET_INBAND_EVENT:
|
|
// 回傳模擬事件
|
|
break;
|
|
|
|
case IOCTL_DTR_SET_INBAND_COMMAND:
|
|
// 處理命令,回傳成功
|
|
break;
|
|
|
|
default:
|
|
// 預設回傳成功
|
|
*pusInBandErrCode = 0;
|
|
return SYSOK;
|
|
}
|
|
|
|
return SYSOK;
|
|
}
|
|
|
|
CHAR mock_QuaryDTR(UCHAR ucCtlrIndex, UCHAR *bPageBuf, UCHAR *pbHeatsink) {
|
|
if (!g_bMockInitialized) mock_init();
|
|
|
|
if (ucCtlrIndex >= MAX_DTR_IN_HOST) {
|
|
return FALSE;
|
|
}
|
|
|
|
// 複製 Mock 資料
|
|
memcpy(bPageBuf, g_mockCtrl[ucCtlrIndex].bPage0Data, 1024);
|
|
*pbHeatsink = 0x00;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
UCHAR mock_GetCtlrEvent(UCHAR bCtlrNo, PEVENT_INFO pEvent_info) {
|
|
if (!g_bMockInitialized) mock_init();
|
|
|
|
// 從佇列取出事件
|
|
if (g_mockEventCount > 0) {
|
|
memcpy(pEvent_info->bEventBuf, g_mockEventQueue[0], 512);
|
|
pEvent_info->bStatus = TRUE;
|
|
|
|
// 移除已取出的事件
|
|
g_mockEventCount--;
|
|
// ... 佇列搬移
|
|
|
|
return SYSOK;
|
|
}
|
|
|
|
pEvent_info->bStatus = FALSE;
|
|
return SYSFAIL_EVENT_EMPTY;
|
|
}
|
|
```
|
|
|
|
## 模擬的 Page 資料
|
|
|
|
### Page 0: Controller Info
|
|
|
|
```c
|
|
void fill_mock_page0(UCHAR *buf) {
|
|
// Offset 0-31: Serial Number
|
|
memcpy(buf + 0, "MOCK00000001", 16);
|
|
|
|
// Offset 32-63: Model Name
|
|
memcpy(buf + 32, "Accusys Mock RAID", 20);
|
|
|
|
// Offset 64-95: Firmware Version
|
|
memcpy(buf + 64, "1.0.0.0", 8);
|
|
|
|
// 其他欄位...
|
|
// 需要完整對應 Java GUI 解析的格式
|
|
}
|
|
```
|
|
|
|
### 需要的 Page
|
|
|
|
| Page | 大小 | 用途 |
|
|
|------|------|------|
|
|
| 0x00 | 1024 | Controller Info |
|
|
| 0x01 | 1024 | RAID Snapshot |
|
|
| 0x07 | 512 | LUN Map Table |
|
|
| 0x0A-0x0E | 1024 | RAID Info |
|
|
| 0x10-0x11 | 512 | Disk/Slice Names |
|
|
| 0x14 | 512 | Array Info |
|
|
| 0x1A | 512 | Enclosure Info |
|
|
| 0x1B | 512 | Disk List |
|
|
|
|
## 模擬事件
|
|
|
|
### 預設模擬事件
|
|
|
|
```c
|
|
// 初始化時註冊一些模擬事件
|
|
void mock_register_default_events(void) {
|
|
// 磁碟上線事件
|
|
UCHAR diskOnlineEvent[] = {
|
|
0x01, 0x00, 0x00, 0x00, // Event Type
|
|
0x00, 0x00, 0x00, 0x00, // Timestamp
|
|
0x01, 0x02, 0x00, 0x00, // Slot 1, Online
|
|
// ...
|
|
};
|
|
mock_set_event(diskOnlineEvent, 32);
|
|
|
|
// RAID 重建完成事件
|
|
UCHAR rebuildDoneEvent[] = {
|
|
0x02, 0x00, 0x00, 0x00, // Event Type
|
|
// ...
|
|
};
|
|
mock_set_event(rebuildDoneEvent, 32);
|
|
}
|
|
```
|
|
|
|
## 配置檔案
|
|
|
|
### mock_config.json
|
|
|
|
```json
|
|
{
|
|
"mock_mode": true,
|
|
"controller_count": 2,
|
|
"controllers": [
|
|
{
|
|
"serial_number": "MOCK00000001",
|
|
"model_name": "Accusys GEN2",
|
|
"firmware_version": "3.8.0",
|
|
"status": "online",
|
|
"raid_arrays": [
|
|
{
|
|
"id": 0,
|
|
"level": "RAID5",
|
|
"disks": [0, 1, 2, 3],
|
|
"status": "Normal",
|
|
"capacity": 10000000
|
|
}
|
|
],
|
|
"disks": [
|
|
{"slot": 0, "status": "Online", "capacity": 4000000},
|
|
{"slot": 1, "status": "Online", "capacity": 4000000},
|
|
{"slot": 2, "status": "Online", "capacity": 4000000},
|
|
{"slot": 3, "status": "Online", "capacity": 4000000},
|
|
{"slot": 4, "status": "Online", "capacity": 4000000, "spare": true}
|
|
]
|
|
},
|
|
{
|
|
"serial_number": "MOCK00000002",
|
|
"model_name": "Accusys GEN2",
|
|
"status": "online"
|
|
}
|
|
],
|
|
"events": [
|
|
{
|
|
"type": "DiskOnline",
|
|
"slot": 2,
|
|
"timestamp": 1234567890
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
## 編譯選項
|
|
|
|
### Makefile
|
|
|
|
```makefile
|
|
# 一般編譯
|
|
build:
|
|
gcc -o DtrGuiSrv01 *.c -lpthread -lm
|
|
|
|
# Mock 模式編譯
|
|
build-mock:
|
|
gcc -o DtrGuiSrv01_mock *.c -DENABLE_MOCK_MODE -lpthread -lm
|
|
|
|
# 兩者區別
|
|
ifdef ENABLE_MOCK_MODE
|
|
CFLAGS += -DENABLE_MOCK_MODE
|
|
SRCS += mock_driver.c
|
|
endif
|
|
```
|
|
|
|
### CMake
|
|
|
|
```cmake
|
|
option(ENABLE_MOCK "Enable mock mode for testing" OFF)
|
|
|
|
if(ENABLE_MOCK)
|
|
add_definitions(-DENABLE_MOCK_MODE)
|
|
set(MOCK_SOURCES mock_driver.c)
|
|
endif()
|
|
|
|
add_executable(DtrGuiSrv01 ${SOURCES} ${MOCK_SOURCES})
|
|
```
|
|
|
|
## 執行模式
|
|
|
|
### 1. 環境變數
|
|
|
|
```bash
|
|
# 啟用 Mock 模式
|
|
export DTR_GUI_MOCK_MODE=1
|
|
./DtrGuiSrv01
|
|
|
|
# 指定 Mock 設定檔
|
|
export DTR_GUI_MOCK_CONFIG=./mock_config.json
|
|
./DtrGuiSrv01
|
|
```
|
|
|
|
### 2. 命令列參數
|
|
|
|
```bash
|
|
./DtrGuiSrv01 --mock
|
|
./DtrGuiSrv01 -m
|
|
./DtrGuiSrv01 --mock-config ./mock_config.json
|
|
```
|
|
|
|
### 3. 設定檔
|
|
|
|
```ini
|
|
# dtr_guiconf.ini
|
|
[Server]
|
|
Port=8922
|
|
MockMode=1
|
|
MockConfig=./mock_config.json
|
|
```
|
|
|
|
## 測試案例
|
|
|
|
### 1. 基本連線測試
|
|
|
|
```python
|
|
import socket
|
|
|
|
def test_connection():
|
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
s.connect(('localhost', 8922))
|
|
|
|
# 發送 GET_PAGE 請求
|
|
request = build_get_page_request(page_code=0x00)
|
|
s.send(request)
|
|
|
|
# 驗證回應
|
|
response = s.recv(4096)
|
|
assert response[0] == 0x50 # Success
|
|
|
|
s.close()
|
|
```
|
|
|
|
### 2. 控制器查詢測試
|
|
|
|
```python
|
|
def test_query_controller():
|
|
# 查詢控制器資訊
|
|
response = send_inband_command(opcode=0x01, page=0x00)
|
|
|
|
# 驗證資料格式
|
|
assert response['serial_number'] == 'MOCK00000001'
|
|
assert response['model_name'] == 'Accusys Mock RAID'
|
|
```
|
|
|
|
### 3. 事件測試
|
|
|
|
```python
|
|
def test_event_polling():
|
|
# 設定模擬事件
|
|
mock_set_event(disk_online_event)
|
|
|
|
# 取得事件
|
|
response = send_event_request(opcode=0x02)
|
|
|
|
# 驗證事件
|
|
assert response['event_type'] == 'DiskOnline'
|
|
```
|
|
|
|
## 實作時間評估
|
|
|
|
| 工作項目 | 時間 |
|
|
|----------|------|
|
|
| 建立 mock_driver.h/c 框架 | 1 天 |
|
|
| 實作基本 DevIoControl | 2 天 |
|
|
| 實作 QuaryDTR / GetCtlrEvent | 1 天 |
|
|
| 實作 Page 0 (Controller Info) | 1 天 |
|
|
| 實作其他 Page 資料 | 2-3 天 |
|
|
| 模擬事件系統 | 1 天 |
|
|
| JSON 設定檔支援 | 1 天 |
|
|
| 測試與除錯 | 2 天 |
|
|
| **總計** | **9-10 天** |
|
|
|
|
## 風險與限制
|
|
|
|
### 已知限制
|
|
|
|
1. **不完整模擬**: 無法完全模擬所有 RAID 操作
|
|
2. **狀態有限**: 只能模擬基本狀態
|
|
3. **Driver 特定**: 某些低層級錯誤無法模擬
|
|
|
|
### 風險控制
|
|
|
|
1. **清晰標記**: Mock 模式下顯示明顯標識
|
|
2. **日誌區分**: Mock 日誌使用不同前綴 `[MOCK]`
|
|
3. **版本標記**: 回應中包含 Mock 標記
|
|
|
|
```c
|
|
// 回應中加入 Mock 標記
|
|
UCHAR mock_page0[] = {
|
|
// ...
|
|
'M', 'O', 'C', 'K', // 識別碼
|
|
// ...
|
|
};
|
|
```
|
|
|
|
## 結論
|
|
|
|
### 可行性: ✅ 高
|
|
|
|
**結論**: 完全可行,預估 9-10 天可完成。
|
|
|
|
### 實作策略
|
|
|
|
1. **先實現基本框架**: 快速建立可運行的 Mock 骨架
|
|
2. **逐步完善**: 按需求添加更多 Page 支援
|
|
3. **測試驅動**: 先寫測試案例,再實作對應 Mock
|
|
|
|
### 優點
|
|
|
|
- 開發期不需要實際 RAID 硬體
|
|
- CI/CD 可自動化測試
|
|
- 容易重現問題
|
|
- 教學演示方便
|
|
|
|
### 產出
|
|
|
|
- `mock_driver.h` - Mock 驅動介面
|
|
- `mock_driver.c` - Mock 驅動實作
|
|
- `mock_config.json` - 模擬設定檔範例
|
|
- 編譯腳本更新
|