gns3-server/docs/bugs/issue-2676-remote-compute-image-path-validation.md
YueGuobin 6ff4f575bb
docs: add analysis for issue #2676 remote compute image path validation
Add detailed documentation explaining:
- Problem description and root cause
- Architecture overview (Controller vs Compute)
- Why automatic upload is not triggered
- Affected node types
- Solution implementation
- Testing scenarios

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-03 17:47:25 +08:00

173 lines
6.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Issue #2676: 远程计算节点镜像路径验证失败
## 问题描述
当在远程计算节点上创建 IOU 节点时,控制器将其本地绝对镜像路径发送给计算节点,但计算节点拒绝该路径,因为它与计算节点配置的镜像目录不匹配。
## 错误信息
```
'/home/gns3/GNS3/images/IOU/i86bi-linux-l3-adventerprisek9-15.4.1T.bin' is not allowed on this remote server. Please only use a file from '/home/yueguobin/GNS3/images/IOU'
```
## 根本原因
### 架构说明
GNS3 采用控制器-计算节点分离架构:
- **Controller控制节点**:管理拓扑,负责节点创建、连接等逻辑
- **Compute计算节点**:实际运行虚拟机,执行节点操作
### 问题详细分析
#### 1. 控制器发送绝对路径
`gns3server/controller/node.py``_node_data()` 方法中,控制器准备节点数据时,直接将本地镜像的绝对路径发送给计算节点:
```python
# 控制器端的镜像路径是控制器的本地路径
path = "/home/gns3/GNS3/images/IOU/i86bi-linux-l3-adventerprisek9-15.4.1T.bin"
data["path"] = path # 发送给计算节点
```
#### 2. 计算节点验证路径
`gns3server/compute/base_manager.py``get_abs_image_path()` 方法中第448-456行
```python
# 如果是绝对路径,检查是否在计算节点的允许目录中
if os.path.isabs(orig_path):
for directory in valid_directory_prefices:
if os.path.commonprefix([directory, path]) == directory:
if os.path.exists(path):
return path
raise ImageMissingError(orig_path)
# 路径前缀不匹配,抛出错误
raise NodeError(f"'{path}' is not allowed on this remote server...")
```
#### 3. 路径不匹配
- **控制器** 的镜像目录:`/home/gns3/GNS3/images`
- **计算节点** 的镜像目录:`/home/yueguobin/GNS3/images`
- 路径前缀检查 `os.path.commonprefix([directory, path]) == directory` 失败
- 抛出 `NodeError`,节点创建失败
### 为什么不触发镜像上传?
控制器有自动上传缺失镜像的机制(`_upload_missing_image()`),但这个机制只在收到 `ImageMissingError` 时触发。
由于计算节点抛出的是 `NodeError` 而不是 `ImageMissingError`,控制器无法触发自动上传逻辑。
## 影响范围
此问题影响所有使用镜像文件的节点类型:
| 节点类型 | 镜像路径字段 | 状态 |
|---------|------------|------|
| **IOU** | `path` | ❌ 受影响 |
| **QEMU** | `hda_disk_image`, `hdb_disk_image`, `hdc_disk_image`, `hdd_disk_image`<br>`cdrom_image`, `bios_image`, `initrd`, `kernel_image` | ❌ 受影响 |
| **Dynamips (IOS)** | `image` | ❌ 受影响 |
| **VMware** | `vmx_path` | ❌ 受影响 |
| **Docker** | `image` (Docker 镜像名称,非文件路径) | ✅ 不受影响 |
## 解决方案
### 实现方案
修改控制器端的 `_node_data()` 方法,对远程计算节点发送相对路径(仅文件名)而非绝对路径。
**文件**: `gns3server/controller/node.py`
**修改位置**: 第541-546行
```python
# For remote computes, convert absolute image paths to relative paths
# The remote compute will search for the image in its own configured directories
if self._compute.id != "local" and "path" in data and os.path.isabs(data["path"]):
data["path"] = os.path.basename(data["path"])
```
### 工作原理
#### 1. 控制器发送相对路径
```python
# 原来发送:"/home/gns3/GNS3/images/IOU/i86bi-linux-l3-adventerprisek9-15.4.1T.bin"
# 现在发送:"i86bi-linux-l3-adventerprisek9-15.4.1T.bin"
if self._compute.id != "local" and "path" in data and os.path.isabs(data["path"]):
data["path"] = os.path.basename(data["path"])
```
#### 2. 计算节点搜索镜像
计算节点的 `get_abs_image_path()` 方法处理相对路径第432-446行
```python
if not os.path.isabs(orig_path):
for directory in valid_directory_prefices:
path = self._recursive_search_file_in_directory(directory, orig_path)
if path:
return force_unix_path(path)
# 未找到,抛出 ImageMissingError
raise ImageMissingError(orig_path)
```
计算节点在其配置的镜像目录中搜索:
- 默认目录:`~/GNS3/images/IOU`(从配置文件 `gns3_server.conf` 读取)
- 额外目录:`Server.additional_images_paths` 中配置的目录
#### 3. 自动上传机制
如果计算节点未找到镜像,返回 `ImageMissingError`,控制器捕获后自动上传:
```python
except ComputeConflictError as e:
response = e.response()
if response.get("exception") == "ImageMissingError":
res = await self._upload_missing_image(self._node_type, response["image"])
```
### 向后兼容性
- **本地计算节点**`id == "local"`):不受影响,继续使用绝对路径
- **远程计算节点**`id != "local"`):使用相对路径,由计算节点在其目录中搜索
## 测试场景
### 场景 1镜像已存在于计算节点
1. 控制器创建节点,发送相对路径 `i86bi-linux-l3-adventerprisek9-15.4.1T.bin`
2. 计算节点在 `~/GNS3/images/IOU/` 中找到镜像
3. 节点创建成功 ✅
### 场景 2镜像不存在于计算节点
1. 控制器创建节点,发送相对路径 `i86bi-linux-l3-adventerprisek9-15.4.1T.bin`
2. 计算节点未找到镜像,返回 `ImageMissingError`
3. 控制器自动上传镜像到计算节点
4. 重试创建,节点创建成功 ✅
### 场景 3同名但内容不同的镜像
⚠️ **潜在风险**:如果计算节点已存在同名镜像但内容不同(不同版本),不会触发上传,可能使用错误的镜像。
**建议改进**:未来可以添加 MD5 校验和验证,确保使用正确的镜像版本。
## 当前状态
- ✅ IOU 节点已修复
- ⏳ 其他节点类型QEMU, Dynamips, VMware需要类似的修复
## 相关文件
- `gns3server/controller/node.py` - 控制器节点实现
- `gns3server/compute/base_manager.py` - 计算节点基础管理器
- `gns3server/compute/iou/iou_vm.py` - IOU 节点实现
- `gns3server/utils/images.py` - 镜像工具函数
## 参考
- GitHub Issue: #2676
- 修复分支: `fix/iou-remote-path-validation`