Skip to content

<fix>[volume]: support thirdPartySoftwarePackage#3318

Closed
zstack-robot-2 wants to merge 1 commit intofeature-zsv-5.0.0-zmigratefrom
sync/tao.gan/zmigrate-ZSV-1000@@3
Closed

<fix>[volume]: support thirdPartySoftwarePackage#3318
zstack-robot-2 wants to merge 1 commit intofeature-zsv-5.0.0-zmigratefrom
sync/tao.gan/zmigrate-ZSV-1000@@3

Conversation

@zstack-robot-2
Copy link
Copy Markdown
Collaborator

APIImpact

Resolves: ZSV-1

Change-Id: I656979726c736267776c7262716969726874716d

sync from gitlab !9141

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 9, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

本 PR 在备份存储域新增多种消息/回复类型并扩展 Ceph 插件以支持文件上传/下载/删除/进度查询、取消与升级包部署,同时在 BackupStorageBase 加入本地消息分发分支;新增多项 SDK 接口、zMigrate 接口、测试辅助方法与 Ceph 模拟器,并扩展许可枚举。

Changes

Cohort / File(s) Summary
Backup storage messages & replies
header/src/main/java/org/zstack/header/storage/backup/CancelDownloadFileOnBackupStorageHostMsg.java, .../CancelDownloadFileOnBackupStorageHostReply.java, .../DeleteFilesOnBackupStorageHostMsg.java, .../DeleteFilesOnBackupStorageHostReply.java, .../GetFileDownloadProgressFromBackupStorageHostMsg.java, .../GetFileDownloadProgressFromBackupStorageHostReply.java, .../UploadFileToBackupStorageHostMsg.java, .../UploadFileToBackupStorageHostReply.java
新增上传/删除/取消下载/查询下载进度相关的消息与回复类,包含 backupStorage/host/task/url/installPath/进度/校验等字段及访问器。
Software upgrade messages & replies
header/src/main/java/org/zstack/header/storage/backup/SoftwareUpgradePackageDeployMsg.java, .../SoftwareUpgradePackageDeployReply.java
新增升级包部署消息与空回复类,消息包含源/目标路径、目标主机 SSH 凭据与脚本路径等字段及访问器。
Ceph backup storage plugin
plugin/ceph/src/main/java/org/zstack/storage/ceph/backup/CephBackupStorageBase.java
新增多种 AgentCommand/AgentResponse 内部类、REST 路径常量并注入 ApiTimeoutManager;实现对新增消息的构造/解析、HTTP 调用、超时与取消处理以及回复映射。
BackupStorageBase handlers
storage/src/main/java/org/zstack/storage/backup/BackupStorageBase.java
新增 5 个受保护的本地消息处理方法(Upload/Delete/GetProgress/SoftwareUpgrade/CancelDownload),并在本地消息路由中加入相应 instanceof 分支,默认委派到 bus.dealWithUnknownMessage(msg)。
SDK: software package actions & results
sdk/src/main/java/org/zstack/sdk/softwarePackage/header/*Upload*, *UploadAndExecute*, *CleanUpgrade*, *CleanUpgradeSoftwarePackageAction.java, *CleanUpgradeSoftwarePackageResult.java
新增多个 SDK Action 与 Result/DTO(上传软件包、上传并执行升级、清理升级等),含 REST 元数据、参数定义、同步/异步调用封装与结果映射。
SDK: zMigrate APIs & results
sdk/src/main/java/org/zstack/sdk/zmigrate/api/GetZMigrateGatewayVmInstancesAction.java, .../GetZMigrateGatewayVmInstancesResult.java, GetZMigrateImagesAction.java, GetZMigrateImagesResult.java, GetZMigrateInfosAction.java, GetZMigrateInfosResult.java
新增 zMigrate 相关 SDK Action 与返回 DTO,提供获取网关 VM、镜像列表与管理信息的 API 封装与 REST 元数据。
SDK: additional license
sdk/src/main/java/org/zstack/sdk/AdditionalLicenseType.java
在枚举中新增许可证类型常量 zmigrate
Test utilities & simulators
testlib/src/main/java/org/zstack/testlib/ApiHelper.groovy, testlib/src/main/java/org/zstack/testlib/CephBackupStorageSpec.groovy
ApiHelper 增加/调整大量 API 封装方法(包括 cleanUpgradeSoftwarePackage);CephBackupStorageSpec 新增 Ceph 备份存储的下载/上传/进度/删除/升级部署模拟器响应。

Sequence Diagram(s)

sequenceDiagram
  participant SDK as "SDK Action"
  participant API as "API Server"
  participant Bus as "Message Bus / BackupStorageBase"
  participant Ceph as "CephBackupStorageBase"
  participant Agent as "Backup Storage Agent"

  SDK->>API: 发起上传/下载/删除/升级 等 REST 请求
  API->>Bus: 构造 NeedReplyMessage / CancelMessage 并发送
  Bus->>Ceph: BackupStorageBase 转发到 CephBackupStorageBase 处理
  Ceph->>Agent: 构造 AgentCommand (Download/Upload/Delete/Progress/Upgrade/Cancel)
  Agent-->>Ceph: 返回 AgentResponse / 进度/状态
  Ceph-->>Bus: 映射为 MessageReply 并回传
  Bus-->>API: API 返回结果给调用方
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇 我是小兔提新令,消息与回复排队行,
Ceph 路径开新径,上传下载竞相鸣,
进度与取消细数声,升级脚本悄助攻,
SDK 与测试共欢庆,合并一跳春色盈。

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed PR标题清晰准确地反映了主要变更内容,即支持第三方软件包(thirdPartySoftwarePackage)功能。
Description check ✅ Passed PR描述与变更相关,提供了问题解决编号(ZSV-1)和来源信息(sync from gitlab !9141),内容与changeset相关。

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch sync/tao.gan/zmigrate-ZSV-1000@@3

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In
`@plugin/ceph/src/main/java/org/zstack/storage/ceph/backup/CephBackupStorageBase.java`:
- Around line 2174-2206: The DeleteFilesCmd sent via
CephBackupStorageMonBase.httpCall lacks the AgentCommand base fields; before
calling monBase.httpCall in handle(DeleteFilesOnBackupStorageHostMsg) set the
command's base fields (e.g. cmd.fsid = getSelf().getFsid(); cmd.uuid =
mon.getUuid()) so the agent receives fsid/uuid, and apply the same fix to the
other httpCall usages in this class (the other commands used in the ranges
referenced) — ensure each AgentCommand subclass populates fsid and uuid (or the
equivalent base fields) from getSelf() and the mon before calling
monBase.httpCall(...).

In `@sdk/src/main/java/org/zstack/sdk/zmigrate/api/GetZMigrateImagesAction.java`:
- Around line 47-58: In makeResult, rename the abbreviated variables to full
words to improve readability: change parameter res to apiResult, local ret to
result, and local value to resultValue (or similar descriptive names); update
the references inside the method (including the error check, assignment
ret.error, and the getResult call
org.zstack.sdk.zmigrate.api.GetZMigrateImagesResult) so the logic and
null-coalescing behavior remain identical; apply the same renaming pattern to
the other occurrences noted (around the other makeResult-like blocks at the
indicated regions) so all short names (res/ret/value/info) become descriptive
across GetZMigrateImagesAction.

In `@testlib/src/main/java/org/zstack/testlib/CephBackupStorageSpec.groovy`:
- Around line 233-260: The simulator handlers for
CephBackupStorageBase.FILE_UPLOAD_PATH and
CephBackupStorageBase.FILE_UPLOAD_PROGRESS_PATH are returning generic
AgentResponse which mismatches real agents; change the FILE_UPLOAD_PATH handler
to return a CephBackupStorageBase.UploadFileResponse and set its
directUploadPath (and any other expected fields), and change the
FILE_UPLOAD_PROGRESS_PATH handler to return a
CephBackupStorageBase.GetDownloadFileProgressResponse and populate the
progress-related fields so tests see the same shape as real responses; keep the
other simulator handlers as-is.
🧹 Nitpick comments (4)
sdk/src/main/java/org/zstack/sdk/softwarePackage/header/UploadSoftwarePackageToBackupStorageResult.java (1)

3-3: 冗余的同包导入。

SoftwarePackageInventory 与当前类在同一个包 org.zstack.sdk.softwarePackage.header 中,无需显式导入。

♻️ 建议移除冗余导入
 package org.zstack.sdk.softwarePackage.header;
 
-import org.zstack.sdk.softwarePackage.header.SoftwarePackageInventory;
-
 public class UploadSoftwarePackageToBackupStorageResult {
sdk/src/main/java/org/zstack/sdk/zmigrate/api/GetZMigrateGatewayVmInstancesResult.java (1)

14-20: List 类型应添加泛型参数以增强类型安全。

当前 gatewayVmInstances 使用原始类型 java.util.List,缺少泛型参数。虽然此模式在 SDK 中广泛采用(整个 SDK 代码库都遵循此约定),但添加泛型参数(如 List<String>List<?>)可以提高编译时类型检查的严格性。建议与其他 SDK 类统一进行泛型参数升级。

sdk/src/main/java/org/zstack/sdk/softwarePackage/header/UploadAndExecuteSoftwareUpgradePackageResult.java (1)

4-4: 冗余的同包导入语句

SoftwarePackageInventory 与当前类在同一个包 org.zstack.sdk.softwarePackage.header 中,Java 不需要显式导入同包类。

♻️ 建议移除冗余导入
 package org.zstack.sdk.softwarePackage.header;
 
-
-import org.zstack.sdk.softwarePackage.header.SoftwarePackageInventory;
 
 public class UploadAndExecuteSoftwareUpgradePackageResult {
header/src/main/java/org/zstack/header/storage/backup/SoftwareUpgradePackageDeployMsg.java (1)

11-11: 可选建议:考虑使用 Integer 包装类型替代 int。

targetHostSshPort 使用 int 原始类型,默认值为 0。如果需要区分"未设置"和"显式设置为 0"的情况,建议使用 Integer 类型,以便通过 null 检测未设置状态。

♻️ 可选改进
-    private int targetHostSshPort;
+    private Integer targetHostSshPort;

同时需要修改 getter/setter 的返回类型:

-    public int getTargetHostSshPort() {
+    public Integer getTargetHostSshPort() {
         return targetHostSshPort;
     }

-    public void setTargetHostSshPort(int targetHostSshPort) {
+    public void setTargetHostSshPort(Integer targetHostSshPort) {
         this.targetHostSshPort = targetHostSshPort;
     }

Comment on lines +2174 to +2206
@Override
protected void handle(final DeleteFilesOnBackupStorageHostMsg msg) {
DeleteFilesOnBackupStorageHostReply reply = new DeleteFilesOnBackupStorageHostReply();
if (CoreGlobalProperty.UNIT_TEST_ON) {
bus.reply(msg, reply);
return;
}

CephBackupStorageMonVO mon = getSelf().getMons().stream()
.filter(m -> m.getUuid().equals(msg.getBackupStorageHostUuid())).findAny().orElse(null);
if (mon == null) {
reply.setError(operr("failed to find mon with uuid [%s]", msg.getBackupStorageHostUuid()));
bus.reply(msg, reply);
return;
}

DeleteFilesCmd cmd = new DeleteFilesCmd();
cmd.filesPath = msg.getFilesPath();

CephBackupStorageMonBase monBase = new CephBackupStorageMonBase(mon);
monBase.httpCall(DELETE_FILE_PATH, cmd, DeleteFilesResponse.class, new ReturnValueCompletion<DeleteFilesResponse>(msg) {
@Override
public void fail(ErrorCode err) {
reply.setError(err);
bus.reply(msg, reply);
}

@Override
public void success(DeleteFilesResponse rsp) {
bus.reply(msg, reply);
}
});
}
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

为指定 mon 的 httpCall 补全 AgentCommand 基础字段。
这三处直接使用 CephBackupStorageMonBase.httpCall(...),不会像 HttpCaller 一样自动填充 fsid/uuid。若 agent 依赖该字段,会导致请求失败或定位错误。建议在发送前显式赋值。

✅ 建议修复
 DeleteFilesCmd cmd = new DeleteFilesCmd();
+cmd.setUuid(self.getUuid());
+cmd.setFsid(getSelf().getFsid());
 cmd.filesPath = msg.getFilesPath();
 GetDownloadFileProgressCmd cmd = new GetDownloadFileProgressCmd();
+cmd.setUuid(self.getUuid());
+cmd.setFsid(getSelf().getFsid());
 cmd.taskUuid = msg.getTaskUuid();
 SoftwareUpgradePackageCmd cmd = new SoftwareUpgradePackageCmd();
+cmd.setUuid(self.getUuid());
+cmd.setFsid(getSelf().getFsid());
 cmd.upgradePackagePath = msg.getUpgradePackagePath();

Also applies to: 2208-2250, 2252-2291

🤖 Prompt for AI Agents
In
`@plugin/ceph/src/main/java/org/zstack/storage/ceph/backup/CephBackupStorageBase.java`
around lines 2174 - 2206, The DeleteFilesCmd sent via
CephBackupStorageMonBase.httpCall lacks the AgentCommand base fields; before
calling monBase.httpCall in handle(DeleteFilesOnBackupStorageHostMsg) set the
command's base fields (e.g. cmd.fsid = getSelf().getFsid(); cmd.uuid =
mon.getUuid()) so the agent receives fsid/uuid, and apply the same fix to the
other httpCall usages in this class (the other commands used in the ranges
referenced) — ensure each AgentCommand subclass populates fsid and uuid (or the
equivalent base fields) from getSelf() and the mon before calling
monBase.httpCall(...).

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment from tao.gan:

删除物理机上文件,不需要补全

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MatheMatrix 感谢您的说明!

理解了,如果删除文件的 agent 端点确实不依赖这些字段,那么 DeleteFilesCmd 可以不补全。

不过注意到同一批代码中,GetDownloadFileProgressCmd (Line 2208-2250) 和 SoftwareUpgradePackageCmd (Line 2252-2291) 也采用了相同的直接调用 monBase.httpCall() 的方式,没有经过 HttpCallerprepareCmd() 环节。

请确认:

  1. 这两个命令的 agent 端点是否也不需要 fsid/uuid 字段?
  2. 如果需要,建议在这两处补全字段,保持与代码库中其他 AgentCommand 的一致性

如果这三个操作的 agent 实现都明确不依赖这些字段,可以考虑在代码注释中说明原因,方便后续维护。

Comment on lines +47 to +58
private Result makeResult(ApiResult res) {
Result ret = new Result();
if (res.error != null) {
ret.error = res.error;
return ret;
}

org.zstack.sdk.zmigrate.api.GetZMigrateImagesResult value = res.getResult(org.zstack.sdk.zmigrate.api.GetZMigrateImagesResult.class);
ret.value = value == null ? new org.zstack.sdk.zmigrate.api.GetZMigrateImagesResult() : value;

return ret;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

避免使用缩写变量名以符合命名规范。

ret/res/info 可读性较弱,建议改为完整单词。

♻️ 建议命名修正
-    private Result makeResult(ApiResult res) {
-        Result ret = new Result();
-        if (res.error != null) {
-            ret.error = res.error;
-            return ret;
+    private Result makeResult(ApiResult apiResult) {
+        Result result = new Result();
+        if (apiResult.error != null) {
+            result.error = apiResult.error;
+            return result;
         }
         
-        org.zstack.sdk.zmigrate.api.GetZMigrateImagesResult value = res.getResult(org.zstack.sdk.zmigrate.api.GetZMigrateImagesResult.class);
-        ret.value = value == null ? new org.zstack.sdk.zmigrate.api.GetZMigrateImagesResult() : value; 
-
-        return ret;
+        org.zstack.sdk.zmigrate.api.GetZMigrateImagesResult value = apiResult.getResult(org.zstack.sdk.zmigrate.api.GetZMigrateImagesResult.class);
+        result.value = value == null ? new org.zstack.sdk.zmigrate.api.GetZMigrateImagesResult() : value; 
+
+        return result;
     }
@@
-            public void complete(ApiResult res) {
-                completion.complete(makeResult(res));
+            public void complete(ApiResult apiResult) {
+                completion.complete(makeResult(apiResult));
             }
@@
-        RestInfo info = new RestInfo();
-        info.httpMethod = "GET";
-        info.path = "/zmigrate/images";
-        info.needSession = true;
-        info.needPoll = false;
-        info.parameterName = "";
-        return info;
+        RestInfo restInfo = new RestInfo();
+        restInfo.httpMethod = "GET";
+        restInfo.path = "/zmigrate/images";
+        restInfo.needSession = true;
+        restInfo.needPoll = false;
+        restInfo.parameterName = "";
+        return restInfo;
     }
As per coding guidelines 不允许使用不必要的缩写,如:AbsSchedulerJob、condi、Fu 等。应使用完整单词提升可读性。

Also applies to: 66-69, 82-89

🤖 Prompt for AI Agents
In `@sdk/src/main/java/org/zstack/sdk/zmigrate/api/GetZMigrateImagesAction.java`
around lines 47 - 58, In makeResult, rename the abbreviated variables to full
words to improve readability: change parameter res to apiResult, local ret to
result, and local value to resultValue (or similar descriptive names); update
the references inside the method (including the error check, assignment
ret.error, and the getResult call
org.zstack.sdk.zmigrate.api.GetZMigrateImagesResult) so the logic and
null-coalescing behavior remain identical; apply the same renaming pattern to
the other occurrences noted (around the other makeResult-like blocks at the
indicated regions) so all short names (res/ret/value/info) become descriptive
across GetZMigrateImagesAction.

Comment thread testlib/src/main/java/org/zstack/testlib/CephBackupStorageSpec.groovy Outdated
@MatheMatrix MatheMatrix force-pushed the sync/tao.gan/zmigrate-ZSV-1000@@3 branch from 19b4a85 to 9906a04 Compare February 9, 2026 12:21
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In
`@sdk/src/main/java/org/zstack/sdk/zmigrate/api/GetZMigrateGatewayVmInstancesResult.java`:
- Around line 6-17: The setters in GetZMigrateGatewayVmInstancesResult use
parameter names identical to the fields (managementVmInstanceUuid,
gatewayVmInstances); rename the parameters (e.g., managementVmInstanceUuidParam
or value, gatewayVmInstancesParam) inside setManagementVmInstanceUuid and
setGatewayVmInstances and assign them to this.managementVmInstanceUuid and
this.gatewayVmInstances respectively to avoid name collision and follow naming
conventions.

In `@testlib/src/main/java/org/zstack/testlib/ApiHelper.groovy`:
- Around line 38617-38641: The cleanUpgradeSoftwarePackage method in ApiHelper
(and similar methods) appears to be manually edited in generated code; revert
any manual changes and regenerate this file from the upstream code generator so
the changes come from the generator rather than hand edits; ensure the generator
source that emits methods like cleanUpgradeSoftwarePackage, the ApiPathTracker
usage and Test.apiPaths population is updated/synced and then re-run the
generator to produce a consistent ApiHelper.groovy output.
🧹 Nitpick comments (5)
sdk/src/main/java/org/zstack/sdk/zmigrate/api/GetZMigrateImagesResult.java (1)

6-6: 建议为 Map 添加泛型参数以增强类型安全。

使用原始类型 java.util.Map 会产生编译器警告,并且缺乏类型安全性。建议指定泛型参数,例如 Map<String, Object> 或更具体的类型。

♻️ 建议的修改
-    public java.util.Map images;
+    public java.util.Map<String, Object> images;

同时更新 getter 和 setter 的签名:

-    public void setImages(java.util.Map images) {
+    public void setImages(java.util.Map<String, Object> images) {
         this.images = images;
     }
-    public java.util.Map getImages() {
+    public java.util.Map<String, Object> getImages() {
         return this.images;
     }
sdk/src/main/java/org/zstack/sdk/softwarePackage/header/UploadSoftwarePackageToBackupStorageResult.java (1)

3-3: 冗余的同包导入

SoftwarePackageInventory 与当前类在同一个包 org.zstack.sdk.softwarePackage.header 中,无需显式导入。

♻️ 建议移除冗余导入
 package org.zstack.sdk.softwarePackage.header;
 
-import org.zstack.sdk.softwarePackage.header.SoftwarePackageInventory;
-
 public class UploadSoftwarePackageToBackupStorageResult {
sdk/src/main/java/org/zstack/sdk/softwarePackage/header/CleanUpgradeSoftwarePackageAction.java (1)

34-38: 原始类型 List 缺少泛型参数

systemTagsuserTags 使用了原始的 java.util.List 类型,建议添加泛型参数以提高类型安全性。不过考虑到这与项目中其他 SDK Action 类的风格保持一致,可作为后续统一优化的改进点。

♻️ 建议添加泛型参数
     `@Param`(required = false)
-    public java.util.List systemTags;
+    public java.util.List<String> systemTags;

     `@Param`(required = false)
-    public java.util.List userTags;
+    public java.util.List<String> userTags;
sdk/src/main/java/org/zstack/sdk/softwarePackage/header/UploadSoftwarePackageToBackupStorageAction.java (1)

46-53: 原始类型 List 缺少泛型参数

tagUuidssystemTagsuserTags 使用了原始的 java.util.List 类型。与前一个文件相同的建议,可考虑添加泛型参数。

♻️ 建议添加泛型参数
     `@Param`(required = false, nonempty = false, nullElements = false, emptyString = true, noTrim = false)
-    public java.util.List tagUuids;
+    public java.util.List<String> tagUuids;

     `@Param`(required = false)
-    public java.util.List systemTags;
+    public java.util.List<String> systemTags;

     `@Param`(required = false)
-    public java.util.List userTags;
+    public java.util.List<String> userTags;
sdk/src/main/java/org/zstack/sdk/zmigrate/api/GetZMigrateGatewayVmInstancesResult.java (1)

14-19: List 增加泛型以提升类型安全

当前使用了原始类型 List,建议补充具体泛型类型。根据字段名称 gatewayVmInstances,该列表很可能存储 VM 实例标识符(如 String UUID)或对象。如无法确定具体类型,至少应使用 List<?> 作为类型安全的最小保障。

♻️ 建议修改
-    public java.util.List gatewayVmInstances;
-    public void setGatewayVmInstances(java.util.List gatewayVmInstances) {
+    public java.util.List<?> gatewayVmInstances;
+    public void setGatewayVmInstances(java.util.List<?> gatewayVmInstances) {
         this.gatewayVmInstances = gatewayVmInstances;
     }
-    public java.util.List getGatewayVmInstances() {
+    public java.util.List<?> getGatewayVmInstances() {
         return this.gatewayVmInstances;
     }

Comment on lines +6 to +17
public java.lang.String managementVmInstanceUuid;
public void setManagementVmInstanceUuid(java.lang.String managementVmInstanceUuid) {
this.managementVmInstanceUuid = managementVmInstanceUuid;
}
public java.lang.String getManagementVmInstanceUuid() {
return this.managementVmInstanceUuid;
}

public java.util.List gatewayVmInstances;
public void setGatewayVmInstances(java.util.List gatewayVmInstances) {
this.gatewayVmInstances = gatewayVmInstances;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

参数名与成员变量同名,违反命名规范

setter 参数与字段同名,按规范需要避免,建议改成不同的参数名以减少歧义。

🔧 建议修改
-    public void setManagementVmInstanceUuid(java.lang.String managementVmInstanceUuid) {
-        this.managementVmInstanceUuid = managementVmInstanceUuid;
+    public void setManagementVmInstanceUuid(java.lang.String managementVmInstanceUuidValue) {
+        this.managementVmInstanceUuid = managementVmInstanceUuidValue;
     }
@@
-    public void setGatewayVmInstances(java.util.List gatewayVmInstances) {
-        this.gatewayVmInstances = gatewayVmInstances;
+    public void setGatewayVmInstances(java.util.List gatewayVmInstancesValue) {
+        this.gatewayVmInstances = gatewayVmInstancesValue;
     }

As per coding guidelines: 避免在父子类或同一代码块中出现相同名字的成员或局部变量,防止混淆。

🤖 Prompt for AI Agents
In
`@sdk/src/main/java/org/zstack/sdk/zmigrate/api/GetZMigrateGatewayVmInstancesResult.java`
around lines 6 - 17, The setters in GetZMigrateGatewayVmInstancesResult use
parameter names identical to the fields (managementVmInstanceUuid,
gatewayVmInstances); rename the parameters (e.g., managementVmInstanceUuidParam
or value, gatewayVmInstancesParam) inside setManagementVmInstanceUuid and
setGatewayVmInstances and assign them to this.managementVmInstanceUuid and
this.gatewayVmInstances respectively to avoid name collision and follow naming
conventions.

Comment thread testlib/src/main/java/org/zstack/testlib/ApiHelper.groovy
@MatheMatrix MatheMatrix force-pushed the sync/tao.gan/zmigrate-ZSV-1000@@3 branch 2 times, most recently from ddb9e36 to 9c184d2 Compare February 9, 2026 13:08
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@sdk/src/main/java/org/zstack/sdk/softwarePackage/header/CleanUpgradeSoftwarePackageAction.java`:
- Around line 94-102: The REST path in
CleanUpgradeSoftwarePackageAction.getRestInfo currently uses the singular
"/software-package/upgrade/{uuid}" and should be changed to the plural form to
match the module convention and UploadAndExecuteSoftwareUpgradePackageAction;
update the RestInfo.path value in getRestInfo of class
CleanUpgradeSoftwarePackageAction to "/software-packages/upgrade/{uuid}" so API
naming is consistent across actions.
🧹 Nitpick comments (3)
sdk/src/main/java/org/zstack/sdk/zmigrate/api/GetZMigrateGatewayVmInstancesAction.java (1)

28-33: 建议为标签列表添加泛型,提升类型安全。

使用原始 List 会丢失编译期类型检查,建议改为 List。

♻️ 建议修改
-    public java.util.List systemTags;
+    public java.util.List<String> systemTags;

-    public java.util.List userTags;
+    public java.util.List<String> userTags;
sdk/src/main/java/org/zstack/sdk/softwarePackage/header/UploadSoftwarePackageToBackupStorageAction.java (1)

46-53: List 字段使用了原始类型。

tagUuidssystemTagsuserTags 字段使用了原始 List 类型而非泛型类型。由于这是自动生成的 SDK 代码,此问题可忽略,但如果手动维护,建议使用 List<String> 以提高类型安全性。

sdk/src/main/java/org/zstack/sdk/softwarePackage/header/CleanUpgradeSoftwarePackageAction.java (1)

28-29: uuid 参数缺少 maxLength 约束。

uuid 字段标记为 required = true,但未指定 maxLength。其他类似的 Action 类中,UUID 字段通常会有长度限制(如 32 或 36 字符)。建议确认是否需要添加长度约束以保持一致性。

Comment on lines +94 to +102
protected RestInfo getRestInfo() {
RestInfo info = new RestInfo();
info.httpMethod = "DELETE";
info.path = "/software-package/upgrade/{uuid}";
info.needSession = true;
info.needPoll = true;
info.parameterName = "";
return info;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 搜索服务端 API 定义中的 software-package 相关路径
echo "=== 搜索服务端 API 定义 ==="
rg -n "software-package|upgrade" --type java -g '!sdk/**' -i | grep -E "(RestRequest|path)" | head -20

# 搜索类似的清理/操作端点
echo -e "\n=== 搜索类似的清理操作端点 ==="
rg -n "@RestRequest.*DELETE" --type java -A 2 | head -40

# 查找 header 文件中的 API 定义
echo -e "\n=== 查找 software-package 相关的 header 定义 ==="
find . -path "*/header/*" -name "*Software*" -type f | head -20

Repository: MatheMatrix/zstack

Length of output: 3124


🏁 Script executed:

#!/bin/bash
# 查看完整的 CleanUpgradeSoftwarePackageAction 所在目录结构
echo "=== CleanUpgradeSoftwarePackageAction 文件所在目录 ==="
find . -name "CleanUpgradeSoftwarePackageAction.java" -type f

# 查找对应的消息类(server-side)
echo -e "\n=== 查找对应的服务端消息类 ==="
find . -name "*CleanUpgradeSoftwarePackage*" -type f | grep -v sdk | grep -v target

# 查找 UploadSoftwarePackageToBackupStorageAction 对比
echo -e "\n=== 查找上传操作进行对比 ==="
find . -name "*UploadSoftwarePackageToBackupStorage*" -type f

Repository: MatheMatrix/zstack

Length of output: 460


🏁 Script executed:

#!/bin/bash
# 检查 UploadSoftwarePackageToBackupStorageAction 的实际路径
echo "=== UploadSoftwarePackageToBackupStorageAction 路径 ==="
grep -n "path.*software" ./sdk/src/main/java/org/zstack/sdk/softwarePackage/header/UploadSoftwarePackageToBackupStorageAction.java

# 检查 CleanUpgradeSoftwarePackageAction 的路径
echo -e "\n=== CleanUpgradeSoftwarePackageAction 路径 ==="
grep -n "path.*software" ./sdk/src/main/java/org/zstack/sdk/softwarePackage/header/CleanUpgradeSoftwarePackageAction.java

# 搜索所有 software 相关的 API 消息定义(server-side)
echo -e "\n=== 搜索所有 software 相关的服务端 API 消息 ==="
find . -path "*/header/*" -type f -name "*.java" ! -path "*/sdk/*" | xargs grep -l -i "software.*msg\|software.*message" 2>/dev/null | head -10

# 检查是否有 CleanUpgrade 相关的服务端定义
echo -e "\n=== 查找 CleanUpgrade 服务端定义 ==="
find . -path "*/header/*" -type f -name "*.java" ! -path "*/sdk/*" | xargs grep -l "CleanUpgrade\|CleanUpgradeSoftware" 2>/dev/null

Repository: MatheMatrix/zstack

Length of output: 546


🏁 Script executed:

#!/bin/bash
# 对比所有软件包相关操作的 REST 路径
echo "=== 所有 softwarePackage 相关操作的 REST 路径 ==="
grep -r "path = " ./sdk/src/main/java/org/zstack/sdk/softwarePackage/header/ --include="*.java" | grep -E "Action.java" | sed 's/.*path = "//;s/".*//'

