<feature>[vm]: add MetadataImpact#3663
<feature>[vm]: add MetadataImpact#3663zstack-robot-2 wants to merge 1 commit intofeature-zsv-5.0.0-vm-support-vtpm-and-secucebootfrom
Conversation
APIImpact Resolves: ZSV-11559 Change-Id: I6b6a6378627264646d6a76726762736e77787373
概览该变更引入了完整的虚拟机实例元数据管理系统,包括在主存储后端上更新、检索、扫描和清理元数据的功能,涵盖数据库模式、API端点、垃圾回收机制和存储插件集成。 变更清单
序列图sequenceDiagram
participant Client
participant API as API Handler<br/>(PrimaryStorageBase)
participant PS as Primary Storage<br/>(LocalStorage/NFS)
participant Host as KVM Host<br/>Backend
participant DB as Database
Client->>API: APIScanVmInstanceMetadataFromPrimaryStorageMsg
activate API
API->>PS: ScanVmInstanceMetadataFromPrimaryStorageMsg
deactivate API
activate PS
PS->>Host: HTTP POST /metadata/scan
deactivate PS
activate Host
Host->>DB: Query local VM metadata files
Host-->>PS: ScanVmMetadataRsp + VmMetadataScanEntry[]
deactivate Host
activate PS
PS-->>API: ScanVmInstanceMetadataFromPrimaryStorageReply
deactivate PS
activate API
API->>API: Filter entries<br/>(remove nulls, templates)
API-->>Client: APIScanVmInstanceMetadataFromPrimaryStorageEvent
deactivate API
sequenceDiagram
participant VM as VM Expunge
participant Flow as VmExpungeMetadata<br/>Flow
participant GC as Cleanup GC
participant PS as Primary Storage
participant Host as Host Backend
VM->>Flow: run(vmInstanceSpec)
activate Flow
alt Metadata cleanup needed
Flow->>PS: CleanupVmInstanceMetadataOnPrimaryStorageMsg
activate PS
PS->>Host: HTTP POST /metadata/cleanup
deactivate PS
Host-->>Flow: Success Reply
Flow->>Flow: Log success
else Cleanup failed
Flow->>Flow: Create cleanup GC job
Flow->>GC: Schedule retry with interval
GC->>GC: Wait for interval
GC->>PS: Retry cleanup
end
deactivate Flow
代码审查工作量估算🎯 4 (复杂) | ⏱️ ~75 分钟 诗歌
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
Actionable comments posted: 13
🧹 Nitpick comments (16)
header/src/main/java/org/zstack/header/vm/metadata/VmUuidFromApiResolver.java (1)
10-13: 可将result初始化延后到空判之后。当前在
fieldValues为空时会产生一次不必要的ArrayList分配;这是小优化,可后续顺手调整。♻️ 建议修改
default List<String> batchResolveVmUuids(List<String> fieldValues) { - List<String> result = new ArrayList<>(); if (fieldValues == null || fieldValues.isEmpty()) { - return result; + return new ArrayList<>(); } + List<String> result = new ArrayList<>(fieldValues.size()); for (String v : fieldValues) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@header/src/main/java/org/zstack/header/vm/metadata/VmUuidFromApiResolver.java` around lines 10 - 13, In VmUuidFromApiResolver, avoid allocating the ArrayList when fieldValues is null or empty by moving the List<String> result = new ArrayList<>(); declaration to after the null/empty check; specifically, in the method that currently creates result before "if (fieldValues == null || fieldValues.isEmpty()) { return result; }", change the flow so you first return an empty list (Collections.emptyList() or new ArrayList<>()) on the null/empty check and only allocate the mutable result list when you know fieldValues contains elements that must be processed.sdk/src/main/java/org/zstack/sdk/RegisterVmInstanceFromMetadataResult.java (1)
3-3: 冗余的同包导入。
VmInstanceInventory与RegisterVmInstanceFromMetadataResult位于同一包org.zstack.sdk中,无需显式导入。🔧 建议移除冗余导入
package org.zstack.sdk; -import org.zstack.sdk.VmInstanceInventory; - public class RegisterVmInstanceFromMetadataResult {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/src/main/java/org/zstack/sdk/RegisterVmInstanceFromMetadataResult.java` at line 3, The import of VmInstanceInventory is redundant because VmInstanceInventory and the class RegisterVmInstanceFromMetadataResult are in the same package (org.zstack.sdk); remove the line importing VmInstanceInventory from RegisterVmInstanceFromMetadataResult so the class relies on the package scope instead of an unnecessary explicit import.header/src/main/java/org/zstack/header/vm/metadata/MetadataImpact.java (1)
11-27: “Required”语义与默认值存在冲突。Line 13、Line 19、Line 25 注释写“Required”,但
resolver、field、updateOnFailure都有默认值,调用方遗漏配置时会静默生效为默认值。建议二选一:
- 在注解消费端对
Impact != NONE做强校验;- 将注释改为“可选且有默认行为”,避免误导。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@header/src/main/java/org/zstack/header/vm/metadata/MetadataImpact.java` around lines 11 - 27, The Javadoc says resolver, field, and updateOnFailure are "Required" but they have defaults; update the annotation consumer to enforce the requirement when Impact != NONE (or specifically when impact is CONFIG or STORAGE): when processing MetadataImpact, validate that resolver() and field() are non-empty (and explicitly handle updateOnFailure if your logic needs an explicit value) and throw a clear configuration/validation exception if missing; alternatively, if you prefer keeping silent defaults, update the Javadoc on resolver(), field(), and updateOnFailure() to state they are optional and describe the default behavior instead of saying "Required".header/src/main/java/org/zstack/header/vm/metadata/VmMetadataResourcePersistExtensionPoint.java (1)
6-15: 建议为扩展点接口方法补充 Javadoc 说明。该接口是跨模块扩展契约,当前方法缺少注释会降低实现方对参数语义(如
resourceType、now、回滚语义)的理解一致性。As per coding guidelines: “接口方法不应有多余的修饰符(例如 public),且必须配有有效的 Javadoc 注释。”
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@header/src/main/java/org/zstack/header/vm/metadata/VmMetadataResourcePersistExtensionPoint.java` around lines 6 - 15, Add Javadoc to the VmMetadataResourcePersistExtensionPoint interface and to each method (getPrimaryStorageType, afterVolumePersist, afterSnapshotPersist, afterRegistrationRollback) describing purpose, parameter semantics (explain primaryStorageUuid, resourceUuid, resourceType meaning, hostUuid, size units, and what Timestamp now represents), and the rollback semantics for afterRegistrationRollback (what callers should expect when it's invoked and whether implementors must be idempotent). Also ensure no redundant access modifiers are used on the interface methods (they should remain package-private syntax-wise as in interface defaults) and keep the Javadoc concise and consistent with project coding guidelines.sdk/src/main/java/org/zstack/sdk/ScanVmInstanceMetadataFromPrimaryStorageResult.java (1)
5-12: 建议使用泛型参数化 List 类型。
java.util.List缺少泛型参数,建议使用java.util.List<VmMetadataScanEntry>以提升类型安全性。如果此类是自动生成的,可忽略此建议。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/src/main/java/org/zstack/sdk/ScanVmInstanceMetadataFromPrimaryStorageResult.java` around lines 5 - 12, The vmInstanceMetadata field and its accessor methods in ScanVmInstanceMetadataFromPrimaryStorageResult use a raw java.util.List; change the field declaration and the signatures of setVmInstanceMetadata and getVmInstanceMetadata to use a parameterized type (e.g., java.util.List<VmMetadataScanEntry>) to restore generic type safety, updating the types in the field name vmInstanceMetadata and the methods setVmInstanceMetadata and getVmInstanceMetadata accordingly; if VmMetadataScanEntry is in a different package, import or fully qualify it.header/src/main/java/org/zstack/header/storage/primary/VmMetadataScanEntryDoc_zh_cn.groovy (1)
7-66: 字段描述为空,建议补充说明。所有字段的
desc均为空字符串,缺少对各字段含义的描述。作为 API 文档,建议为每个字段添加有意义的中文描述,以帮助 API 使用者理解各字段的用途。例如:
vmUuid: "虚拟机UUID"vmName: "虚拟机名称"sizeBytes: "元数据文件大小(字节)"incomplete: "元数据是否不完整"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@header/src/main/java/org/zstack/header/storage/primary/VmMetadataScanEntryDoc_zh_cn.groovy` around lines 7 - 66, 所有 field 的 desc 目前为空,按 API 文档要求请为每个字段补充中文描述以说明含义;在 VmMetadataScanEntryDoc_zh_cn.groovy 中更新各 field 条目(比如 vmUuid, vmName, vmCategory, architecture, schemaVersion, metadataPath, hostUuid, sizeBytes, lastUpdateTime, incomplete)将 desc 从 "" 替换为简短中文说明(例如 vmUuid: "虚拟机 UUID", vmName: "虚拟机名称", sizeBytes: "元数据文件大小(字节)", incomplete: "元数据是否不完整" 等),保持风格一致并在必要时补充单位或取值说明以便文档清晰。header/src/main/java/org/zstack/header/vm/APIUpdateVmInstanceMetadataEventDoc_zh_cn.groovy (1)
5-23: 文档字段不完整,缺少实际响应字段。根据
APIUpdateVmInstanceMetadataEvent.java的定义,该事件应包含totalCleaned、totalFailed和failedVmUuids字段,但此文档仅记录了通用的success和error字段。建议补充缺失的业务字段文档,以保持 API 文档的完整性。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@header/src/main/java/org/zstack/header/vm/APIUpdateVmInstanceMetadataEventDoc_zh_cn.groovy` around lines 5 - 23, The doc is missing the business response fields declared in APIUpdateVmInstanceMetadataEvent; add field entries for totalCleaned (integer, 总共清理的条目数), totalFailed (integer, 清理失败的条目数) and failedVmUuids (list/string array, 清理失败的云主机UUID列表) in APIUpdateVmInstanceMetadataEventDoc_zh_cn.groovy alongside the existing success and error blocks so the generated API docs reflect the actual event properties; use the same "since" version as the file (5.0.0) and include appropriate types and descriptions matching the Java fields in APIUpdateVmInstanceMetadataEvent.header/src/main/java/org/zstack/header/vm/metadata/VmMetadataPathBuildExtensionPoint.java (1)
3-11: 建议为接口方法添加 Javadoc 注释。根据编码规范,接口方法应配有有效的 Javadoc 注释,以说明每个方法的用途、参数含义和返回值。例如:
validateMetadataPath的返回值语义(成功返回什么,失败返回什么)buildVmMetadataPath和buildMetadataDir的路径格式约定As per coding guidelines: "接口方法不应有多余的修饰符(例如 public),且必须配有有效的 Javadoc 注释"。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@header/src/main/java/org/zstack/header/vm/metadata/VmMetadataPathBuildExtensionPoint.java` around lines 3 - 11, 为 VmMetadataPathBuildExtensionPoint 接口的每个方法添加 Javadoc 注释:对 getPrimaryStorageType、buildVmMetadataPath、buildMetadataDir、validateMetadataPath 和 requireHostForCleanup 逐个说明用途、参数含义(例如 primaryStorageUuid、vmInstanceUuid)、返回值语义(明确 validateMetadataPath 在成功/失败时返回什么——例如 null/空串或错误描述)、以及路径格式约定和任何约束(如是否包含前缀/后缀、分隔符规则);保持注释简洁明确以便实现者和调用方理解各方法契约。compute/src/main/java/org/zstack/compute/vm/VmExpungeMetadataFlow.java (2)
56-56: 多余的空行第56行有一个多余的空行。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@compute/src/main/java/org/zstack/compute/vm/VmExpungeMetadataFlow.java` at line 56, VmExpungeMetadataFlow.java 中第56行存在多余空行,删除该空行以保持代码格式一致;定位到类 VmExpungeMetadataFlow(或相关方法体)并移除该多余的空白行即可,不影响逻辑或其他符号。
102-103: 冗余的final变量声明
finalPsUuid和finalHostUuid可以简化。由于psUuid在第102行之后不再被修改,而hostUuid虽然在第84行被重新赋值,但在第101行之后也不再改变,这两个变量可以用更简洁的方式处理。♻️ 建议的简化方式
可以将
hostUuid的处理逻辑提前,使其成为effectively final,或直接在lambda中使用:- final String finalPsUuid = psUuid; - final String finalHostUuid = hostUuid; - bus.makeTargetServiceIdByResourceUuid(cmsg, PrimaryStorageConstant.SERVICE_ID, psUuid); + final String msgPsUuid = psUuid; + final String msgHostUuid = hostUuid; bus.send(cmsg, new CloudBusCallBack(trigger) { `@Override` public void run(MessageReply reply) { if (reply.isSuccess()) { logger.info(String.format("[MetadataExpunge] successfully deleted metadata for vm[uuid:%s] on ps[uuid:%s]", - vmUuid, finalPsUuid)); + vmUuid, msgPsUuid)); } else { logger.warn(String.format("[MetadataExpunge] failed to delete metadata for vm[uuid:%s] on ps[uuid:%s]: %s, " + - "submitting GC job for retry", vmUuid, finalPsUuid, reply.getError())); - submitGC(finalPsUuid, vmUuid, rootVolumeUuid, metadataPath, finalHostUuid); + "submitting GC job for retry", vmUuid, msgPsUuid, reply.getError())); + submitGC(msgPsUuid, vmUuid, rootVolumeUuid, metadataPath, msgHostUuid); } trigger.next(); } });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@compute/src/main/java/org/zstack/compute/vm/VmExpungeMetadataFlow.java` around lines 102 - 103, 在 VmExpungeMetadataFlow 中消除冗余的 final 变量:不要创建 finalPsUuid 和 finalHostUuid 两个中间变量,改为直接在 lambda/匿名类中使用已是 effectively final 的 psUuid 或将 hostUuid 的赋值逻辑提前使其成为 effectively final;如果必须显式固定值,保留单个局部 final 变量(例如 finalPsUuid 或 finalHostUuid 之一)并移除另一个,确保引用的变量在创建 lambda 之后不再被修改(参考符号:psUuid、hostUuid、finalPsUuid、finalHostUuid)。compute/src/main/java/org/zstack/compute/vm/CleanupVmInstanceMetadataOnPrimaryStorageGC.java (2)
78-80: 修改@GC字段可能导致问题在第80行,
hostUuid被设置为null。由于hostUuid是用@GC注解标记的字段,这个修改可能会被持久化。如果GC作业在本次触发后失败并被重新调度,下次触发时hostUuid将为null,即使原始提交时有有效值。建议使用局部变量来避免修改持久化字段:
♻️ 建议的修复
+ String effectiveHostUuid = hostUuid; if (hostUuid != null && !dbf.isExist(hostUuid, HostVO.class)) { if (requireHost) { logger.debug(String.format("[MetadataCleanupGC] host[uuid:%s] no longer exists " + "and ps[uuid:%s, type:%s] requires host for cleanup, cancel gc for vm[uuid:%s]", hostUuid, primaryStorageUuid, psType, vmUuid)); completion.cancel(); return; } logger.info(String.format("[MetadataCleanupGC] host[uuid:%s] no longer exists for vm[uuid:%s], " + "clear hostUuid and let the primary storage backend pick an available host", hostUuid, vmUuid)); - hostUuid = null; + effectiveHostUuid = null; } CleanupVmInstanceMetadataOnPrimaryStorageMsg msg = new CleanupVmInstanceMetadataOnPrimaryStorageMsg(); msg.setPrimaryStorageUuid(primaryStorageUuid); msg.setVmInstanceUuid(vmUuid); msg.setRootVolumeUuid(rootVolumeUuid); msg.setMetadataPath(metadataPath); - msg.setHostUuid(hostUuid); + msg.setHostUuid(effectiveHostUuid);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@compute/src/main/java/org/zstack/compute/vm/CleanupVmInstanceMetadataOnPrimaryStorageGC.java` around lines 78 - 80, The code in CleanupVmInstanceMetadataOnPrimaryStorageGC sets the `@GC-annotated` field hostUuid to null which may persist that change; instead, stop mutating the field and use a local variable for the temporary "no longer exists" state and subsequent logic. Locate the hostUuid usage in the class (field hostUuid and the block that logs and sets hostUuid = null), introduce a local variable (e.g., localHostUuid) to hold the nullable value for this GC run, leave the hostUuid field untouched, and update any subsequent references in this method to use the local variable so no persistent `@GC` field is modified.
50-50: 使用PrimaryStorageAO_而非PrimaryStorageVO_第50行使用
PrimaryStorageAO_进行查询选择和条件判断。虽然这可能是有意为之(AO可能是VO的父类),但与第43行使用PrimaryStorageVO.class检查存在性不一致。建议统一使用PrimaryStorageVO_。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@compute/src/main/java/org/zstack/compute/vm/CleanupVmInstanceMetadataOnPrimaryStorageGC.java` at line 50, The query at String psType = Q.New(PrimaryStorageVO.class)... is using PrimaryStorageAO_ for the generated metamodel but the existence check above uses PrimaryStorageVO.class, causing inconsistency; change the metamodel references to PrimaryStorageVO_ so the select and eq use PrimaryStorageVO_.type and PrimaryStorageVO_.uuid (i.e., update the Q.New(...).select(...) and .eq(...) calls to reference PrimaryStorageVO_ instead of PrimaryStorageAO_) to keep the type/VO usage consistent with the earlier check in CleanupVmInstanceMetadataOnPrimaryStorageGC.plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorageBackend.java (1)
96-104: 接口方法签名与现有模式不一致新增的handle方法签名为
(msg, hostUuid, completion),而现有的handle方法(第25-55行)签名为(PrimaryStorageInventory inv, msg, completion)。这种不一致可能会造成维护上的困惑。如果这是有意为之的设计(例如metadata操作不需要inventory参数),建议在接口中添加注释说明原因。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorageBackend.java` around lines 96 - 104, The new handle method signatures in NfsPrimaryStorageBackend (handle(UpdateVmInstanceMetadataOnPrimaryStorageMsg...), handle(GetVmInstanceMetadataFromPrimaryStorageMsg...), handle(ScanVmInstanceMetadataFromPrimaryStorageMsg...), handle(CleanupVmInstanceMetadataOnPrimaryStorageMsg...), handle(RebaseVolumeBackingFileOnPrimaryStorageMsg...)) are inconsistent with the existing handle pattern that accepts (PrimaryStorageInventory inv, msg, completion); update the interface so these metadata-related handles either follow the same signature order and include PrimaryStorageInventory as the first parameter, or if the omission of PrimaryStorageInventory is intentional, add a clear Javadoc comment on each of the five methods explaining why we use (msg, hostUuid, completion) and document the design rationale; ensure the change is applied to the method declarations in NfsPrimaryStorageBackend and any implementing classes to keep the API consistent.plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorage.java (1)
1994-1998: 中间变量可以简化第1995行声明
finalHostUuid,然后在第1996行同时检查hostUuid是否为null。这个模式可以简化。♻️ 建议的简化
String hostUuid = msg.getHostUuid(); - String finalHostUuid = hostUuid; - if (hostUuid == null || !connectedHosts.stream().anyMatch(h -> h.getUuid().equals(finalHostUuid))) { + if (hostUuid == null || connectedHosts.stream().noneMatch(h -> h.getUuid().equals(hostUuid))) { hostUuid = connectedHosts.get(0).getUuid(); }同样的模式在第2048-2051行也存在。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorage.java` around lines 1994 - 1998, Remove the unnecessary intermediate variable finalHostUuid by introducing a single final/local capture for the lambda and using it in the containment check; e.g. create a final String candidate = hostUuid (or final String host = hostUuid) and use connectedHosts.stream().anyMatch(h -> h.getUuid().equals(candidate)) in the if condition, then assign hostUuid = connectedHosts.get(0).getUuid() when needed; apply the same simplification to the identical pattern around the code that currently lives at lines ~2048-2051 so both checks use a single captured local instead of two variables.plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageVmMetadataExtension.java (1)
38-54:buildMetadataDir()和buildVmMetadataPath()存在代码重复。两个方法都执行相同的 URL 查询和空值检查。可以考虑提取一个私有方法来获取和验证 URL。
♻️ 可选的重构建议
+ private String getPrimaryStorageUrl(String primaryStorageUuid) { + String url = Q.New(PrimaryStorageVO.class).select(PrimaryStorageVO_.url).eq(PrimaryStorageVO_.uuid, primaryStorageUuid).findValue(); + if (url == null) { + throw new CloudRuntimeException(String.format("cannot find url for primary storage[uuid:%s]", primaryStorageUuid)); + } + return normalizeBaseDir(url); + } + `@Override` public String buildMetadataDir(String primaryStorageUuid) { - String url = Q.New(PrimaryStorageVO.class).select(PrimaryStorageVO_.url).eq(PrimaryStorageVO_.uuid, primaryStorageUuid).findValue(); - if (url == null) { - throw new CloudRuntimeException(String.format("cannot find url for primary storage[uuid:%s]", primaryStorageUuid)); - } - return String.format("%s/%s", normalizeBaseDir(url), VmInstanceMetadataConstants.METADATA_DIR_NAME); + return String.format("%s/%s", getPrimaryStorageUrl(primaryStorageUuid), VmInstanceMetadataConstants.METADATA_DIR_NAME); } `@Override` public String buildVmMetadataPath(String primaryStorageUuid, String vmInstanceUuid) { - String url = Q.New(PrimaryStorageVO.class).select(PrimaryStorageVO_.url).eq(PrimaryStorageVO_.uuid, primaryStorageUuid).findValue(); - if (url == null) { - throw new CloudRuntimeException(String.format("cannot find url for primary storage[uuid:%s]", primaryStorageUuid)); - } - return String.format("%s/%s/%s%s", normalizeBaseDir(url), VmInstanceMetadataConstants.METADATA_DIR_NAME, vmInstanceUuid, VmInstanceMetadataConstants.FILE_METADATA_SUFFIX); + return String.format("%s/%s/%s%s", getPrimaryStorageUrl(primaryStorageUuid), VmInstanceMetadataConstants.METADATA_DIR_NAME, vmInstanceUuid, VmInstanceMetadataConstants.FILE_METADATA_SUFFIX); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageVmMetadataExtension.java` around lines 38 - 54, Both buildMetadataDir and buildVmMetadataPath repeat the same PrimaryStorageVO URL query and null check; extract a private helper (e.g., getPrimaryStorageUrl(String primaryStorageUuid)) that performs Q.New(PrimaryStorageVO.class).select(PrimaryStorageVO_.url).eq(...).findValue() and throws the CloudRuntimeException if null, then have buildMetadataDir and buildVmMetadataPath call this helper and reuse normalizeBaseDir and VmInstanceMetadataConstants to build their return values.plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageKvmBackend.java (1)
918-962: 新增的 DTO 类结构正确,遵循了现有模式。这些命令和响应类与文件中的现有模式一致。有一个小的风格建议:
PrefixRebaseBackingFilesCmd和PrefixRebaseBackingFilesRsp使用了完全限定的父类名(LocalStorageKvmBackend.AgentCommand/LocalStorageKvmBackend.AgentResponse),但由于它们已经在LocalStorageKvmBackend类内部定义,可以直接使用简短的类名AgentCommand/AgentResponse,与其他嵌套类保持一致。♻️ 可选的风格改进
-public static class PrefixRebaseBackingFilesCmd extends LocalStorageKvmBackend.AgentCommand { +public static class PrefixRebaseBackingFilesCmd extends AgentCommand { public List<String> filePaths; public String oldPrefix; public String newPrefix; } -public static class PrefixRebaseBackingFilesRsp extends LocalStorageKvmBackend.AgentResponse { +public static class PrefixRebaseBackingFilesRsp extends AgentResponse { public int rebasedCount; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageKvmBackend.java` around lines 918 - 962, The two nested classes PrefixRebaseBackingFilesCmd and PrefixRebaseBackingFilesRsp use fully qualified parent types LocalStorageKvmBackend.AgentCommand and LocalStorageKvmBackend.AgentResponse; change their declarations to extend the inner types directly (AgentCommand and AgentResponse) to match the style of the other DTOs in LocalStorageKvmBackend (e.g., use "class PrefixRebaseBackingFilesCmd extends AgentCommand" and "class PrefixRebaseBackingFilesRsp extends AgentResponse"), keeping all fields and behavior unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@compute/src/main/java/org/zstack/compute/vm/VmInstanceApiInterceptor.java`:
- Around line 1360-1362: The current call throws ApiMessageInterceptionException
using argerr(error) which treats the dynamic error string as a format pattern;
change the throw site (the block that calls ext.validateMetadataPath(psUuid,
path) and throws new ApiMessageInterceptionException(...)) to pass the message
as an argument, e.g. use argerr("%s", error) so the returned error is safely
formatted as data rather than a format string; keep the rest of the logic
(checking error != null and throwing ApiMessageInterceptionException) unchanged.
In `@header/src/main/java/org/zstack/header/vm/APIChangeVmNicStateMsg.java`:
- Line 12: Implement a Spring bean named "NicUuidToVmUuidResolver" that
implements the VmUuidFromApiResolver interface and resolves a NIC UUID to its VM
UUID; create a class (e.g., NicUuidToVmUuidResolver) implementing
VmUuidFromApiResolver, inject the appropriate NIC/VM lookup service or DAO used
elsewhere in the project, implement the resolver method to fetch the VM UUID by
the given nicUuid (handling not-found and errors consistently with other
resolvers), and register it as a Spring bean with the id
"NicUuidToVmUuidResolver" so that the
`@MetadataImpact`(resolver="NicUuidToVmUuidResolver") usages in
APIChangeVmNicStateMsg, APIDetachL3NetworkFromVmMsg, APIUpdateVmNicDriverMsg and
APIChangeVmNicNetworkMsg resolve correctly.
In
`@header/src/main/java/org/zstack/header/vm/APIGetVmInstanceMetadataFromPrimaryStorageMsgDoc_zh_cn.groovy`:
- Line 20: 在 APIGetVmInstanceMetadataFromPrimaryStorageMsgDoc_zh_cn.groovy 中的
desc """""" 为空,会在生成的 API 文档中留下空白说明;请用一条最小语义的中文描述替换该空字符串,例如说明此 API
获取虚拟机在主存储上的元数据(或类似简短句子),以便在类/文件
APIGetVmInstanceMetadataFromPrimaryStorageMsgDoc_zh_cn.groovy 中的 desc
字段有实际内容并避免空说明。
In
`@header/src/main/java/org/zstack/header/vm/metadata/VmMetadataPathReplacementExtensionPoint.java`:
- Around line 8-10: Add Javadoc for the interface methods
getPrimaryStorageType() and calculatePathReplacements(String targetPsUuid,
List<String> allOldPaths) describing their purpose, expected inputs and outputs,
null/empty handling, exception behavior and any concurrency or lifecycle
constraints; also document the nested PathReplacementResult class fields and
meaning (e.g., mapping semantics, order guarantees). Ensure the Javadoc follows
project conventions (brief summary + `@param/`@return/@throws where applicable)
and do not add extra modifiers to the interface methods.
In
`@header/src/main/java/org/zstack/header/vm/metadata/VmUuidFromApiResolver.java`:
- Around line 7-24: 为接口 VmUuidFromApiResolver 的两个方法 resolveVmUuid 和
batchResolveVmUuids 添加完整的 Javadoc:在 resolveVmUuid 上说明方法用途(将 API 字段值解析为 VM
UUID)、参数说明 (`@param` fieldValue 允许为 null 或空时的行为)、返回值说明 (`@return` 返回解析出的 VM UUID 或
null);在 batchResolveVmUuids 上说明批量解析行为、对 null 或空集合以及单个 null 元素的处理规则、返回值描述(返回解析成功的
VM UUID 列表,保持输入顺序或说明不会包含 nulls);遵守接口规范不要为接口方法添加多余修饰符(如 public)。
In
`@plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageBase.java`:
- Around line 3371-3377: The cleanup and update branches use different sync keys
causing races; make both branches return the same VM-scoped sync signature so
cleanup and update serialize on the same key. Locate the getSyncSignature
implementations (the one returning "update-metadata-on-ps-%s" and the other
returning "update-metadata-on-ps-%s-%s") and change them to use a single helper
(e.g., buildVmSyncSignature(vmUuid)) that produces a signature based only on the
VM UUID (for example "update-metadata-on-ps-{vmUuid}"), and ensure both
getSyncLevel implementations remain consistent so cleanup and update acquire the
same lock.
- Around line 910-911: 当前分支对 RebaseVolumeBackingFileOnPrimaryStorageMsg 直接调用本类的
handle((RebaseVolumeBackingFileOnPrimaryStorageMsg) msg),绕过了父类
PrimaryStorageBase.handleLocalMessage() 中的 checkPrimaryStatus(msg) 校验,导致
maintenance 状态检查失效;请将该分支改为交由 super.handleLocalMessage(msg) 处理(即不要直接调用本类 handle
方法),让父类先执行 checkPrimaryStatus(msg) 再按多态分发到本类的
handle(RebaseVolumeBackingFileOnPrimaryStorageMsg)。
In
`@plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageSimulator.java`:
- Around line 375-383: The prefixRebaseBackingFiles endpoint does not record
incoming commands for tests; add a List<PrefixRebaseBackingFilesCmd> field named
prefixRebaseBackingFilesCmds to LocalStorageSimulatorConfig and update the
LocalStorageSimulator.prefixRebaseBackingFiles method to append the parsed
PrefixRebaseBackingFilesCmd (cmd) to config.prefixRebaseBackingFilesCmds before
building/replying the PrefixRebaseBackingFilesRsp so tests can inspect the
received commands. Ensure imports and field visibility match other config lists
(e.g., writeVmMetadataCmds) and follow the same null-safety/initialization
pattern.
In
`@plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageVmMetadataExtension.java`:
- Around line 151-168: The createResourceRef method in
LocalStorageVmMetadataExtension has a TOCTOU race: the exists check
(Q.New(...).isExists()) can race and dbf.persist(ref) may throw a
unique-constraint/duplicate-key error; replace the check-with-insert pattern
with a safe upsert or wrap dbf.persist(ref) in a try/catch to handle constraint
violations gracefully (detect the duplicate-key/constraint exception thrown by
the ORM/DB and treat it as a no-op), or use a DB-level upsert function if
available so concurrent callers won't fail; update createResourceRef to catch
the specific persistence exception and log/debug that the ref already exists
instead of letting the error propagate.
In
`@plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorageKVMBackend.java`:
- Around line 136-141: NfsPrimaryStorageKVMBackend adds five new request paths
(WRITE_VM_METADATA_PATH, GET_VM_INSTANCE_METADATA_PATH, SCAN_VM_METADATA_PATH,
CLEANUP_VM_METADATA_PATH, NFS_PREFIX_REBASE_BACKING_FILES_PATH) but the KVM
agent has no registered handlers for them; add corresponding handler
registrations in the KVM agent (plugin/kvm) following the pattern used by
LocalStorageSimulator/LocalStorageKvmBackend: implement request processors for
each path that deserialize the incoming request, perform the same semantics
expected by NfsPrimaryStorageKVMBackend, and register them with the agent’s HTTP
dispatcher so the agent will not return 404s for these endpoints.
In
`@resourceconfig/src/main/java/org/zstack/resourceconfig/APIDeleteResourceConfigMsg.java`:
- Line 8: The project is missing an implementation of
ResourceUuidToVmUuidResolver referenced by the `@MetadataImpact` on
APIDeleteResourceConfigMsg (resourceUuid of type ResourceVO.class); implement a
Spring bean class named e.g. ResourceUuidToVmUuidResolverImpl that implements
ResourceUuidToVmUuidResolver, annotate it as a component/service, and implement
resolve(resourceUuid) to: 1) check if resourceUuid is a VM UUID (query
VmInstanceVO) and return it; 2) check known VM-related resources (VolumeVO ->
getVmInstanceUuid, VmNicVO -> getVmInstanceUuid, etc.) and return the associated
VM UUID when found; 3) return null for non-VM-related resources. Use existing
DAOs/VOs for lookups and keep the resolver registered so `@MetadataImpact` can
autowire it.
In `@sdk/src/main/java/org/zstack/sdk/CleanupVmInstanceMetadataAction.java`:
- Around line 28-29: The vmUuids field in CleanupVmInstanceMetadataAction
currently allows empty-string elements because its `@Param` uses emptyString =
true; change the annotation to emptyString = false on the public java.util.List
vmUuids field so SDK validation will reject [""] values (or, if this file is
generated, fix the API message parameter constraint for vmUuids to set
emptyString=false and re-generate the SDK so the generated
CleanupVmInstanceMetadataAction has emptyString=false).
In
`@sdk/src/main/java/org/zstack/sdk/GetVmInstanceMetadataFromPrimaryStorageAction.java`:
- Around line 28-29: The uuid field in
GetVmInstanceMetadataFromPrimaryStorageAction is marked required=true but still
allows empty strings (nonempty=false, emptyString=true), so update the `@Param` on
the uuid field to reject empty strings (e.g., set nonempty=true and
emptyString=false) so the SDK client-side validation blocks uuid=""; if this
file is generated, instead fix the source API message's `@APIParam` for uuid and
re-generate the SDK.
---
Nitpick comments:
In
`@compute/src/main/java/org/zstack/compute/vm/CleanupVmInstanceMetadataOnPrimaryStorageGC.java`:
- Around line 78-80: The code in CleanupVmInstanceMetadataOnPrimaryStorageGC
sets the `@GC-annotated` field hostUuid to null which may persist that change;
instead, stop mutating the field and use a local variable for the temporary "no
longer exists" state and subsequent logic. Locate the hostUuid usage in the
class (field hostUuid and the block that logs and sets hostUuid = null),
introduce a local variable (e.g., localHostUuid) to hold the nullable value for
this GC run, leave the hostUuid field untouched, and update any subsequent
references in this method to use the local variable so no persistent `@GC` field
is modified.
- Line 50: The query at String psType = Q.New(PrimaryStorageVO.class)... is
using PrimaryStorageAO_ for the generated metamodel but the existence check
above uses PrimaryStorageVO.class, causing inconsistency; change the metamodel
references to PrimaryStorageVO_ so the select and eq use PrimaryStorageVO_.type
and PrimaryStorageVO_.uuid (i.e., update the Q.New(...).select(...) and .eq(...)
calls to reference PrimaryStorageVO_ instead of PrimaryStorageAO_) to keep the
type/VO usage consistent with the earlier check in
CleanupVmInstanceMetadataOnPrimaryStorageGC.
In `@compute/src/main/java/org/zstack/compute/vm/VmExpungeMetadataFlow.java`:
- Line 56: VmExpungeMetadataFlow.java 中第56行存在多余空行,删除该空行以保持代码格式一致;定位到类
VmExpungeMetadataFlow(或相关方法体)并移除该多余的空白行即可,不影响逻辑或其他符号。
- Around line 102-103: 在 VmExpungeMetadataFlow 中消除冗余的 final 变量:不要创建 finalPsUuid
和 finalHostUuid 两个中间变量,改为直接在 lambda/匿名类中使用已是 effectively final 的 psUuid 或将
hostUuid 的赋值逻辑提前使其成为 effectively final;如果必须显式固定值,保留单个局部 final 变量(例如 finalPsUuid
或 finalHostUuid 之一)并移除另一个,确保引用的变量在创建 lambda
之后不再被修改(参考符号:psUuid、hostUuid、finalPsUuid、finalHostUuid)。
In
`@header/src/main/java/org/zstack/header/storage/primary/VmMetadataScanEntryDoc_zh_cn.groovy`:
- Around line 7-66: 所有 field 的 desc 目前为空,按 API 文档要求请为每个字段补充中文描述以说明含义;在
VmMetadataScanEntryDoc_zh_cn.groovy 中更新各 field 条目(比如 vmUuid, vmName, vmCategory,
architecture, schemaVersion, metadataPath, hostUuid, sizeBytes, lastUpdateTime,
incomplete)将 desc 从 "" 替换为简短中文说明(例如 vmUuid: "虚拟机 UUID", vmName: "虚拟机名称",
sizeBytes: "元数据文件大小(字节)", incomplete: "元数据是否不完整" 等),保持风格一致并在必要时补充单位或取值说明以便文档清晰。
In
`@header/src/main/java/org/zstack/header/vm/APIUpdateVmInstanceMetadataEventDoc_zh_cn.groovy`:
- Around line 5-23: The doc is missing the business response fields declared in
APIUpdateVmInstanceMetadataEvent; add field entries for totalCleaned (integer,
总共清理的条目数), totalFailed (integer, 清理失败的条目数) and failedVmUuids (list/string array,
清理失败的云主机UUID列表) in APIUpdateVmInstanceMetadataEventDoc_zh_cn.groovy alongside
the existing success and error blocks so the generated API docs reflect the
actual event properties; use the same "since" version as the file (5.0.0) and
include appropriate types and descriptions matching the Java fields in
APIUpdateVmInstanceMetadataEvent.
In `@header/src/main/java/org/zstack/header/vm/metadata/MetadataImpact.java`:
- Around line 11-27: The Javadoc says resolver, field, and updateOnFailure are
"Required" but they have defaults; update the annotation consumer to enforce the
requirement when Impact != NONE (or specifically when impact is CONFIG or
STORAGE): when processing MetadataImpact, validate that resolver() and field()
are non-empty (and explicitly handle updateOnFailure if your logic needs an
explicit value) and throw a clear configuration/validation exception if missing;
alternatively, if you prefer keeping silent defaults, update the Javadoc on
resolver(), field(), and updateOnFailure() to state they are optional and
describe the default behavior instead of saying "Required".
In
`@header/src/main/java/org/zstack/header/vm/metadata/VmMetadataPathBuildExtensionPoint.java`:
- Around line 3-11: 为 VmMetadataPathBuildExtensionPoint 接口的每个方法添加 Javadoc 注释:对
getPrimaryStorageType、buildVmMetadataPath、buildMetadataDir、validateMetadataPath
和 requireHostForCleanup 逐个说明用途、参数含义(例如
primaryStorageUuid、vmInstanceUuid)、返回值语义(明确 validateMetadataPath 在成功/失败时返回什么——例如
null/空串或错误描述)、以及路径格式约定和任何约束(如是否包含前缀/后缀、分隔符规则);保持注释简洁明确以便实现者和调用方理解各方法契约。
In
`@header/src/main/java/org/zstack/header/vm/metadata/VmMetadataResourcePersistExtensionPoint.java`:
- Around line 6-15: Add Javadoc to the VmMetadataResourcePersistExtensionPoint
interface and to each method (getPrimaryStorageType, afterVolumePersist,
afterSnapshotPersist, afterRegistrationRollback) describing purpose, parameter
semantics (explain primaryStorageUuid, resourceUuid, resourceType meaning,
hostUuid, size units, and what Timestamp now represents), and the rollback
semantics for afterRegistrationRollback (what callers should expect when it's
invoked and whether implementors must be idempotent). Also ensure no redundant
access modifiers are used on the interface methods (they should remain
package-private syntax-wise as in interface defaults) and keep the Javadoc
concise and consistent with project coding guidelines.
In
`@header/src/main/java/org/zstack/header/vm/metadata/VmUuidFromApiResolver.java`:
- Around line 10-13: In VmUuidFromApiResolver, avoid allocating the ArrayList
when fieldValues is null or empty by moving the List<String> result = new
ArrayList<>(); declaration to after the null/empty check; specifically, in the
method that currently creates result before "if (fieldValues == null ||
fieldValues.isEmpty()) { return result; }", change the flow so you first return
an empty list (Collections.emptyList() or new ArrayList<>()) on the null/empty
check and only allocate the mutable result list when you know fieldValues
contains elements that must be processed.
In
`@plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageKvmBackend.java`:
- Around line 918-962: The two nested classes PrefixRebaseBackingFilesCmd and
PrefixRebaseBackingFilesRsp use fully qualified parent types
LocalStorageKvmBackend.AgentCommand and LocalStorageKvmBackend.AgentResponse;
change their declarations to extend the inner types directly (AgentCommand and
AgentResponse) to match the style of the other DTOs in LocalStorageKvmBackend
(e.g., use "class PrefixRebaseBackingFilesCmd extends AgentCommand" and "class
PrefixRebaseBackingFilesRsp extends AgentResponse"), keeping all fields and
behavior unchanged.
In
`@plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageVmMetadataExtension.java`:
- Around line 38-54: Both buildMetadataDir and buildVmMetadataPath repeat the
same PrimaryStorageVO URL query and null check; extract a private helper (e.g.,
getPrimaryStorageUrl(String primaryStorageUuid)) that performs
Q.New(PrimaryStorageVO.class).select(PrimaryStorageVO_.url).eq(...).findValue()
and throws the CloudRuntimeException if null, then have buildMetadataDir and
buildVmMetadataPath call this helper and reuse normalizeBaseDir and
VmInstanceMetadataConstants to build their return values.
In
`@plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorage.java`:
- Around line 1994-1998: Remove the unnecessary intermediate variable
finalHostUuid by introducing a single final/local capture for the lambda and
using it in the containment check; e.g. create a final String candidate =
hostUuid (or final String host = hostUuid) and use
connectedHosts.stream().anyMatch(h -> h.getUuid().equals(candidate)) in the if
condition, then assign hostUuid = connectedHosts.get(0).getUuid() when needed;
apply the same simplification to the identical pattern around the code that
currently lives at lines ~2048-2051 so both checks use a single captured local
instead of two variables.
In
`@plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorageBackend.java`:
- Around line 96-104: The new handle method signatures in
NfsPrimaryStorageBackend
(handle(UpdateVmInstanceMetadataOnPrimaryStorageMsg...),
handle(GetVmInstanceMetadataFromPrimaryStorageMsg...),
handle(ScanVmInstanceMetadataFromPrimaryStorageMsg...),
handle(CleanupVmInstanceMetadataOnPrimaryStorageMsg...),
handle(RebaseVolumeBackingFileOnPrimaryStorageMsg...)) are inconsistent with the
existing handle pattern that accepts (PrimaryStorageInventory inv, msg,
completion); update the interface so these metadata-related handles either
follow the same signature order and include PrimaryStorageInventory as the first
parameter, or if the omission of PrimaryStorageInventory is intentional, add a
clear Javadoc comment on each of the five methods explaining why we use (msg,
hostUuid, completion) and document the design rationale; ensure the change is
applied to the method declarations in NfsPrimaryStorageBackend and any
implementing classes to keep the API consistent.
In `@sdk/src/main/java/org/zstack/sdk/RegisterVmInstanceFromMetadataResult.java`:
- Line 3: The import of VmInstanceInventory is redundant because
VmInstanceInventory and the class RegisterVmInstanceFromMetadataResult are in
the same package (org.zstack.sdk); remove the line importing VmInstanceInventory
from RegisterVmInstanceFromMetadataResult so the class relies on the package
scope instead of an unnecessary explicit import.
In
`@sdk/src/main/java/org/zstack/sdk/ScanVmInstanceMetadataFromPrimaryStorageResult.java`:
- Around line 5-12: The vmInstanceMetadata field and its accessor methods in
ScanVmInstanceMetadataFromPrimaryStorageResult use a raw java.util.List; change
the field declaration and the signatures of setVmInstanceMetadata and
getVmInstanceMetadata to use a parameterized type (e.g.,
java.util.List<VmMetadataScanEntry>) to restore generic type safety, updating
the types in the field name vmInstanceMetadata and the methods
setVmInstanceMetadata and getVmInstanceMetadata accordingly; if
VmMetadataScanEntry is in a different package, import or fully qualify it.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 3dbeaeef-fd7a-4620-9cce-ec13376c0727
⛔ Files ignored due to path filters (5)
conf/globalConfig/vm.xmlis excluded by!**/*.xmlconf/persistence.xmlis excluded by!**/*.xmlconf/serviceConfig/primaryStorage.xmlis excluded by!**/*.xmlconf/serviceConfig/vmInstance.xmlis excluded by!**/*.xmlconf/springConfigXml/VmInstanceManager.xmlis excluded by!**/*.xml
📒 Files selected for processing (145)
compute/src/main/java/org/zstack/compute/vm/CleanupVmInstanceMetadataOnPrimaryStorageGC.javacompute/src/main/java/org/zstack/compute/vm/VmExpungeMetadataFlow.javacompute/src/main/java/org/zstack/compute/vm/VmGlobalConfig.javacompute/src/main/java/org/zstack/compute/vm/VmInstanceApiInterceptor.javacompute/src/main/java/org/zstack/compute/vm/VmSystemTags.javaconf/db/zsv/V5.0.0__schema.sqlheader/src/main/java/org/zstack/header/storage/primary/APIScanVmInstanceMetadataFromPrimaryStorageEvent.javaheader/src/main/java/org/zstack/header/storage/primary/APIScanVmInstanceMetadataFromPrimaryStorageEventDoc_zh_cn.groovyheader/src/main/java/org/zstack/header/storage/primary/APIScanVmInstanceMetadataFromPrimaryStorageMsg.javaheader/src/main/java/org/zstack/header/storage/primary/APIScanVmInstanceMetadataFromPrimaryStorageMsgDoc_zh_cn.groovyheader/src/main/java/org/zstack/header/storage/primary/CleanupVmInstanceMetadataOnPrimaryStorageMsg.javaheader/src/main/java/org/zstack/header/storage/primary/CleanupVmInstanceMetadataOnPrimaryStorageReply.javaheader/src/main/java/org/zstack/header/storage/primary/GetVmInstanceMetadataFromPrimaryStorageMsg.javaheader/src/main/java/org/zstack/header/storage/primary/GetVmInstanceMetadataFromPrimaryStorageReply.javaheader/src/main/java/org/zstack/header/storage/primary/ReadVmInstanceMetadataMsg.javaheader/src/main/java/org/zstack/header/storage/primary/ReadVmInstanceMetadataReply.javaheader/src/main/java/org/zstack/header/storage/primary/RebaseVolumeBackingFileOnPrimaryStorageMsg.javaheader/src/main/java/org/zstack/header/storage/primary/RebaseVolumeBackingFileOnPrimaryStorageReply.javaheader/src/main/java/org/zstack/header/storage/primary/ScanVmInstanceMetadataFromPrimaryStorageMsg.javaheader/src/main/java/org/zstack/header/storage/primary/ScanVmInstanceMetadataFromPrimaryStorageReply.javaheader/src/main/java/org/zstack/header/storage/primary/VmMetadataScanEntry.javaheader/src/main/java/org/zstack/header/storage/primary/VmMetadataScanEntryDoc_zh_cn.groovyheader/src/main/java/org/zstack/header/storage/snapshot/APIDeleteVolumeSnapshotMsg.javaheader/src/main/java/org/zstack/header/storage/snapshot/APIRevertVolumeFromSnapshotMsg.javaheader/src/main/java/org/zstack/header/storage/snapshot/APIShrinkVolumeSnapshotMsg.javaheader/src/main/java/org/zstack/header/storage/snapshot/APIUpdateVolumeSnapshotMsg.javaheader/src/main/java/org/zstack/header/storage/snapshot/group/APIDeleteVolumeSnapshotGroupMsg.javaheader/src/main/java/org/zstack/header/storage/snapshot/group/APIRevertVmFromSnapshotGroupMsg.javaheader/src/main/java/org/zstack/header/storage/snapshot/group/APIUngroupVolumeSnapshotGroupMsg.javaheader/src/main/java/org/zstack/header/storage/snapshot/group/APIUpdateVolumeSnapshotGroupMsg.javaheader/src/main/java/org/zstack/header/tag/APICreateSystemTagMsg.javaheader/src/main/java/org/zstack/header/tag/APICreateSystemTagsMsg.javaheader/src/main/java/org/zstack/header/tag/APIDeleteTagMsg.javaheader/src/main/java/org/zstack/header/tag/APIUpdateSystemTagMsg.javaheader/src/main/java/org/zstack/header/vm/APIAttachIsoToVmInstanceMsg.javaheader/src/main/java/org/zstack/header/vm/APIAttachL3NetworkToVmMsg.javaheader/src/main/java/org/zstack/header/vm/APIAttachVmNicToVmMsg.javaheader/src/main/java/org/zstack/header/vm/APIChangeInstanceOfferingMsg.javaheader/src/main/java/org/zstack/header/vm/APIChangeVmNicNetworkMsg.javaheader/src/main/java/org/zstack/header/vm/APIChangeVmNicStateMsg.javaheader/src/main/java/org/zstack/header/vm/APICleanupVmInstanceMetadataEvent.javaheader/src/main/java/org/zstack/header/vm/APICleanupVmInstanceMetadataEventDoc_zh_cn.groovyheader/src/main/java/org/zstack/header/vm/APICleanupVmInstanceMetadataMsg.javaheader/src/main/java/org/zstack/header/vm/APICleanupVmInstanceMetadataMsgDoc_zh_cn.groovyheader/src/main/java/org/zstack/header/vm/APIConvertTemplatedVmInstanceToVmInstanceMsg.javaheader/src/main/java/org/zstack/header/vm/APIConvertVmInstanceToTemplatedVmInstanceMsg.javaheader/src/main/java/org/zstack/header/vm/APIDeleteVmBootModeMsg.javaheader/src/main/java/org/zstack/header/vm/APIDeleteVmConsolePasswordMsg.javaheader/src/main/java/org/zstack/header/vm/APIDeleteVmHostnameMsg.javaheader/src/main/java/org/zstack/header/vm/APIDeleteVmSshKeyMsg.javaheader/src/main/java/org/zstack/header/vm/APIDeleteVmStaticIpMsg.javaheader/src/main/java/org/zstack/header/vm/APIDestroyVmInstanceMsg.javaheader/src/main/java/org/zstack/header/vm/APIDetachIsoFromVmInstanceMsg.javaheader/src/main/java/org/zstack/header/vm/APIDetachL3NetworkFromVmMsg.javaheader/src/main/java/org/zstack/header/vm/APIGetVmInstanceMetadataFromPrimaryStorageEvent.javaheader/src/main/java/org/zstack/header/vm/APIGetVmInstanceMetadataFromPrimaryStorageEventDoc_zh_cn.groovyheader/src/main/java/org/zstack/header/vm/APIGetVmInstanceMetadataFromPrimaryStorageMsg.javaheader/src/main/java/org/zstack/header/vm/APIGetVmInstanceMetadataFromPrimaryStorageMsgDoc_zh_cn.groovyheader/src/main/java/org/zstack/header/vm/APIMigrateVmMsg.javaheader/src/main/java/org/zstack/header/vm/APIRecoverVmInstanceMsg.javaheader/src/main/java/org/zstack/header/vm/APIRegisterVmInstanceFromMetadataEvent.javaheader/src/main/java/org/zstack/header/vm/APIRegisterVmInstanceFromMetadataEventDoc_zh_cn.groovyheader/src/main/java/org/zstack/header/vm/APIRegisterVmInstanceFromMetadataMsg.javaheader/src/main/java/org/zstack/header/vm/APIRegisterVmInstanceFromMetadataMsgDoc_zh_cn.groovyheader/src/main/java/org/zstack/header/vm/APIReimageVmInstanceMsg.javaheader/src/main/java/org/zstack/header/vm/APISetVmBootModeMsg.javaheader/src/main/java/org/zstack/header/vm/APISetVmBootOrderMsg.javaheader/src/main/java/org/zstack/header/vm/APISetVmBootVolumeMsg.javaheader/src/main/java/org/zstack/header/vm/APISetVmClockTrackMsg.javaheader/src/main/java/org/zstack/header/vm/APISetVmConsolePasswordMsg.javaheader/src/main/java/org/zstack/header/vm/APISetVmHostnameMsg.javaheader/src/main/java/org/zstack/header/vm/APISetVmQxlMemoryMsg.javaheader/src/main/java/org/zstack/header/vm/APISetVmSoundTypeMsg.javaheader/src/main/java/org/zstack/header/vm/APISetVmSshKeyMsg.javaheader/src/main/java/org/zstack/header/vm/APISetVmStaticIpMsg.javaheader/src/main/java/org/zstack/header/vm/APIUpdateVmInstanceMetadataEvent.javaheader/src/main/java/org/zstack/header/vm/APIUpdateVmInstanceMetadataEventDoc_zh_cn.groovyheader/src/main/java/org/zstack/header/vm/APIUpdateVmInstanceMetadataMsg.javaheader/src/main/java/org/zstack/header/vm/APIUpdateVmInstanceMetadataMsgDoc_zh_cn.groovyheader/src/main/java/org/zstack/header/vm/APIUpdateVmInstanceMsg.javaheader/src/main/java/org/zstack/header/vm/APIUpdateVmNicDriverMsg.javaheader/src/main/java/org/zstack/header/vm/APIUpdateVmPriorityMsg.javaheader/src/main/java/org/zstack/header/vm/VmInstanceState.javaheader/src/main/java/org/zstack/header/vm/cdrom/APISetVmInstanceDefaultCdRomMsg.javaheader/src/main/java/org/zstack/header/vm/metadata/MetadataImpact.javaheader/src/main/java/org/zstack/header/vm/metadata/ResourceMetadata.javaheader/src/main/java/org/zstack/header/vm/metadata/UpdateVmInstanceMetadataMsg.javaheader/src/main/java/org/zstack/header/vm/metadata/UpdateVmInstanceMetadataOnPrimaryStorageMsg.javaheader/src/main/java/org/zstack/header/vm/metadata/UpdateVmInstanceMetadataOnPrimaryStorageReply.javaheader/src/main/java/org/zstack/header/vm/metadata/UpdateVmInstanceMetadataReply.javaheader/src/main/java/org/zstack/header/vm/metadata/VmInstanceMetadataConstants.javaheader/src/main/java/org/zstack/header/vm/metadata/VmInstanceMetadataDTO.javaheader/src/main/java/org/zstack/header/vm/metadata/VmMetadataCategory.javaheader/src/main/java/org/zstack/header/vm/metadata/VmMetadataDirtyService.javaheader/src/main/java/org/zstack/header/vm/metadata/VmMetadataDirtyVO.javaheader/src/main/java/org/zstack/header/vm/metadata/VmMetadataDirtyVO_.javaheader/src/main/java/org/zstack/header/vm/metadata/VmMetadataFlushStateVO.javaheader/src/main/java/org/zstack/header/vm/metadata/VmMetadataFlushStateVO_.javaheader/src/main/java/org/zstack/header/vm/metadata/VmMetadataPathBuildExtensionPoint.javaheader/src/main/java/org/zstack/header/vm/metadata/VmMetadataPathReplacementExtensionPoint.javaheader/src/main/java/org/zstack/header/vm/metadata/VmMetadataResourcePersistExtensionPoint.javaheader/src/main/java/org/zstack/header/vm/metadata/VmUuidFromApiResolver.javaheader/src/main/java/org/zstack/header/vm/metadata/VolumeResourceMetadata.javaheader/src/main/java/org/zstack/header/volume/APIAttachDataVolumeToVmMsg.javaheader/src/main/java/org/zstack/header/volume/APIChangeVolumeStateMsg.javaheader/src/main/java/org/zstack/header/volume/APICreateVolumeSnapshotGroupMsg.javaheader/src/main/java/org/zstack/header/volume/APICreateVolumeSnapshotMsg.javaheader/src/main/java/org/zstack/header/volume/APIDeleteDataVolumeMsg.javaheader/src/main/java/org/zstack/header/volume/APIDetachDataVolumeFromVmMsg.javaheader/src/main/java/org/zstack/header/volume/APIExpungeDataVolumeMsg.javaheader/src/main/java/org/zstack/header/volume/APIFlattenVolumeMsg.javaheader/src/main/java/org/zstack/header/volume/APIRecoverDataVolumeMsg.javaheader/src/main/java/org/zstack/header/volume/APISyncVolumeSizeMsg.javaheader/src/main/java/org/zstack/header/volume/APIUndoSnapshotCreationMsg.javaheader/src/main/java/org/zstack/header/volume/APIUpdateVolumeMsg.javaplugin/localstorage/src/main/java/org/zstack/storage/primary/local/APILocalStorageMigrateVolumeMsg.javaplugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageBase.javaplugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageHypervisorBackend.javaplugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageKvmBackend.javaplugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageSimulator.javaplugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageSimulatorConfig.javaplugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageVmMetadataExtension.javaplugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorage.javaplugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorageBackend.javaplugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorageKVMBackend.javaplugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorageKVMBackendCommands.javaplugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsVmMetadataExtension.javaresourceconfig/src/main/java/org/zstack/resourceconfig/APIDeleteResourceConfigMsg.javaresourceconfig/src/main/java/org/zstack/resourceconfig/APIUpdateResourceConfigMsg.javasdk/src/main/java/SourceClassMap.javasdk/src/main/java/org/zstack/sdk/CleanupVmInstanceMetadataAction.javasdk/src/main/java/org/zstack/sdk/CleanupVmInstanceMetadataResult.javasdk/src/main/java/org/zstack/sdk/GetVmInstanceMetadataFromPrimaryStorageAction.javasdk/src/main/java/org/zstack/sdk/GetVmInstanceMetadataFromPrimaryStorageResult.javasdk/src/main/java/org/zstack/sdk/RegisterVmInstanceFromMetadataAction.javasdk/src/main/java/org/zstack/sdk/RegisterVmInstanceFromMetadataResult.javasdk/src/main/java/org/zstack/sdk/ScanVmInstanceMetadataFromPrimaryStorageAction.javasdk/src/main/java/org/zstack/sdk/ScanVmInstanceMetadataFromPrimaryStorageResult.javasdk/src/main/java/org/zstack/sdk/UpdateVmInstanceMetadataAction.javasdk/src/main/java/org/zstack/sdk/UpdateVmInstanceMetadataResult.javasdk/src/main/java/org/zstack/sdk/VmMetadataScanEntry.javastorage/src/main/java/org/zstack/storage/primary/PrimaryStorageBase.javatestlib/src/main/java/org/zstack/testlib/ApiHelper.groovytestlib/src/main/java/org/zstack/testlib/LocalStorageSpec.groovytestlib/src/main/java/org/zstack/testlib/NfsPrimaryStorageSpec.groovy
| String error = ext.validateMetadataPath(psUuid, path); | ||
| if (error != null) { | ||
| throw new ApiMessageInterceptionException(argerr(error)); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Platform.argerr definition ==="
fd 'Platform.java' -x rg -n -C4 '\bargerr\s*\(|String\.format' {}
echo
echo "=== validateMetadataPath implementations ==="
rg -n -C4 --glob '*.java' '\bvalidateMetadataPath\s*\('Repository: MatheMatrix/zstack
Length of output: 21308
🏁 Script executed:
# 查找 Platform.java 中的 err() 方法定义和完整的 argerr() 方法
rg -n -A 15 'private static.*err\(|public static.*err\(' compute/src/main/java/org/zstack/compute/vm/../../../core/src/main/java/org/zstack/core/Platform.java 2>/dev/null || rg -n -A 15 'private static.*err\(|public static.*err\(' -g '**Platform.java'Repository: MatheMatrix/zstack
Length of output: 5667
🏁 Script executed:
# 查找 validateMetadataPath 的具体实现
rg -n -B 2 -A 8 'validateMetadataPath\s*\(' --type javaRepository: MatheMatrix/zstack
Length of output: 5318
🏁 Script executed:
# 更直接的方法:查找 Platform.java 文件位置并读取相关部分
fd 'Platform.java' --type f | head -5Repository: MatheMatrix/zstack
Length of output: 174
argerr(error) 会将动态错误消息当作格式字符串,需要改为 argerr("%s", error) 以安全处理。
当 error 包含 % 字符时(例如扩展返回包含用户输入 metadataPath 的错误消息),调用 String.format(error, {}) 会抛出 MissingFormatArgumentException 而非参数校验错误。查证表明 argerr() 最终通过 err() 方法中的 toI18nString(fmt, args) 调用 String.format() 处理第一个参数作为格式字符串。
虽然当前实现返回静态错误消息,但接口设计允许扩展返回包含用户输入的动态错误消息,因此应采用 argerr("%s", error) 的安全写法,将 error 作为参数传递而非格式字符串。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@compute/src/main/java/org/zstack/compute/vm/VmInstanceApiInterceptor.java`
around lines 1360 - 1362, The current call throws
ApiMessageInterceptionException using argerr(error) which treats the dynamic
error string as a format pattern; change the throw site (the block that calls
ext.validateMetadataPath(psUuid, path) and throws new
ApiMessageInterceptionException(...)) to pass the message as an argument, e.g.
use argerr("%s", error) so the returned error is safely formatted as data rather
than a format string; keep the rest of the logic (checking error != null and
throwing ApiMessageInterceptionException) unchanged.
| import org.zstack.header.other.APIMultiAuditor; | ||
| import org.zstack.header.rest.APINoSee; | ||
| import org.zstack.header.rest.RestRequest; | ||
| import org.zstack.header.vm.metadata.MetadataImpact; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# 验证 NicUuidToVmUuidResolver 是否已实现
rg -n "class NicUuidToVmUuidResolver" --type=java
rg -n "NicUuidToVmUuidResolver" --type=java -lRepository: MatheMatrix/zstack
Length of output: 349
🏁 Script executed:
# 搜索所有resolver实现
find . -name "*Resolver*.java" -type f | head -20
# 搜索MetadataImpact注解的使用示例
rg "@MetadataImpact" --type=java -A 3 | head -50
# 检查是否有resolver实现的base class或interface
rg "class.*Resolver" --type=java | grep -i "resolver"Repository: MatheMatrix/zstack
Length of output: 6479
🏁 Script executed:
# 查找元数据resolver相关的实现
find . -path "*/metadata/*" -name "*.java" -type f | grep -i resolver
# 查找resolver接口或基类
rg "interface.*Resolver|abstract class.*Resolver|class.*Resolver" --type=java | grep -v "test\|Test"
# 搜索resolver注册或工厂机制
rg "NicUuidToVmUuidResolver\|resolverClass\|getResolver" --type=java | head -30Repository: MatheMatrix/zstack
Length of output: 650
🏁 Script executed:
# 查看VmUuidFromApiResolver接口定义
cat -n header/src/main/java/org/zstack/header/vm/metadata/VmUuidFromApiResolver.java
# 搜索VmUuidFromApiResolver的实现类
rg "implements VmUuidFromApiResolver" --type=java
# 在compute包中搜索resolver相关实现
find compute -name "*Resolver*.java" -type f
# 搜索所有metadata相关的resolver
find . -path "*/metadata/*" -type f -name "*.java"Repository: MatheMatrix/zstack
Length of output: 2530
🏁 Script executed:
# 查看MetadataImpact注解定义
cat -n header/src/main/java/org/zstack/header/vm/metadata/MetadataImpact.java
# 在整个compute目录中搜索任何Nic或resolver相关的类
find compute -name "*.java" -type f | xargs grep -l "NicUuidToVmUuidResolver\|VmUuidFromApiResolver" 2>/dev/null | head -10
# 搜索@Component或@Bean注解且包含resolver的类
rg "@Component|@Bean" compute --type=java -A 2 | grep -i "resolver" -B 2 -A 2 | head -30Repository: MatheMatrix/zstack
Length of output: 1172
🏁 Script executed:
# 搜索所有VmUuidFromApiResolver的实现
rg "class.*implements.*VmUuidFromApiResolver|class.*VmUuidFromApiResolver" --type=java -A 3
# 在compute包中搜索resolver的注册或bean定义
find compute -name "*.java" -type f | xargs grep -l "@Component\|@Bean\|@Service" | xargs grep -l -i "resolver" | head -5
# 搜索所有实现了resolver接口的类
rg "implements VmUuidFromApiResolver\|extends.*Resolver" --type=java -B 2 -A 5 | head -50Repository: MatheMatrix/zstack
Length of output: 44
🏁 Script executed:
# 搜索所有包含resolver的配置文件和Spring注册
find . -type f \( -name "*.xml" -o -name "*.properties" -o -name "*.yml" -o -name "*.yaml" \) | xargs grep -l "resolver" 2>/dev/null | head -10
# 搜索任何resolver相关的factory或registry
rg "resolver.*factory|registry.*resolver|ResolverFactory" --type=java -i | head -20
# 检查是否有resolver的自动发现或初始化机制
rg "getBean\(.*resolver|resolverMap\|resolverRegistry" --type=java -i | head -20Repository: MatheMatrix/zstack
Length of output: 44
🏁 Script executed:
# 查看APIChangeVmNicStateMsg.java完整内容
cat header/src/main/java/org/zstack/header/vm/APIChangeVmNicStateMsg.java
# 搜索所有提到NicUuidToVmUuidResolver的地方
rg "NicUuidToVmUuidResolver" --type=java -B 2 -A 2
# 检查是否有任何Nic相关的resolver定义(不同命名)
find compute -name "*Nic*.java" -type f | xargs grep -l "VmUuidFromApiResolver\|Resolver" 2>/dev/null | head -10Repository: MatheMatrix/zstack
Length of output: 5077
缺失 NicUuidToVmUuidResolver Spring Bean 实现。
@MetadataImpact 注解指定的 resolver = "NicUuidToVmUuidResolver" 在整个代码库中不存在。根据 MetadataImpact 注解定义,resolver 参数值应为已注册的 Spring Bean ID,且该 Bean 必须实现 VmUuidFromApiResolver 接口。
当前有 4 个 API 消息类引用此不存在的 resolver:
- APIChangeVmNicStateMsg.java
- APIDetachL3NetworkFromVmMsg.java
- APIUpdateVmNicDriverMsg.java
- APIChangeVmNicNetworkMsg.java
需要实现 NicUuidToVmUuidResolver 并将其注册为 Spring Bean,以解析网卡 UUID 到虚拟机 UUID 的映射。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@header/src/main/java/org/zstack/header/vm/APIChangeVmNicStateMsg.java` at
line 12, Implement a Spring bean named "NicUuidToVmUuidResolver" that implements
the VmUuidFromApiResolver interface and resolves a NIC UUID to its VM UUID;
create a class (e.g., NicUuidToVmUuidResolver) implementing
VmUuidFromApiResolver, inject the appropriate NIC/VM lookup service or DAO used
elsewhere in the project, implement the resolver method to fetch the VM UUID by
the given nicUuid (handling not-found and errors consistently with other
resolvers), and register it as a Spring bean with the id
"NicUuidToVmUuidResolver" so that the
`@MetadataImpact`(resolver="NicUuidToVmUuidResolver") usages in
APIChangeVmNicStateMsg, APIDetachL3NetworkFromVmMsg, APIUpdateVmNicDriverMsg and
APIChangeVmNicNetworkMsg resolve correctly.
|
|
||
| clz APIGetVmInstanceMetadataFromPrimaryStorageMsg.class | ||
|
|
||
| desc """""" |
There was a problem hiding this comment.
补充请求 desc 内容,避免生成空说明。
Line 20 当前是空字符串,建议填入最小语义描述,避免 API 文档出现空白说明。
✏️ 建议修改
- desc """"""
+ desc """从主存储读取指定云主机元数据"""📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| desc """""" | |
| desc """从主存储读取指定云主机元数据""" |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@header/src/main/java/org/zstack/header/vm/APIGetVmInstanceMetadataFromPrimaryStorageMsgDoc_zh_cn.groovy`
at line 20, 在 APIGetVmInstanceMetadataFromPrimaryStorageMsgDoc_zh_cn.groovy 中的
desc """""" 为空,会在生成的 API 文档中留下空白说明;请用一条最小语义的中文描述替换该空字符串,例如说明此 API
获取虚拟机在主存储上的元数据(或类似简短句子),以便在类/文件
APIGetVmInstanceMetadataFromPrimaryStorageMsgDoc_zh_cn.groovy 中的 desc
字段有实际内容并避免空说明。
| String getPrimaryStorageType(); | ||
| PathReplacementResult calculatePathReplacements(String targetPsUuid, List<String> allOldPaths); | ||
| class PathReplacementResult { |
There was a problem hiding this comment.
为扩展点接口方法补充 Javadoc。
Line 8-9 的接口方法缺少注释,后续实现方难以统一语义与输入输出约束。
📝 建议修改
public interface VmMetadataPathReplacementExtensionPoint {
+ /**
+ * `@return` 当前扩展实现支持的主存储类型标识
+ */
String getPrimaryStorageType();
+
+ /**
+ * 计算 metadata 原路径到当前路径的替换信息。
+ *
+ * `@param` targetPsUuid 目标主存储 UUID
+ * `@param` allOldPaths 待处理的历史 metadata 路径集合
+ * `@return` 替换结果(映射与前缀替换信息)
+ */
PathReplacementResult calculatePathReplacements(String targetPsUuid, List<String> allOldPaths);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| String getPrimaryStorageType(); | |
| PathReplacementResult calculatePathReplacements(String targetPsUuid, List<String> allOldPaths); | |
| class PathReplacementResult { | |
| /** | |
| * `@return` 当前扩展实现支持的主存储类型标识 | |
| */ | |
| String getPrimaryStorageType(); | |
| /** | |
| * 计算 metadata 原路径到当前路径的替换信息。 | |
| * | |
| * `@param` targetPsUuid 目标主存储 UUID | |
| * `@param` allOldPaths 待处理的历史 metadata 路径集合 | |
| * `@return` 替换结果(映射与前缀替换信息) | |
| */ | |
| PathReplacementResult calculatePathReplacements(String targetPsUuid, List<String> allOldPaths); | |
| class PathReplacementResult { |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@header/src/main/java/org/zstack/header/vm/metadata/VmMetadataPathReplacementExtensionPoint.java`
around lines 8 - 10, Add Javadoc for the interface methods
getPrimaryStorageType() and calculatePathReplacements(String targetPsUuid,
List<String> allOldPaths) describing their purpose, expected inputs and outputs,
null/empty handling, exception behavior and any concurrency or lifecycle
constraints; also document the nested PathReplacementResult class fields and
meaning (e.g., mapping semantics, order guarantees). Ensure the Javadoc follows
project conventions (brief summary + `@param/`@return/@throws where applicable)
and do not add extra modifiers to the interface methods.
| String resolveVmUuid(String fieldValue); | ||
|
|
||
| default List<String> batchResolveVmUuids(List<String> fieldValues) { | ||
| List<String> result = new ArrayList<>(); | ||
| if (fieldValues == null || fieldValues.isEmpty()) { | ||
| return result; | ||
| } | ||
| for (String v : fieldValues) { | ||
| if (v == null) { | ||
| continue; | ||
| } | ||
| String vmUuid = resolveVmUuid(v); | ||
| if (vmUuid != null) { | ||
| result.add(vmUuid); | ||
| } | ||
| } | ||
| return result; | ||
| } |
There was a problem hiding this comment.
请为接口方法补充 Javadoc 注释。
当前两个接口方法都缺少 Javadoc,不符合仓库 Java 接口规范,后续会影响 API 意图可读性与文档生成一致性。
✍️ 建议修改
public interface VmUuidFromApiResolver {
+ /**
+ * 将 API 字段值解析为虚拟机 UUID。
+ *
+ * `@param` fieldValue API 传入字段值
+ * `@return` 解析得到的 VM UUID;无法解析时返回 null
+ */
String resolveVmUuid(String fieldValue);
+ /**
+ * 批量解析 API 字段值为虚拟机 UUID 列表。
+ *
+ * `@param` fieldValues API 字段值列表
+ * `@return` 解析成功的 VM UUID 列表(不包含 null)
+ */
default List<String> batchResolveVmUuids(List<String> fieldValues) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@header/src/main/java/org/zstack/header/vm/metadata/VmUuidFromApiResolver.java`
around lines 7 - 24, 为接口 VmUuidFromApiResolver 的两个方法 resolveVmUuid 和
batchResolveVmUuids 添加完整的 Javadoc:在 resolveVmUuid 上说明方法用途(将 API 字段值解析为 VM
UUID)、参数说明 (`@param` fieldValue 允许为 null 或空时的行为)、返回值说明 (`@return` 返回解析出的 VM UUID 或
null);在 batchResolveVmUuids 上说明批量解析行为、对 null 或空集合以及单个 null 元素的处理规则、返回值描述(返回解析成功的
VM UUID 列表,保持输入顺序或说明不会包含 nulls);遵守接口规范不要为接口方法添加多余修饰符(如 public)。
| private void createResourceRef(String primaryStorageUuid, String resourceUuid, | ||
| String resourceType, String hostUuid, long size, Timestamp now) { | ||
| boolean exists = Q.New(LocalStorageResourceRefVO.class).eq(LocalStorageResourceRefVO_.resourceUuid, resourceUuid).isExists(); | ||
| if (exists) { | ||
| logger.debug(String.format("LocalStorageResourceRefVO for resource[uuid:%s] already exists, skip creation", resourceUuid)); | ||
| return; | ||
| } | ||
|
|
||
| LocalStorageResourceRefVO ref = new LocalStorageResourceRefVO(); | ||
| ref.setPrimaryStorageUuid(primaryStorageUuid); | ||
| ref.setResourceUuid(resourceUuid); | ||
| ref.setResourceType(resourceType); | ||
| ref.setHostUuid(hostUuid); | ||
| ref.setSize(size); | ||
| ref.setCreateDate(now); | ||
| ref.setLastOpDate(now); | ||
| dbf.persist(ref); | ||
| } |
There was a problem hiding this comment.
createResourceRef 中存在潜在的竞态条件。
第 153-157 行的"先检查后插入"模式存在 TOCTOU(Time-of-Check to Time-of-Use)问题。如果两个并发请求同时通过存在性检查,其中一个会因数据库唯一约束而失败。
建议使用数据库级别的 upsert 或在 dbf.persist() 处捕获约束冲突异常:
🛡️ 建议的防御性修复
private void createResourceRef(String primaryStorageUuid, String resourceUuid,
String resourceType, String hostUuid, long size, Timestamp now) {
boolean exists = Q.New(LocalStorageResourceRefVO.class).eq(LocalStorageResourceRefVO_.resourceUuid, resourceUuid).isExists();
if (exists) {
logger.debug(String.format("LocalStorageResourceRefVO for resource[uuid:%s] already exists, skip creation", resourceUuid));
return;
}
LocalStorageResourceRefVO ref = new LocalStorageResourceRefVO();
ref.setPrimaryStorageUuid(primaryStorageUuid);
ref.setResourceUuid(resourceUuid);
ref.setResourceType(resourceType);
ref.setHostUuid(hostUuid);
ref.setSize(size);
ref.setCreateDate(now);
ref.setLastOpDate(now);
- dbf.persist(ref);
+ try {
+ dbf.persist(ref);
+ } catch (Exception e) {
+ // Handle duplicate key exception in case of race condition
+ if (Q.New(LocalStorageResourceRefVO.class).eq(LocalStorageResourceRefVO_.resourceUuid, resourceUuid).isExists()) {
+ logger.debug(String.format("LocalStorageResourceRefVO for resource[uuid:%s] created by concurrent call, skip", resourceUuid));
+ } else {
+ throw e;
+ }
+ }
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageVmMetadataExtension.java`
around lines 151 - 168, The createResourceRef method in
LocalStorageVmMetadataExtension has a TOCTOU race: the exists check
(Q.New(...).isExists()) can race and dbf.persist(ref) may throw a
unique-constraint/duplicate-key error; replace the check-with-insert pattern
with a safe upsert or wrap dbf.persist(ref) in a try/catch to handle constraint
violations gracefully (detect the duplicate-key/constraint exception thrown by
the ORM/DB and treat it as a no-op), or use a DB-level upsert function if
available so concurrent callers won't fail; update createResourceRef to catch
the specific persistence exception and log/debug that the ref already exists
instead of letting the error propagate.
| public static final String WRITE_VM_METADATA_PATH = "/nfsprimarystorage/vm/metadata/write"; | ||
|
|
||
| public static final String GET_VM_INSTANCE_METADATA_PATH = "/nfsprimarystorage/vm/metadata/get"; | ||
| public static final String SCAN_VM_METADATA_PATH = "/nfsprimarystorage/vm/metadata/scan"; | ||
| public static final String CLEANUP_VM_METADATA_PATH = "/nfsprimarystorage/vm/metadata/cleanup"; | ||
| public static final String NFS_PREFIX_REBASE_BACKING_FILES_PATH = "/nfsprimarystorage/snapshot/prefixrebasebackingfiles"; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
paths=(
"/nfsprimarystorage/vm/metadata/write"
"/nfsprimarystorage/vm/metadata/get"
"/nfsprimarystorage/vm/metadata/scan"
"/nfsprimarystorage/vm/metadata/cleanup"
"/nfsprimarystorage/snapshot/prefixrebasebackingfiles"
)
for p in "${paths[@]}"; do
echo "== $p =="
rg -n -C2 --fixed-strings "$p" \
-g '!**/target/**' \
-g '!**/build/**' \
-g '!**/out/**'
doneRepository: MatheMatrix/zstack
Length of output: 4995
🏁 Script executed:
# 搜索 NfsPrimaryStorage 相关的 agent 处理代码
rg -n "nfsprimarystorage" \
-g '!**/target/**' \
-g '!**/build/**' \
-g '!**/out/**' \
--type java | head -50Repository: MatheMatrix/zstack
Length of output: 11287
🏁 Script executed:
# 搜索是否有这些常量被使用的地方(除了定义)
rg -n "WRITE_VM_METADATA_PATH|GET_VM_INSTANCE_METADATA_PATH|SCAN_VM_METADATA_PATH|CLEANUP_VM_METADATA_PATH|NFS_PREFIX_REBASE_BACKING_FILES_PATH" \
-g '!**/target/**' \
-g '!**/build/**' \
-g '!**/out/**' \
--type javaRepository: MatheMatrix/zstack
Length of output: 4466
🏁 Script executed:
# 搜索 KVM agent 中的路径处理 handler
rg -n "metadata" \
-g '!**/target/**' \
-g '!**/build/**' \
-g '!**/out/**' \
--type java | grep -i "kvm\|agent\|handler" | head -30Repository: MatheMatrix/zstack
Length of output: 4908
🏁 Script executed:
# 搜索 KVM agent 模块(不是 simulator)
find . -path "**/target" -prune -o -path "**/build" -prune -o \
-type f -name "*.java" -print | xargs grep -l "RequestMapping\|@PostMapping" | grep -i kvm | grep -v simulator | head -20Repository: MatheMatrix/zstack
Length of output: 44
🏁 Script executed:
# 查找哪里实现了 /nfsprimarystorage 路由处理
rg -n "@RequestMapping|@PostMapping|@GetMapping" \
-g '!**/target/**' \
-g '!**/build/**' \
--type java -A1 | grep -B1 "nfsprimarystorage"Repository: MatheMatrix/zstack
Length of output: 44
🏁 Script executed:
# 查找 KVM host agent 代码,看是否有新路径的处理
find . -path "**/target" -prune -o -path "**/build" -prune -o \
-type f -name "KVMHost.java" -print | head -5Repository: MatheMatrix/zstack
Length of output: 116
🏁 Script executed:
# 查看 KVMHost.java 大小和内容,查找 metadata 相关的处理器
wc -l plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.javaRepository: MatheMatrix/zstack
Length of output: 119
🏁 Script executed:
# 在 KVMHost.java 中搜索 metadata 相关代码
rg -n "metadata|/nfsprimarystorage" \
plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.javaRepository: MatheMatrix/zstack
Length of output: 44
🏁 Script executed:
# 查找注册 handler 或 route 的代码位置
rg -n "RequestMapping\|addPath\|registerHandler\|addRoute" \
plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java | head -20Repository: MatheMatrix/zstack
Length of output: 44
🏁 Script executed:
# 在整个 agent 代码中搜索这些新 path 是否有处理
rg -n "WRITE_VM_METADATA_PATH|GET_VM_INSTANCE_METADATA_PATH|SCAN_VM_METADATA_PATH|CLEANUP_VM_METADATA_PATH|NFS_PREFIX_REBASE_BACKING_FILES_PATH" \
plugin/kvm/ \
-g '!**/target/**' \
-g '!**/build/**'Repository: MatheMatrix/zstack
Length of output: 44
🏁 Script executed:
# 查找 NFS 相关的 agent 处理代码(如果存在)
find plugin/kvm -name "*nfs*" -o -name "*Nfs*" 2>/dev/null | head -10Repository: MatheMatrix/zstack
Length of output: 44
🏁 Script executed:
# 查看 KVMHost.java 中是否有 POST handler 或类似的路由注册
grep -n "POST\|handler\|route" plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java | head -30Repository: MatheMatrix/zstack
Length of output: 230
确认生产态 KVM agent 尚未同步注册这 5 个新 path,合入前必须完成 agent 侧实现。
管理节点已在发送这些新 path 的请求(NfsPrimaryStorageKVMBackend lines 2078/2108/2140/2176/2210),但搜索结果表明 KVM agent 代码中完全没有对应的处理器。这将在生产环境导致运行时 404 错误。
/nfsprimarystorage/vm/metadata/write/nfsprimarystorage/vm/metadata/get/nfsprimarystorage/vm/metadata/scan/nfsprimarystorage/vm/metadata/cleanup/nfsprimarystorage/snapshot/prefixrebasebackingfiles
需要在 KVM agent 侧(plugin/kvm)添加这 5 个 path 的处理器注册。参考 LocalStorageSimulator/LocalStorageKvmBackend 的实现模式,确保 agent 能正确处理这些请求。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorageKVMBackend.java`
around lines 136 - 141, NfsPrimaryStorageKVMBackend adds five new request paths
(WRITE_VM_METADATA_PATH, GET_VM_INSTANCE_METADATA_PATH, SCAN_VM_METADATA_PATH,
CLEANUP_VM_METADATA_PATH, NFS_PREFIX_REBASE_BACKING_FILES_PATH) but the KVM
agent has no registered handlers for them; add corresponding handler
registrations in the KVM agent (plugin/kvm) following the pattern used by
LocalStorageSimulator/LocalStorageKvmBackend: implement request processors for
each path that deserialize the incoming request, perform the same semantics
expected by NfsPrimaryStorageKVMBackend, and register them with the agent’s HTTP
dispatcher so the agent will not return 404s for these endpoints.
| import org.zstack.header.message.APIDeleteMessage; | ||
| import org.zstack.header.message.APIParam; | ||
| import org.zstack.header.rest.RestRequest; | ||
| import org.zstack.header.vm.metadata.MetadataImpact; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# 验证 ResourceUuidToVmUuidResolver 的实现
rg -n "class ResourceUuidToVmUuidResolver" --type=java -A 20Repository: MatheMatrix/zstack
Length of output: 44
🏁 Script executed:
# Find the APIDeleteResourceConfigMsg file
fd -t f "APIDeleteResourceConfigMsg.java"Repository: MatheMatrix/zstack
Length of output: 148
🏁 Script executed:
# Search for ResourceUuidToVmUuidResolver anywhere in codebase
rg "ResourceUuidToVmUuidResolver" --type=javaRepository: MatheMatrix/zstack
Length of output: 860
🏁 Script executed:
# Search for similar resolver patterns
rg "Resolver.*class" --type=java -A 5 | head -100Repository: MatheMatrix/zstack
Length of output: 809
🏁 Script executed:
# Look for `@MetadataImpact` annotation usage to understand the pattern
rg "@MetadataImpact" --type=java -B 2 -A 3 | head -150Repository: MatheMatrix/zstack
Length of output: 17219
🏁 Script executed:
# Search for where resolver classes are defined/implemented
rg "class.*Resolver.*{" --type=java -B 2 | head -200Repository: MatheMatrix/zstack
Length of output: 161
🏁 Script executed:
# Check if there's a resolver registry or factory
rg "ResourceUuidToVmUuidResolver\|VolumeUuidToVmUuidResolver\|VmUuidDirectResolver" --type=java | grep -v "@MetadataImpact" | head -50Repository: MatheMatrix/zstack
Length of output: 44
🏁 Script executed:
# Look for resolver implementations
fd -type f -name "*Resolver*.java" | head -30Repository: MatheMatrix/zstack
Length of output: 232
🏁 Script executed:
# Search for resolver class definitions (simpler pattern)
rg "class.*Resolver" --type=java -B 1 -A 3 | head -150Repository: MatheMatrix/zstack
Length of output: 1718
🏁 Script executed:
# Find all resolver files
fd ".*Resolver.*\.java$"Repository: MatheMatrix/zstack
Length of output: 139
🏁 Script executed:
# Check header directory for MetadataImpact related infrastructure
rg "MetadataImpact" --type=java | grep -v "@MetadataImpact" | head -50Repository: MatheMatrix/zstack
Length of output: 6401
🏁 Script executed:
# Read the MetadataImpact interface to understand resolver expectations
cat header/src/main/java/org/zstack/header/vm/metadata/MetadataImpact.javaRepository: MatheMatrix/zstack
Length of output: 934
🏁 Script executed:
# Search for resolver implementations in the metadata directory
fd -type f -name "*.java" | xargs rg "Resolver" | grep -i "metadata\|resource" | head -50Repository: MatheMatrix/zstack
Length of output: 9542
🏁 Script executed:
# Look for all resolver implementations
rg "implements.*Resolver\|extends.*Resolver" --type=javaRepository: MatheMatrix/zstack
Length of output: 44
🏁 Script executed:
# Look for VmUuidFromApiResolver interface
cat header/src/main/java/org/zstack/header/vm/metadata/VmUuidFromApiResolver.javaRepository: MatheMatrix/zstack
Length of output: 738
🏁 Script executed:
# Search for actual implementations of VmUuidFromApiResolver
rg "implements VmUuidFromApiResolver" --type=java -B 2 -A 10Repository: MatheMatrix/zstack
Length of output: 44
🏁 Script executed:
# Search in resourceconfig directory for resolver
fd -type f "*.java" resourceconfig/ | xargs rg "ResourceUuidToVmUuidResolver" 2>/dev/nullRepository: MatheMatrix/zstack
Length of output: 1048
🏁 Script executed:
# Search for all implementations of VmUuidFromApiResolver
rg "implements VmUuidFromApiResolver"Repository: MatheMatrix/zstack
Length of output: 44
🏁 Script executed:
# Search for bean configurations that might define ResourceUuidToVmUuidResolver
rg "ResourceUuidToVmUuidResolver" --type=java -B 5 -A 5Repository: MatheMatrix/zstack
Length of output: 5589
🏁 Script executed:
# Look for Spring bean definitions in resourceconfig
fd -type f "*.java" resourceconfig/ | xargs rg "@Component\|@Bean\|@Service" 2>/dev/null | head -30Repository: MatheMatrix/zstack
Length of output: 232
🏁 Script executed:
# Search for where resolvers are actually implemented/defined
fd -type f "*.java" | xargs grep -l "VolumeUuidToVmUuidResolver\|VmUuidDirectResolver\|NicUuidToVmUuidResolver" | head -10Repository: MatheMatrix/zstack
Length of output: 232
🏁 Script executed:
# Check compute module for resolver implementations
fd -type f "*.java" compute/ | xargs rg "class.*Resolver.*implements" 2>/dev/null | head -20Repository: MatheMatrix/zstack
Length of output: 232
🏁 Script executed:
# Search for `@Component` or configuration that registers resolvers
rg "@Component.*Resolver\|@Bean.*Resolver" -A 5 | head -50Repository: MatheMatrix/zstack
Length of output: 44
🏁 Script executed:
# Search for any file containing actual resolver implementations
find . -name "*.java" -type f -exec grep -l "class.*Resolver.*implements VmUuidFromApiResolver" {} \;Repository: MatheMatrix/zstack
Length of output: 44
🏁 Script executed:
# Look for Spring component scan or bean registration for resolvers
find . -name "*.java" -type f -exec grep -l "ResourceUuidToVmUuidResolver\|VolumeUuidToVmUuidResolver" {} \; | head -20Repository: MatheMatrix/zstack
Length of output: 1343
🏁 Script executed:
# Search in all java files for resolver pattern
find . -name "*.java" -type f -exec grep -H "class.*Resolver" {} \; | grep -i "uuid\|resolver" | head -30Repository: MatheMatrix/zstack
Length of output: 457
🏁 Script executed:
# Search for Spring `@Component` definitions with resolver names
find . -name "*.java" -type f | xargs grep -l "@Component.*ResourceUuidToVmUuidResolver\|@Component.*Resolver" 2>/dev/null | head -10Repository: MatheMatrix/zstack
Length of output: 44
🏁 Script executed:
# Look for configuration classes that might define these beans
find . -name "*.java" -type f -path "*config*" | xargs grep -l "ResourceUuidToVmUuidResolver\|VolumeUuidToVmUuidResolver" 2>/dev/nullRepository: MatheMatrix/zstack
Length of output: 239
🏁 Script executed:
# Check the entire resourceconfig module structure
find resourceconfig/ -name "*.java" -type f | head -30Repository: MatheMatrix/zstack
Length of output: 2634
🏁 Script executed:
# Look at the resourceconfig module for any configuration or bean definitions
find resourceconfig/ -name "*.java" -type f -exec grep -l "@Component\|@Bean\|@Configuration" {} \;Repository: MatheMatrix/zstack
Length of output: 44
🏁 Script executed:
# Search across the entire codebase for implementations of VmUuidFromApiResolver
find . -name "*.java" -type f -exec grep -l "implements VmUuidFromApiResolver" {} \;Repository: MatheMatrix/zstack
Length of output: 44
🏁 Script executed:
# Check if there are any XML or property files that might define beans
find . -name "*.xml" -o -name "*.properties" | xargs grep -l "ResourceUuidToVmUuidResolver" 2>/dev/nullRepository: MatheMatrix/zstack
Length of output: 46
🏁 Script executed:
# Look for any recent resolver implementations
find . -name "*.java" -type f -exec grep -H "VolumeUuidToVmUuidResolver\|VmUuidDirectResolver" {} \; | grep "class\|@Component\|@Bean" | head -20Repository: MatheMatrix/zstack
Length of output: 44
🏁 Script executed:
# Get the raw count of implementations
find . -name "*.java" -type f | xargs grep "implements VmUuidFromApiResolver"Repository: MatheMatrix/zstack
Length of output: 46
🏁 Script executed:
# Search for any Spring bean registration or configuration that might use string-based bean names
find . -name "*.java" -type f | xargs grep -i "bean\|resolver" | grep -i "resource.*uuid\|volume.*uuid" | head -40Repository: MatheMatrix/zstack
Length of output: 5220
🏁 Script executed:
# Check compute module for any resolver implementations
find compute/ -name "*.java" -type f | xargs grep "VmUuidFromApiResolver\|Resolver" | head -50Repository: MatheMatrix/zstack
Length of output: 44
🏁 Script executed:
# Search the entire codebase one more time with simpler pattern
find . -type f -name "*.java" | xargs grep -h "class.*Resolver" | grep -v "^//" | head -50Repository: MatheMatrix/zstack
Length of output: 261
🏁 Script executed:
# Check if resolvers might be in a separate module or plugin
find . -type d -name "*metadata*" -o -type d -name "*resolver*"Repository: MatheMatrix/zstack
Length of output: 114
🏁 Script executed:
# Read the APIDeleteResourceConfigMsg file completely to understand context
cat -n resourceconfig/src/main/java/org/zstack/resourceconfig/APIDeleteResourceConfigMsg.javaRepository: MatheMatrix/zstack
Length of output: 2180
🏁 Script executed:
# Check metadata directory for all resolver files
find ./header/src/main/java/org/zstack/header/vm/metadata -name "*.java" -type fRepository: MatheMatrix/zstack
Length of output: 1678
🏁 Script executed:
# Check for any todo or fixme comments related to resolver
find . -type f -name "*.java" | xargs grep -i "todo.*resolver\|fixme.*resolver" | head -20Repository: MatheMatrix/zstack
Length of output: 44
🏁 Script executed:
# Search in Spring configuration or auto-configuration files
find . -type f -name "*.java" | xargs grep -l "EnableAutoConfiguration\|ComponentScan\|Configuration" | xargs grep -l "metadata\|resolver" | head -10Repository: MatheMatrix/zstack
Length of output: 44
🏁 Script executed:
# Final verification: search for any resolver bean definition patterns
find . -type f -name "*.java" | xargs grep -h "new.*Resolver\|bean.*resolver" | grep -i "resourceuuid\|volume" | head -20Repository: MatheMatrix/zstack
Length of output: 44
🏁 Script executed:
# Check if maybe resolvers are auto-wired or registered differently
find . -type f -name "*.java" | xargs grep -l "VolumeUuidToVmUuidResolver" | xargs cat | grep -A 20 "class.*VolumeUuidToVmUuidResolver"Repository: MatheMatrix/zstack
Length of output: 44
🏁 Script executed:
# One more comprehensive check - look for all files that might define these resolvers
find . -type f -name "*.java" | xargs grep "VolumeUuidToVmUuidResolver\|VmUuidDirectResolver" | grep -v "@MetadataImpact" | grep -v "import"Repository: MatheMatrix/zstack
Length of output: 44
🏁 Script executed:
# Check if resolvers might be registered through a plugin or extension point
find . -type f -name "*.java" | xargs grep -l "PluginRegistry\|ExtensionPointRegistry" | xargs grep -l "Resolver" | head -5Repository: MatheMatrix/zstack
Length of output: 129
🏁 Script executed:
# Search for any service or manager that handles resolver registration
find . -type f -name "*.java" | xargs grep -l "registerResolver\|getResolver\|resolveVmUuid" | head -10Repository: MatheMatrix/zstack
Length of output: 141
🏁 Script executed:
# Look for where MetadataImpact annotation is actually processed/used
find . -type f -name "*.java" | xargs grep -l "MetadataImpact" | xargs grep -l "@Transactional\|Interceptor\|Aspect" | head -10Repository: MatheMatrix/zstack
Length of output: 44
🏁 Script executed:
# Check if resolvers might be in a separate artifact or module
find . -name "pom.xml" -type f | xargs grep -l "metadata\|resolver" | head -5Repository: MatheMatrix/zstack
Length of output: 77
🏁 Script executed:
# One final check - search for any Spring bean definition or factory that might create these resolvers
find . -type f \( -name "*.java" -o -name "*.xml" \) | xargs grep -l "createResolver\|getResolverBean\|resolverRegistry" 2>/dev/null | head -10Repository: MatheMatrix/zstack
Length of output: 44
🏁 Script executed:
# Check if the resolver might be expected to be created but is actually missing
find . -type f -name "*.java" | xargs grep -B 5 -A 5 "ResourceUuidToVmUuidResolver" | head -100Repository: MatheMatrix/zstack
Length of output: 5674
ResourceUuidToVmUuidResolver 实现缺失,无法处理非 VM 资源。
@MetadataImpact 注解引用了 ResourceUuidToVmUuidResolver 作为 Spring Bean,但在代码库中找不到其实现类。由于 resourceUuid 字段类型为 ResourceVO.class,可能指向任意资源类型(Host、Cluster、PrimaryStorage 等),而非仅限于 VM 关联的资源。需要实现 ResourceUuidToVmUuidResolver 以正确解析以下情况:
resourceUuid是 VM UUID 时的直接关联resourceUuid是与 VM 关联的资源(如 Volume、NIC)时的间接关联resourceUuid是与 VM 无关的资源时的处理(应返回 null)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@resourceconfig/src/main/java/org/zstack/resourceconfig/APIDeleteResourceConfigMsg.java`
at line 8, The project is missing an implementation of
ResourceUuidToVmUuidResolver referenced by the `@MetadataImpact` on
APIDeleteResourceConfigMsg (resourceUuid of type ResourceVO.class); implement a
Spring bean class named e.g. ResourceUuidToVmUuidResolverImpl that implements
ResourceUuidToVmUuidResolver, annotate it as a component/service, and implement
resolve(resourceUuid) to: 1) check if resourceUuid is a VM UUID (query
VmInstanceVO) and return it; 2) check known VM-related resources (VolumeVO ->
getVmInstanceUuid, VmNicVO -> getVmInstanceUuid, etc.) and return the associated
VM UUID when found; 3) return null for non-VM-related resources. Use existing
DAOs/VOs for lookups and keep the resolver registered so `@MetadataImpact` can
autowire it.
| @Param(required = true, nonempty = true, nullElements = false, emptyString = true, noTrim = false) | ||
| public java.util.List vmUuids; |
There was a problem hiding this comment.
vmUuids 仍然接受空字符串元素。
nonempty = true 只保证列表非空,但 emptyString = true 仍允许 [""] 这样的值通过 SDK 参数校验。对于 UUID 列表这里应该直接 fail fast;如果这是生成代码,根因应补到对应 API message 的参数约束后再重新生成。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@sdk/src/main/java/org/zstack/sdk/CleanupVmInstanceMetadataAction.java` around
lines 28 - 29, The vmUuids field in CleanupVmInstanceMetadataAction currently
allows empty-string elements because its `@Param` uses emptyString = true; change
the annotation to emptyString = false on the public java.util.List vmUuids field
so SDK validation will reject [""] values (or, if this file is generated, fix
the API message parameter constraint for vmUuids to set emptyString=false and
re-generate the SDK so the generated CleanupVmInstanceMetadataAction has
emptyString=false).
| @Param(required = true, nonempty = false, nullElements = false, emptyString = true, noTrim = false) | ||
| public java.lang.String uuid; |
There was a problem hiding this comment.
uuid 被声明成“必填但可为空字符串”。
required = true 配上 nonempty = false / emptyString = true 会让 uuid="" 直接通过 SDK 校验,错误只能推迟到服务端。这里应把空字符串拦在客户端;如果这是生成产物,请回到对应 API message 的 @APIParam 上修正后再生成 SDK。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@sdk/src/main/java/org/zstack/sdk/GetVmInstanceMetadataFromPrimaryStorageAction.java`
around lines 28 - 29, The uuid field in
GetVmInstanceMetadataFromPrimaryStorageAction is marked required=true but still
allows empty strings (nonempty=false, emptyString=true), so update the `@Param` on
the uuid field to reject empty strings (e.g., set nonempty=true and
emptyString=false) so the SDK client-side validation blocks uuid=""; if this
file is generated, instead fix the source API message's `@APIParam` for uuid and
re-generate the SDK.
APIImpact
Resolves: ZSV-11559
Change-Id: I6b6a6378627264646d6a76726762736e77787373
sync from gitlab !9523