From 6ff4f575bb997dd7d0fd3dbecdb9686ecd7979ef Mon Sep 17 00:00:00 2001 From: YueGuobin Date: Sun, 3 May 2026 17:47:25 +0800 Subject: [PATCH] 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 --- ...76-remote-compute-image-path-validation.md | 172 ++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 docs/bugs/issue-2676-remote-compute-image-path-validation.md diff --git a/docs/bugs/issue-2676-remote-compute-image-path-validation.md b/docs/bugs/issue-2676-remote-compute-image-path-validation.md new file mode 100644 index 000000000..db6589aa8 --- /dev/null +++ b/docs/bugs/issue-2676-remote-compute-image-path-validation.md @@ -0,0 +1,172 @@ +# 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`
`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`