# 查找其他 upgrade 相关的路径模式
echo -e "\n=== 其他 upgrade 相关的路径 ==="
rg "path.*upgrade" --type java -A 1 -B 1 | head -30

Repository: MatheMatrix/zstack

Length of output: 1356


🏁 Script executed:

#!/bin/bash
# 查找服务端的 API 消息类定义,看是否存在对应的 CleanUpgrade 定义
echo "=== 在 header 文件夹中查找任何 upgrade 相关的 API 消息 ==="
find ./header -type f -name "*Upgrade*.java" | grep -E "Msg|Message" | head -20

# 查看 SoftwareUpgradePackageDeployMsg 的内容
echo -e "\n=== 查看 SoftwareUpgradePackageDeployMsg 内容 ==="
head -20 ./header/src/main/java/org/zstack/header/storage/backup/SoftwareUpgradePackageDeployMsg.java

# 查找任何包含 clean 或 remove upgrade 的定义
echo -e "\n=== 查找清理升级包的服务端定义 ==="
rg -n "CleanUpgrade|RemoveUpgrade|DeleteUpgrade" --type java | grep -v sdk | head -20

Repository: MatheMatrix/zstack

Length of output: 969


🏁 Script executed:

#!/bin/bash
# 检查 UploadAndExecuteSoftwareUpgradePackageAction 是否是类似的操作
echo "=== UploadAndExecuteSoftwareUpgradePackageAction 完整路径 ==="
sed -n '94,104p' ./sdk/src/main/java/org/zstack/sdk/softwarePackage/header/UploadAndExecuteSoftwareUpgradePackageAction.java

# 查看所有 software-package 开头的路径
echo -e "\n=== 所有以 /software-package 开头的路径(singular)==="
grep -r "path = \"/software-package" ./sdk/src/main/java/org/zstack/sdk/softwarePackage/ | sed 's/.*path = "//;s/".*//' | sort -u

# 查看所有 /software-packages 开头的路径(plural)
echo -e "\n=== 所有以 /software-packages 开头的路径(plural)==="
grep -r "path = \"/software-packages" ./sdk/src/main/java/org/zstack/sdk/softwarePackage/ | sed 's/.*path = "//;s/".*//' | sort -u

Repository: MatheMatrix/zstack

Length of output: 962


REST 路径命名不一致。

当前路径 /software-package/upgrade/{uuid} 使用了单数形式,但同一模块中 UploadAndExecuteSoftwareUpgradePackageAction 使用的是 /software-packages/backup-storage/{uuid}/upgrade(复数形式)。

根据编码规范:"path: 针对资源使用复数形式",建议统一使用复数形式 /software-packages/upgrade/{uuid} 以保持 API 一致性。

建议修改
     protected RestInfo getRestInfo() {
         RestInfo info = new RestInfo();
         info.httpMethod = "DELETE";
-        info.path = "/software-package/upgrade/{uuid}";
+        info.path = "/software-packages/upgrade/{uuid}";
         info.needSession = true;
         info.needPoll = true;
         info.parameterName = "";
         return info;
     }
🤖 Prompt for AI Agents
In
`@sdk/src/main/java/org/zstack/sdk/softwarePackage/header/CleanUpgradeSoftwarePackageAction.java`
around lines 94 - 102, The REST path in
CleanUpgradeSoftwarePackageAction.getRestInfo currently uses the singular
"/software-package/upgrade/{uuid}" and should be changed to the plural form to
match the module convention and UploadAndExecuteSoftwareUpgradePackageAction;
update the RestInfo.path value in getRestInfo of class
CleanUpgradeSoftwarePackageAction to "/software-packages/upgrade/{uuid}" so API
naming is consistent across actions.

@MatheMatrix MatheMatrix force-pushed the sync/tao.gan/zmigrate-ZSV-1000@@3 branch 10 times, most recently from 9cb785d to ed7009d Compare February 10, 2026 03:40
@MatheMatrix
Copy link
Copy Markdown
Owner

Comment from ye.zou:

Code Review Summary (AI-Assisted)

Verdict: CONDITIONAL - 修复 Critical 问题后可合并

Critical (3)

  1. SSH密码明文传输 - SoftwareUpgradePackageDeployMsg.java

    • targetHostSshPassword 虽有 @NoLogging,但通过 CloudBus 消息明文传递
    • 建议参考 AddKVMHostMsg 使用加密机制
  2. 缺少输入验证 - UploadFileToBackupStorageHostMsg.java

    • urlinstallPath 无验证,可能导致 path traversal 或 URL scheme 滥用
    • 建议添加路径白名单和 scheme 限制
  3. 批量删除无保护 - DeleteFilesOnBackupStorageHostMsg.java

    • filesPath 列表无大小限制,无路径白名单
    • 建议限制最大删除数量,验证路径在安全目录内

Major (3)

  1. 消息路由注册 - 确认 CephBackupStorageBaseBackupStorageBase 中已注册所有新消息的处理方法
  2. CancelDownloadFileOnBackupStorageHostMsg - 缺少 taskUuid 字段用于指定取消目标
  3. Commit message - scope 为 volume 但实际改的是 backup storage,type 用了 fix 但这是新功能

Minor

  • 消息类命名风格不统一
  • Reply 类字段过多,建议拆分或添加注释

Review by AI Assistant (Claude) - 请作者确认实现文件中的路径验证和消息处理逻辑

@ZStack-Robot
Copy link
Copy Markdown
Collaborator

Comment from tao.gan:

1 SSH密码明文传输
1)目前场景仅支持 mn 日志不展示密码。明文传输密码问题,暂不处理

2 缺少输入验证

url 有 scheme 限制

installPath 在 api 中已做校验

3 批量删除无保护

底层删除文件使用 linux.rm_dir_force ,有白名单

4 消息路由注册

1)正常的使用方式

5 CancelDownloadFileOnBackupStorageHostMsg

取消的 job 是 api id,不需要 taskUuid

6 Commit message

type 和 volume 是自动生成的

@MatheMatrix MatheMatrix force-pushed the sync/tao.gan/zmigrate-ZSV-1000@@3 branch 4 times, most recently from 9dd9ec8 to 104ce68 Compare February 11, 2026 14:48
@MatheMatrix
Copy link
Copy Markdown
Owner

Comment from ye.zou:

Code Review — zstack !9141

Reviewer: AI Code Review Bot | Verdict: REQUEST CHANGES

[CRITICAL] Command injection via SSH parameters to agent

File: CephBackupStorageBase.javaSoftwareUpgradePackageCmd

SSH credentials and script paths passed to the Python agent without validation. The agent then interpolates them into sshpass/ssh/scp shell commands.

Fix: Add input validation before building the command:

  • IP: regex ^[\d.]+$
  • Username: regex ^[a-zA-Z0-9._-]+$
  • Script path: must be absolute, no .., alphanumeric + /._- only

[CRITICAL] Path traversal in file download/upload

File: CephBackupStorageBase.javahandle(UploadFileToBackupStorageHostMsg)

installPath used without path traversal validation. ../../etc/shadow could write to arbitrary locations.

Fix: Validate installPath — must be absolute, must not contain ...

[HIGH] Null safety on getMons().stream()

File: CephBackupStorageBase.java — multiple handler methods

getSelf().getMons() may return null → NPE on .stream(). Appears at lines ~2177, 2213, 2254, 2293.

Fix: Add if (getSelf().getMons() == null || getSelf().getMons().isEmpty()) guard.

[HIGH] Missing URL scheme whitelist

File: CephBackupStorageBase.java

After extracting scheme from URL, no whitelist validation. Only http/https/ftp/file should be allowed.

[MEDIUM] @NoLogging may not prevent password in error stack traces

File: SoftwareUpgradePackageCmd.targetHostSshPassword


5 issues total (2 CRITICAL, 2 HIGH, 1 MEDIUM). Please fix CRITICAL and HIGH before merge.

@zstack-robot-1
Copy link
Copy Markdown
Collaborator

Comment from tao.gan:

2 Path traversal in file download/upload
api 的 isntall 已经做了校验
image

@MatheMatrix MatheMatrix force-pushed the sync/tao.gan/zmigrate-ZSV-1000@@3 branch 3 times, most recently from f64341a to 4fac778 Compare February 12, 2026 12:38
@zstack-robot-1
Copy link
Copy Markdown
Collaborator

Comment from ye.zou:

Code Review: zstackio/zstack MR !9141

Feature: thirdPartySoftwarePackage support
Verdict: CONDITIONAL — 修复以下问题后可合并


🔴 Critical (必须修复)

1. NPE 风险 — backupStorageHostUuid 可能为 null
CephBackupStorageBase.java 中多处 mon 查找逻辑:

mons.stream().filter(m -> m.getUuid().equals(msg.getBackupStorageHostUuid())).findAny().orElse(null);

如果 msg.getBackupStorageHostUuid() 为 null,.equals() 会抛 NPE。
建议改为 Objects.equals() 或提前判空。

2. URL scheme 空字符串未校验
CephBackupStorageBase.java 中 scheme 只检查了 null,未检查空字符串:

if (scheme == null) { ... }
cmd.urlScheme = scheme; // scheme 可能是 ""

建议: if (scheme == null || scheme.isEmpty())

3. SSH 密码 @nologging 注解缺少 type
SoftwareUpgradePackageDeployMsg.java@NoLogging 未指定 type = NoLogging.Type.Password,建议补全。


🟡 Major (强烈建议修复)

4. 新 Message 类缺少输入验证
UploadFileToBackupStorageHostMsg 等新消息类的 urlinstallPathfilesPath 字段没有输入校验:

  • url 应验证格式
  • installPath 需防路径穿越 (../../)
  • filesPath 列表应限制最大元素数量

5. 大文件操作 timeout 策略
新增的下载/上传操作使用全局 timeout,大软件包可能超时。建议支持自定义 timeout 或根据操作类型调整。

6. Response vs Reply 字段命名不一致
Agent Response 用 directUploadPath,Java Reply 用 directUploadUrl,Path vs URL 易混淆,建议统一。


🔵 Minor

  • BackupStorageBase.java 新增的 5 个 handle() 默认实现都调用 bus.dealWithUnknownMessage(msg),建议加注释说明需子类覆盖
  • 多处 if (CoreGlobalProperty.UNIT_TEST_ON) 跳过逻辑重复,可抽取为通用方法
  • DeleteFilesCmd.filesPath 缺少 public 访问修饰符

🤖 AI Code Review

@MatheMatrix
Copy link
Copy Markdown
Owner

Comment from tao.gan:

1 NPE 风险

已修改

2 URL scheme 空字符串未校

已修改

3 SSH 密码 @nologging 注解缺少 type

现有用法,不需要增加 type

4 新 Message 类缺少输入验证

为内部消息,这类消息的参数在api中已校验

5 大文件操作 timeout 策略

上传文件超时时间由 agent 处理,不需要 控制面设置超时

6 Response vs Reply

同一改为 directUploadUrl

7 BackupStorageBase.java 新增的 5 个 handle() 默认实现都调用 bus.dealWithUnknownMessage(msg),建议加注释说明需子类覆盖

表示消息目前进 重载 此函数的子类支持处理此逻辑。

8 多处 if (CoreGlobalProperty.UNIT_TEST_ON) 跳过逻辑重复,可抽取为通用方法

运行case中不执行相关逻辑的通过做法

  • DeleteFilesCmd.filesPath 缺少 public 访问修饰符

已修改

@MatheMatrix MatheMatrix force-pushed the sync/tao.gan/zmigrate-ZSV-1000@@3 branch 13 times, most recently from acc03ac to f6c5250 Compare March 4, 2026 01:45
@ZStack-Robot
Copy link
Copy Markdown
Collaborator

Comment from ye.zou:

Code Review: zstack !9141 — thirdPartySoftwarePackage (Round 2)

总体判定: CONDITIONAL — CephBackupStorageBase 改动量大但结构清晰,有几个设计问题值得关注。


[P1/critical] CephBackupStorageBase: SoftwareUpgradePackageCmd 通过 HTTP 传递 SSH 密码

public static class SoftwareUpgradePackageCmd extends AgentCommand {
    public String targetHostSshUsername;
    @NoLogging
    public String targetHostSshPassword;
    public String targetHostIp;
}

这个命令通过 httpCall() 发送到 ceph agent。@NoLogging 只是不记日志,密码仍然以明文 JSON在网络上传输。如果 ceph agent 的 HTTP 通道没有走 TLS,SSH 密码就是裸奔的。

问题:

  1. ceph agent 通信是 HTTP 还是 HTTPS?
  2. 密码从 Java 传到 ceph agent,ceph agent 再 SSH 到目标机器——这是两跳明文传输

建议: 至少在 SoftwareUpgradePackageDeployMsg 层面对密码做加密(用 ZStack 的 EncryptRSA 机制),ceph agent 端解密后再用。或者确认 ceph agent 通道已经是 TLS。


[P2/major] CephBackupStorageBase.handle(UploadFileToBackupStorageHostMsg): URL scheme 解析重复

String scheme;
try {
    URI uri = new URI(msg.getUrl());
    scheme = uri.getScheme();
} catch (URISyntaxException e) {
    reply.setError(operr("failed to parse upload URL [%s]: %s", msg.getUrl(), e.getMessage()));
    bus.reply(msg, reply);
    return;
}

upload:// 开头的 URL 走了一条路径,其他 URL 走另一条路径。但对于 upload:// 的情况,DownloadFileCmd 里的 cmd.urlScheme 根本没设置(因为 upload:// 走的是 UploadFileCmd 路径)。这个没问题,只是两条路径的分叉点不够明显。

建议: 加个注释说明 upload:// 是分片上传协议,其他 scheme 走 agent 端下载。否则下一个维护者看这段代码会一头雾水。


[P2/major] BackupStorageBase: 需要确认 abstract handle 方法已声明

diff 只有 52 行,我看到新增了 6 个 handle 方法在 CephBackupStorageBase 里用 @Override。这些方法在 BackupStorageBase 里应该是 abstract 或有默认实现。请确认 BackupStorageBase.java 的 diff 确实声明了这些方法,否则编译会失败:

handle(UploadFileToBackupStorageHostMsg)
handle(UnzipFileOnBackupStorageHostMsg)
handle(DeleteFilesOnBackupStorageHostMsg)
handle(GetFileDownloadProgressFromBackupStorageHostMsg)
handle(SoftwareUpgradePackageDeployMsg)
handle(CancelDownloadFileOnBackupStorageHostMsg)

[P2/major] CephBackupStorageBase.findMonByUuid: 对 mons 集合的遍历

Set<CephBackupStorageMonVO> mons = getSelf().getMons();
return mons.stream()
    .filter(m -> m.getUuid().equals(backupStorageHostUuid))
    .findAny()
    .orElseThrow(...);

getSelf().getMons() 触发的是 Hibernate lazy-load。如果在同一个事务中多次调用 findMonByUuid(比如连续处理多条消息),每次都会触发查询。对于当前的使用方式(每条消息调用一次),这不是问题。但如果将来在批量操作中用到,记得缓存。


[P3/minor] 新的 Header 消息: DTO 合理但数量多

新增了 14 个 header 类(Msg + Reply 各 7 对)。每对都是标准的 NeedReplyMessage + MessageReply,结构正确:

  • UploadFileToBackupStorageHostMsg/Reply
  • DeleteFilesOnBackupStorageHostMsg/Reply
  • GetFileDownloadProgressFromBackupStorageHostMsg/Reply
  • UnzipFileOnBackupStorageHostMsg/Reply
  • SoftwareUpgradePackageDeployMsg/Reply
  • CancelDownloadFileOnBackupStorageHostMsg/Reply
  • UploadFileToBackupStorageHostReplydirectUploadUrl (注意不是 Path) — 与 utility 端的 directUploadUrl 一致,good。

SoftwareUpgradePackageDeployMsg@NoLogging on password 是正确的。UploadFileToBackupStorageHostMsg@NoLogging(type = Uri) on url 也是正确的。


[P3/minor] KVMAgentCommands.javaKVMHost.java: 9 行改动

改动很小,应该是在 KVM 端注册 upload file 的 handler。没有问题。


[P3/minor] 少了 CancelDownloadFileOnBackupStorageHostMsg 的完整 handler

CephBackupStorageBase 的 diff 在 800 行处截断了。请确认 handle(CancelDownloadFileOnBackupStorageHostMsg) 也已实现,否则 cancel LongJob 时会 NPE。


总结

严重度 数量 说明
P1 critical 1 SSH 密码明文 HTTP 传输
P2 major 2 BackupStorageBase 声明确认; URL scheme 分叉可读性
P3 minor 3 mon lazy-load, header DTO 数量, cancel handler 确认

核心问题是密码传输安全——如果 ceph agent 通道确认是 TLS 的,那 P1 可以降级为 P3。请确认。


Review by AI Assistant (Linus style, Round 2)

@zstack-robot-2
Copy link
Copy Markdown
Collaborator Author

Comment from ye.zou:

Review Tracker: ZSV-1000

Rounds: R1=conditional

# Sev Cat Repo File Finding R1
1 CRIT architecture premium ZMigrateSshClient.java checkZMigrateManagementIsReady blocks thread for 5min 🔴
2 CRIT security premium ZMigrateSshClient.java SSH password exposed in remote shell command line 🔴
3 CRIT security zstack CephBackupStorageBase.java SoftwareUpgradePackageCmd transmits SSH password via HTTP 🔴
4 CRIT compatibility zstack-utility file_system_upload_task.py linux.mkdir() may not exist in zstacklib - runtime crash 🔴
5 CRIT lifecycle zstack-utility host_plugin.py completed=True + success=False is contradictory 🔴
6 MAJ lifecycle premium AbstractSoftwarePackageLongJob.java track() method race condition - can overwrite failure 🔴
7 MAJ error-handling premium UploadSoftwarePackageToBackupStorageTracker.java bus.send(DeleteFilesMsg) fire-and-forget - orphan files 🔴
8 MAJ lifecycle premium UploadSoftwarePackageToBackupStorageTracker.java apiId captured from ThreadContext at construction - null on resume 🔴
9 MAJ convention premium ZMigrateGlobalConfig.java decodeGatewaySshPassword() is dead code - never called 🔴
10 MAJ compatibility premium ZceXManager.java Base64 encoding init config is incompatible with existing DB data 🔴
11 MAJ architecture zstack BackupStorageBase.java Confirm all 6 abstract handle methods are declared 🔴
12 MAJ logging zstack CephBackupStorageBase.java upload:// vs other URL scheme branching lacks comments 🔴
13 min convention premium ZMigrateApiInterceptor.java Missing newline at end of file 🔴
14 min architecture premium ZMigrateConstant.java Hardcoded gateway file paths - should be configurable 🔴
15 min convention premium SoftwarePackageApiInterceptor.java validatePath() method not visible in diff - confirm exists 🔴

Progress: ░░░░░░░░░░ 0/15 resolved (0%)
5 critical issue(s) still open

@MatheMatrix MatheMatrix force-pushed the sync/tao.gan/zmigrate-ZSV-1000@@3 branch from f6c5250 to 4ed80a6 Compare March 4, 2026 03:39
@zstack-robot-2
Copy link
Copy Markdown
Collaborator Author

Comment from ye.zou:

Code Review — zmigrate thirdPartySoftwarePackage (zstack 仓库)


🔴 Critical

C1. conf/errorCodes/ZMigratePlugin.xml vs ZMigratePluginErrors.java — 错误码 ID 不对齐

  • XML: 1003=VerifyGateway, 1006=GetLicenses, 1007=GetPlatformInfo...
  • 枚举: 1003=RegisterZSV, 1004=VerifyGateway, 1006=GetMgmtServerInfo...
  • 从 1003 开始全部错位,日志和 UI 中显示的错误描述全部张冠李戴
  • 必须修复:对齐两者的 ID 和顺序

C2. CephBackupStorageBase.java — God Object 继续增肥

  • 新增约 316 行业务逻辑(6 个 handle 方法 + 内部 Cmd/Rsp 类)直接加到已经臃肿的 God Object
  • 建议:抽取到 CephBackupStorageFileOperationHandler,在 Base 中组合调用

🟠 Major

M1. CephBackupStorageBase.java upload:// 分支未定位指定 mon

  • handle(UploadFileToBackupStorageHostMsg) 的 upload:// 分支未调用 findMonByUuid(),直接用 httpCall() 发给任意可用 mon
  • 后续 tracker 用 backupStorageHostUuid 追踪进度,若实际写入的 mon 与记录的不同,进度查询打到错误的 mon
  • 建议:upload:// 分支同样用 findMonByUuid(msg.getBackupStorageHostUuid()) 获取 mon

M2. CephBackupStorageBase.java:2163 — handleMon 空指针

  • success 回调中 rsp.handleMon.getMonUuid(),handleMon 可能为 null(agent 未填充),NPE 导致消息无 reply
  • 建议:加 null 检查

M3. CephBackupStorageBase.java:987 — SSH 凭据未校验

  • handle(SoftwareUpgradePackageDeployMsg) 未校验 targetHostSshUsername/Password 非空就发给 agent
  • 建议:发送前检查非 null 非空

M4. CephBackupStorageBase.java:293 — DeleteFiles 路径无白名单

  • handle(DeleteFilesOnBackupStorageHostMsg)filePaths 无路径前缀校验,可删任意文件
  • 建议:校验路径以允许的基目录前缀开头,拒绝 ../

M5. CephBackupStorageBase.java:102 — SoftwareUpgradePackageCmd 路径无校验

  • upgradePackagePathupgradeScriptPath 直接传给 agent 做 SCP/SSH,路径穿越风险
  • 建议:Java 和 Python 双层校验

M6. SSRF — url 字段接受任意 scheme

  • UploadFileToBackupStorageHostMsg 的 url 直接传给 agent 下载,含 ftp:// 等,内网 SSRF 风险
  • 建议:只允许 https:// 和 upload://

M7. BackupStorageBase.java — 新消息 dispatch 顺序不集中

  • 6 个新消息类型分散插入 dispatch chain
  • 建议:集中放在同一位置

🟡 Minor

  • Simulator (CephBackupStorageAgent.groovy) 只添加了 FILE_DOWNLOAD_PATH handler,缺少其他 5 个新路径的 stub
  • 非 Ceph BS 收到新消息时只得到 dealWithUnknownMessage 的通用错误,缺少明确的 "not supported" 提示

@MatheMatrix MatheMatrix force-pushed the sync/tao.gan/zmigrate-ZSV-1000@@3 branch 6 times, most recently from 4e5b9af to b73e83b Compare March 5, 2026 14:27
APIImpact

Resolves: ZSV-10000

Change-Id: I656979726c736267776c7262716969726874716d
@MatheMatrix MatheMatrix force-pushed the sync/tao.gan/zmigrate-ZSV-1000@@3 branch from b73e83b to c0f406a Compare March 5, 2026 14:28
@zstack-robot-1
Copy link
Copy Markdown
Collaborator

Comment from ye.zou:

Code Review: zmigrate thirdPartySoftwarePackage (zstack !9141)

关联 MR: premium !12903 + zstack-utility !6606
总体评价: CONDITIONAL — 主要是输入校验和兼容性问题


必须修 (P0/P1)

1. [major:security] CephBackupStorageBase.java:203-216 — URL scheme 无白名单

handle(UploadFileToBackupStorageHostMsg) 解析 URL scheme 后只检查 null/empty,不做白名单过滤。file://data:// 等 scheme 可能触发 agent 端 SSRF。

修法: 加白名单校验

if (!Arrays.asList("http", "https", "ftp", "sftp", "upload").contains(scheme)) {
    reply.setError(operr("unsupported URL scheme: %s", scheme));
    bus.reply(msg, reply);
    return;
}

2. [major:security] CephBackupStorageBase.java:241 + SoftwareUpgradePackageDeployMsg — 路径遍历校验缺失

UnzipFileOnBackupStorageHostMsg.installPathSoftwareUpgradePackageDeployMsgupgradePackagePath/upgradePackageTargetPath/upgradeScriptPath 均无路径遍历校验。攻击者可能利用 ../ 解压或 SCP 到任意位置。

修法: Java 端校验 — Path.normalize() + 禁止 .. + 白名单目录前缀

3. [major:compatibility] KVMHost.java + KVMAgentCommandsdirectUploadPathdirectUploadUrl 破坏性重命名

字段从 directUploadPath 改为 directUploadUrl。混合版本部署时(新 Java + 旧 agent)JSON 反序列化会丢失该字段。

需确认: 如果 feature 分支内闭环(Java 和 agent 同时发版)则可接受;否则需加 @JsonAlias("directUploadPath") 兼容。


建议修 (P2)

# File 问题 建议
4 SoftwareUpgradePackageDeployMsg targetHostSshPort 只校验 > 0,未校验 <= 65535 加范围校验
5 CephBackupStorageBase.java:162 url != null 不检查空字符串 url == null || url.isEmpty()
6 CephBackupStorageBase.java:174 timeoutManager.getTimeout() 未确认是否有对应 GlobalConfig 检查是否需要专用 timeout 配置

架构层面

  • BackupStorageBase 新增 6 个 handle() 方法默认 dealWithUnknownMessage,由 CephBackupStorageBase 覆盖实现 — 标准 ZStack 模板方法模式,OK
  • CephBackupStorageBase 新增 300+ 行处理 6 种消息类型,体量增大。后续可考虑策略模式拆分,但当前可接受
  • Header 消息定义清晰,Msg/Reply 配对完整

Review by AI Code Reviewer — 3 parallel agents (correctness/security/architecture)

@zstack-robot-2
Copy link
Copy Markdown
Collaborator Author

Comment from tao.gan:

问题1

数据面对urlScheme 做了判断 if self.urlScheme is None or self.urlScheme not in self.STRATEGY_MAP:

问题2
CephBackupStorageBase.java:241 + SoftwareUpgradePackageDeployMsg
各类 path 在数据面都做了 判断类似判断 linux.validate_install_path

问题3
该字段 数据面和控制面均一起修改

问题4 数据面校验了 port

问题5 已修改StringUtils.isEmpty(msg.getUrl())

问题6 使用的是 ApiTimeoutManagerImpl.getTimeout() 计算出来的超时时间

@MatheMatrix MatheMatrix deleted the sync/tao.gan/zmigrate-ZSV-1000@@3 branch March 6, 2026 02:42
@MatheMatrix
Copy link
Copy Markdown
Owner

Comment from ye.zou:

Code Review: thirdPartySoftwarePackage (zstack !9141 + premium !12903 + utility !6606)

Verdict: CONDITIONAL — 有几个关键问题需修复,修复后可合入


Critical (必须修复)

1. [critical:security] CephBackupStorageBase.java — URL scheme 无白名单 (SSRF)

handle(UploadFileToBackupStorageHostMsg) 只检查 scheme 是否为 null,但不限制允许的协议。file://gopher://dict:// 等危险协议全部放行,可导致 SSRF 攻击。

添加 scheme 白名单校验,仅允许 http/https/ftp

2. [critical:security] CephBackupStorageBase.java — SSH 参数校验不完整

SoftwareUpgradePackageDeployMsgtargetHostSshPort 只检查 <= 0,未检查 > 65535targetHostIptargetHostSshUsername 完全无校验,可能包含注入字符。

添加 IP 格式校验、用户名正则、端口范围 1-65535。可参考 utility 侧 ssh_validation.py 的实现

3. [critical:compatibility] KVMAgentCommands.java — directUploadPath → directUploadUrl 重命名

API 字段重命名是破坏性变更。需确认所有调用方(agent、SDK、旧版本客户端)已同步更新。若有旧版本 agent 混部风险,建议保留 legacy 别名。


Major (强烈建议修复)

4. [major:security] CephBackupStorageBase.java — installPath 未校验

UploadFileToBackupStorageHostMsginstallPath 直接传到 Ceph agent 执行文件操作,无路径穿越校验。

确保 SoftwarePackageApiInterceptor.validatePath() 已实现并覆盖:绝对路径、../ 穿越、shell 元字符、黑名单目录

5. [major:credential] CephBackupStorageBase.java — SSH 密码 HTTP 明文传输

SoftwareUpgradePackageCmd 包含 Base64 编码的 SSH 密码,通过 HTTP 传到 Ceph agent。@NoLogging 已防日志泄漏,但管理网明文传输仍有嗅探风险。

建议密码操作下沉到 agent 侧或使用 HTTPS transport


Nit

  • UploadFileResponse.directUploadUrl 含义需注释说明:是 URL 还是 file path?

整体评价:安全意识不错(utility 侧的 ssh_validation.pyvalidate_install_path 等加固做得好),但 Java 侧(CephBackupStorageBase、KVMHost)的校验没有对齐到同样的水平。建议把 utility 侧的安全模式在 Java 侧也补齐。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants