[network]: ZSTAC-79206 管理网络 IPv6 支持 M1/M2/M3 (Java)#3858
[network]: ZSTAC-79206 管理网络 IPv6 支持 M1/M2/M3 (Java)#3858zstack-robot-1 wants to merge 34 commits intofeature-mgt-ip6from
Conversation
Resolves: ZCF-1365 Change-Id: I73687962636e7871626e687761626d6661716668
Resolves: ZCF-1365 Change-Id: I7262787a6474667a766d77766165796f73717775
Resolves: ZCF-1365 Change-Id: I647469616679716d7366686c77617073746f776c
Resolves: ZCF-1365 Change-Id: I7a61747778757574656967626c6a736366716b6a
<doc>[sdnController]: integraion zns into cloud See merge request zstackio/zstack!9447
Resolves: ZCF-1365 Change-Id: I65767164636167726d6369726f63666b68666477
<fix>[zns]: add ZnsUuidHelper utility for UUID format conversion See merge request zstackio/zstack!9467
Resolves: ZCF-1365 Change-Id: I696f6a6a6e6d7867746e70736d6d646861686166
<fix>[rest]: fix bug<description> See merge request zstackio/zstack!9500
Resolves: ZCF-1365 Change-Id: I73637569786c6d6e6d6479646961726365737074
<fix>[rest]: improve markdown validation error reporting See merge request zstackio/zstack!9529
Resolves: ZCF-2063 Change-Id: Ib7ed3b9a111bad1ec0d6f76ece259bfd25444bb7
<fix>[zns]: add ZNS error code ORG_ZSTACK_NETWORK_ZNS_10011 See merge request zstackio/zstack!9584
AttachL2NetworkToCluster incorrectly blocks ZNS L2
networks. Three validation points assume all L2s use
host physical interfaces. ZNS is an SDN controller,
physicalInterface is always "" and meaningless.
Fix 1: L2NetworkApiInterceptor - skip vSwitchType
conflict check for ZNS L2
Fix 2: KVMApiInterceptor - skip vlan device name
length check for ZNS L2
Fix 3: L2NoVlanNetwork - skip physicalInterface
conflict for ZNS L2NoVlan, exclude ZNS
L2Vlan from non-SDN overlap query
Resolves: ZCF-2073
Change-Id: I757966777a64736a6d646178786e6b70617a7a77
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
<fix>[l2]: skip ZNS L2 in attach interceptors See merge request zstackio/zstack!9589
Handle blank or omitted type in APIChangeL2NetworkVlanIdMsg by normalizing to the current L2 type before validation checks. Resolves: ZSTAC-2074 Change-Id: Icf960d0766b726047d8613305042cfa14e120c61
<fix>[network]: normalize ZNS L2 change target type handling See merge request zstackio/zstack!9613
Release SDN NICs before removing VmNicVO. Keep NIC deletion on release failure. Resolves: ZCF-2047 Change-Id: I83f534ea19849467a728e3b6fb9ee2f6bb43bb7e Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
<doc>[sdnController]: integraion zns into cloud See merge request zstackio/zstack!9574
Add error codes 10020-10024 for ZNS DHCP service operations in CloudOperationsErrorCode. Resolves: ZCF-2132 Change-Id: I4da4f433a58827acf33e98bde8801fa2f1110248
- New AfterUpdateIpRangeExtensionPoint interface - New AfterUpdateVmNicMacExtensionPoint interface - L3BasicNetwork: dispatch afterUpdateIpRange after DB update - VmInstanceExtensionPointEmitter: dispatch afterUpdateVmNicMac - VmInstanceBase: fix Stopped branch missing afterChangeVmNicState - MevocoVmInstanceBase: call afterUpdateVmNicMac after MAC update Resolves: ZCF-2732 Change-Id: Ibc1e23da203b42e280e1fe1c23e3434b8787b5a8
<feature>[zns]: add ZNS DHCP error codes See merge request zstackio/zstack!9631
<fix>[zns]: add extension points for IpRange and NIC MAC update See merge request zstackio/zstack!9635
Resolves: ZCF-2734 Change-Id: If566219badf46592e2bdcd0cbdd3ea43fc45f6e3
<feature>[zns]: add ZNS error code 10019 See merge request zstackio/zstack!9619
Why - VM/VMNic lifecycle extension points are scattered across 6+ interfaces (Pre/Release/Migrate/NIC attach-detach/Host heartbeat); every new OvsKvmBackend-style NIC-bound feature duplicates ~850 lines of boilerplate across those extension points. What (Phase 1 foundation only) - New VmNicLifecycleExtensionPoint interface (header/vm) with 7 methods unifying setup/cleanup/migrate/reconcile semantics. - New VmNicLifecycleManager skeleton (compute/vm) implementing 5 legacy extension points; methods are pass-through success()/done() in PH-1. - New VmNicLifecycleKvmBridge skeleton (plugin/kvm) implementing KVMPingAgentNoFailureExtensionPoint; pass-through done() in PH-1. - New VmNicLifecycleGlobalConfig and conf/globalConfig/vmNicLifecycle .xml declaring reconcileOnHost.timeout (default 30s). - Register two Spring beans in VmInstanceManager.xml and Kvm.xml. Side effects - Zero: skeleton methods are pass-through, legacy extension points are unchanged; the new interface is an opt-in abstraction layer. - Full routing logic will be filled in by PH-2 (Manager) and PH-3 (Bridge). Resolves: ZCF-2893 Change-Id: I330238e91d18c727db106af7057f89f331e082eb
Include VmNicLifecycleExtension.xml in KvmTest.springSpec so the
stub bean is registered when TestCaseStabilityTest pre-builds the
Spring context (it uses ZStackTest.springSpec; the sub-case's
spring{include()} block is silently ignored in that mode).
Default applicable=false so the stub is inert across all other KVM
tests; each vmnic case calls ext.reset() which restores applicable=true
before running its scenario.
http://jira.zstack.io/browse/ZCF-2893
Change-Id: I4e54753a0377010beb0d706c167424f7e68ffa39
Allow commit-msg hook to recognize ZCF-XXXX and AIOS-XXXX as valid jira links so that commits referencing these projects no longer trigger the missing-jira-link warning. Resolves: ZCF-2893
Add IPv6 support for ZStack management network (milestone M1). New files: - IPv6Utils: buildUrl/bracketIpv6/normalizeIpv6/isValidManagementIp - NetworkGlobalConfig: management.server.prefer.ipv6 (default false) Modified: - Platform: getManagementServerIp() prefer IPv4/IPv6 via GlobalConfig; getManagementServerCidr() IPv6 CIDR; jgroupsAddr() JGroups bracket fix; volatile fields for JMM correctness - RESTFacadeImpl: callbackUrl uses IPv6Utils.buildUrl(); Pattern static final - HostApiInterceptor: accept + normalize IPv6 management IP - ApplianceVmFacadeImpl: getMnIpForVr() CIDR matching for dual-stack MN Tests (TP-001~031): IPv6UtilsCase, MnIpv6Case, KvmHostIpv6Case, ApplianceVmIpv6Case Resolves: ZSTAC-79206 Change-Id: I3fec17c211d84c82a84e6711b7d09eea
New utility methods: - IPv6Utils.buildAddr(ip, port): format Ceph monAddr with IPv6 brackets - NetworkUtils.isIpInCidr(ip, cidr): dual-stack CIDR matching F-010: KVMHost.getDataNetworkAddress() uses isIpInCidr for IPv6 storage CIDR F-011: CephBackupStorageMonBase/CephPrimaryStorageMonBase use IPv6Utils.buildAddr F-012: KVMHost.checkMigrateNetworkCidrOfHost() uses isIpInCidr for IPv6 migration F-013: VxlanPoolApiInterceptor accepts IPv6 VTEP addresses F-014a: ZSha2Helper.isMaster() grep pattern fixed for IPv6 VIP detection NFS: NfsApiParamChecker.isValidStorageCidr supports IPv6 CIDR format Resolves: ZSTAC-79206 Change-Id: Idc7022fbdfce429ca8795f3522df682b
MnIpv6StorageMigrationCase: TP-032~038 storage/migration CIDR dual-stack VxlanIpv6Case: TP-039~041 VXLAN vtep IPv6 support ZSha2Ipv6Case: TP-042~046 HA zsha2 IPv6 SSH/nginx/URL/grep Resolves: ZSTAC-79206 Change-Id: Ia4e1480db3514b9c9f9e0e24aff2310d
- KvmHostIpmiPowerExecutor: remove IPv4-only guard for IPMI (F-025) - ConsoleManagerImpl: bracketIpv6() for console proxy hostname (F-026) - KVMAgentCommands/KVMHost: vncListenAddress='::' for IPv6 host (F-027) Resolves: ZSTAC-79206 Change-Id: I3f8a901c7d2e4b6f5c1a8d3e9f2b7c0d1e4a5f6
Add MnIpv6M3Case and MnIpv6M4Case covering: - TP-062~069,076,077: IPMI IPv6, Console Proxy bracket, BM DPU callback, COLO qemu URL - TP-083~089: ZWatch InfluxDB/Prometheus/Grafana URL, License HTTPS URL, Keycloak container name, SSO CAS login URL Resolves: ZSTAC-79206 Change-Id: If19fdab6ef4f6a7f8d9a16fb97c1ee29365197b2
Walkthrough此次变更引入 IPv6 支持、VM NIC 生命周期管理框架和 SDN NIC 分配/释放扩展点,重构 NIC 分配流程并添加多处插件回调与测试覆盖,涉及 compute、network、core、plugin(kvm、sdn、ceph 等)、utils、header、docs 与大量测试代码与 DB 迁移脚本。 Changes
Sequence Diagram(s)sequenceDiagram
participant Client as VM 实例化流程
participant Allocate as VmAllocateNicFlow
participant DB as 数据库
participant Plugin as PluginRegistry/扩展实现
participant SDNFlow as VmAllocateSdnNicFlow
participant Hypervisor as KVM/主机
Client->>Allocate: 请求分配 NIC
Allocate->>DB: persist VmNicVO(保证 UUID)
Allocate->>Plugin: 调用 BeforeAllocateVmNic 扩展(异步 Completion)
Allocate->>Allocate: 分配 IP / 记录 UsedIp(如适用)
Allocate->>SDNFlow: 触发 SDN NIC 分配(传 spec + nics)
SDNFlow->>Plugin: 遍历 AfterAllocateSdnNicExtensionPoint,调用 afterAllocateSdnNic(...)
Plugin-->>SDNFlow: 返回 success/fail(聚合)
SDNFlow->>DB: 持久化/更新 SDN 相关记录
SDNFlow-->>Allocate: 完成(成功则 trigger.next,否则 trigger.fail)
Allocate->>Hypervisor: 应用 NIC 配置 并 继续后续步骤
sequenceDiagram
participant Host as KVM 主机(Ping)
participant Bridge as VmNicLifecycleKvmBridge
participant Plugin as VmNicLifecycleExtensionPoint 实现
participant DB as 数据库(VmInstance/VmNic)
Host->>Bridge: kvmPingAgentNoFailure(host)
Bridge->>Plugin: 查询注册扩展(若无则短路)
Bridge->>DB: 查询主机上期望存在的 Running VM 的 VmNic 列表
Bridge->>Plugin: 对每个扩展过滤适用的 NICs(isApplicable)
Plugin->>Plugin: 执行 reconcileOnHost(matchedNics)(带超时 guard)
Plugin-->>Bridge: 完成或记录警告(best-effort)
Bridge-->>Host: 完成(不阻塞主机 ping 流程)
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 分钟 诗
✨ Finishing Touches🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
Actionable comments posted: 10
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
plugin/sdnController/src/main/java/org/zstack/sdnController/SdnControllerManagerImpl.java (1)
26-29:⚠️ Potential issue | 🟡 Minor存在重复的 import 语句。
Lines 27-29 重复导入了
L3NetworkInventory、L3NetworkVO和SdnControllerL3,这些已在 Lines 26 和 30 通过通配符或显式方式导入。🧹 建议删除重复的 import
import org.zstack.header.network.l3.*; -import org.zstack.header.network.l3.L3NetworkInventory; -import org.zstack.header.network.l3.L3NetworkVO; -import org.zstack.header.network.l3.SdnControllerL3; import org.zstack.header.network.sdncontroller.*;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@plugin/sdnController/src/main/java/org/zstack/sdnController/SdnControllerManagerImpl.java` around lines 26 - 29, Remove the duplicate explicit imports of L3NetworkInventory, L3NetworkVO and SdnControllerL3 that are redundant with the existing org.zstack.header.network.l3.* wildcard import in SdnControllerManagerImpl; keep either the wildcard import or the explicit type imports (preferably the wildcard or only the specific types actually used) and delete the redundant lines so each type is imported exactly once.
🟡 Minor comments (27)
rest/src/main/resources/scripts/RestDocumentationGenerator.groovy-790-799 (1)
790-799:⚠️ Potential issue | 🟡 Minor不要用 markdown 内容反推报错路径。
这里的
mdPath是从md.globalConfig.category/name重新拼出来的;一旦真正不一致的就是category或name,报错里给出的路径就不是当前正在校验的文件,汇总输出会把人带到错误位置。建议把调用方的mdPath直接传进isConsistent(...)。💡 建议修改
- List<String> isConsistent(GlobalConfigMarkDown md, GlobalConfig globalConfig) { + List<String> isConsistent(String mdPath, GlobalConfigMarkDown md, GlobalConfig globalConfig) { if (md == null) { return ["GlobalConfigMarkDown is null"] } if (globalConfig == null) { return ["GlobalConfig is null"] } - String mdPath = - PathUtil.join(PathUtil.join(Paths.get("../doc").toAbsolutePath().normalize().toString(), - "globalconfig"), md.globalConfig.category, md.globalConfig.name) + ".md" List<String> classes = new ArrayList<>() initializer.bindResources.get(globalConfig.getIdentity()).each { classes.add(it.getName()) } List<String> newClasses = classes.sort() String validatorString = initializer.validatorMap.get(globalConfig.getIdentity())- List<String> inconsistencies = isConsistent(markDown, globalConfig) + List<String> inconsistencies = isConsistent(mdPath, markDown, globalConfig)Also applies to: 862-862
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@rest/src/main/resources/scripts/RestDocumentationGenerator.groovy` around lines 790 - 799, The method isConsistent(GlobalConfigMarkDown md, GlobalConfig globalConfig) currently reconstructs mdPath from md.globalConfig.category/name which can mislead error reporting when those fields are wrong; change the signature to accept the caller-provided mdPath (e.g., isConsistent(GlobalConfigMarkDown md, GlobalConfig globalConfig, String mdPath)) and use that mdPath for all error messages instead of recomputing it inside the method (also update the other overloaded call site noted around the second occurrence to pass the original mdPath through). Ensure all callers of isConsistent are updated to pass the existing path and remove the internal PathUtil.join logic.utils/src/main/java/org/zstack/utils/network/NetworkUtils.java-561-563 (1)
561-563:⚠️ Potential issue | 🟡 Minor新增注释请改成英文。
这里的 Javadoc 是中文,和仓库的统一规范不一致,后续生成文档和跨团队协作时也会不一致。
As per coding guidelines "代码里不应当有有中文,包括报错、注释等都应当使用正确的、无拼写错误的英文来写".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@utils/src/main/java/org/zstack/utils/network/NetworkUtils.java` around lines 561 - 563, The Javadoc above the method in NetworkUtils that checks whether an IP (IPv4 or IPv6) belongs to a specified CIDR is written in Chinese; update that Javadoc to English to match repository conventions. Locate the comment in class NetworkUtils (the doc for the method that performs the IP-in-CIDR check, e.g., isIpInCidr or equivalent) and replace the Chinese text with a clear, concise English description (mentioning IP, IPv4/IPv6, and CIDR) and ensure wording follows project style (no Chinese, correct spelling/grammar).header/src/main/java/org/zstack/header/vm/AfterAllocateVmNicIpExtensionPoint.java-17-18 (1)
17-18:⚠️ Potential issue | 🟡 Minor扩展点方法缺少方法级契约注释。
建议在
afterAllocateVmNicIp上补充方法级 Javadoc(触发时机、失败回滚语义、completion 责任),降低实现歧义。As per coding guidelines: `接口方法不应有多余的修饰符(例如 public),且必须配有有效的 Javadoc 注释。`✍️ 建议修改
public interface AfterAllocateVmNicIpExtensionPoint { + /** + * Called after NIC IP allocation is persisted. + * + * `@param` spec current VmInstanceSpec with allocated NIC/IP data + * `@param` completion callback that must be completed by implementation + */ void afterAllocateVmNicIp(VmInstanceSpec spec, Completion completion); }🤖 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/AfterAllocateVmNicIpExtensionPoint.java` around lines 17 - 18, The interface method afterAllocateVmNicIp in AfterAllocateVmNicIpExtensionPoint lacks a Javadoc and uses an unnecessary public modifier; remove the redundant "public" modifier from the interface method signature and add a method-level Javadoc on afterAllocateVmNicIp(VmInstanceSpec spec, Completion completion) that documents: when the extension is triggered (timing relative to NIC/IP allocation), failure/rollback semantics (whether implementors must undo changes or rely on caller rollback), and the Completion responsibility (when to call success() vs fail(), thread/context expectations and that async work must complete before signaling). Ensure the doc follows project Javadoc style and mentions parameter roles for VmInstanceSpec and Completion.header/src/main/java/org/zstack/header/vm/AfterReleaseVmNicExtensionPoint.java-10-11 (1)
10-11:⚠️ Potential issue | 🟡 Minor接口方法建议补充方法级 Javadoc。
当前只有接口级注释,建议在方法上补充参数语义与
Completion回调约定,避免扩展实现方误用。As per coding guidelines: `接口方法不应有多余的修饰符(例如 public),且必须配有有效的 Javadoc 注释。`✍️ 建议修改
public interface AfterReleaseVmNicExtensionPoint { + /** + * Called after VmNic row is deleted from DB. + * + * `@param` nic deleted nic inventory snapshot + * `@param` completion callback that must be completed by implementation + */ void afterReleaseVmNic(VmNicInventory nic, Completion completion); }🤖 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/AfterReleaseVmNicExtensionPoint.java` around lines 10 - 11, Add a method-level Javadoc to AfterReleaseVmNicExtensionPoint.afterReleaseVmNic(VmNicInventory nic, Completion completion) describing the semantics of 'nic' (which VM NIC fields are guaranteed/should be used), the expected behavior callers/extensions must implement, and the Completion callback contract (when to call completion.success() vs completion.fail(Throwable) and that it must always be invoked). Ensure the method declaration itself has no redundant modifiers (leave it as a plain interface method) and include `@param` tags for 'nic' and 'completion' and an `@see` or brief note linking to Completion's contract.utils/src/main/java/org/zstack/utils/network/IPv6Utils.java-16-118 (1)
16-118:⚠️ Potential issue | 🟡 Minor请将注释改为英文并保持拼写规范。
该新增工具类注释中包含中文,违反仓库统一规范。
As per coding guidelines:
代码里不应当有有中文,包括报错、注释等都应当使用正确的、无拼写错误的英文来写。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@utils/src/main/java/org/zstack/utils/network/IPv6Utils.java` around lines 16 - 118, Translate all Chinese comments in the IPv6Utils class to clear, idiomatic English and fix spelling/grammar, keeping the original examples and behavior; update Javadoc for methods buildUrl, buildHttpsUrl, bracketIpv6, normalizeIpv6, buildAddr and the class-level description so comments are entirely English and accurately describe behavior (e.g., "Construct HTTP URL; add brackets for IPv6", "Idempotently add brackets for IPv6 addresses", "Normalize IPv6 to RFC 5952 compressed form, strip zone id", "Format ip:port for Ceph monAddr"), and ensure thrown IllegalArgumentException message in normalizeIpv6 is English as well; do not change logic in methods (buildUrl, buildHttpsUrl, bracketIpv6, normalizeIpv6, buildAddr), only replace comment text and exception message text to English.test/src/test/groovy/org/zstack/test/integration/network/vxlan/VxlanIpv6Case.groovy-12-103 (1)
12-103:⚠️ Potential issue | 🟡 Minor请将新增注释与说明统一改为英文。
当前新增注释包含中文,不符合仓库的统一语言规范。
As per coding guidelines:
代码里不应当有有中文,包括报错、注释等都应当使用正确的、无拼写错误的英文来写。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/src/test/groovy/org/zstack/test/integration/network/vxlan/VxlanIpv6Case.groovy` around lines 12 - 103, Replace all Chinese comments, assertion messages and logger strings in class VxlanIpv6Case with English equivalents: update the class-level Javadoc and the inline comments in methods testVxlanAcceptsIpv6VtepIp, testVtepVoColumnLength, checkVtepIpColumnLength, and testInvalidVtepIpRejected; change assertion messages (e.g. "TP-039: ...", "TP-040: ...", "TP-041: ...") and logger.info messages to clear English wording while preserving the TP-xxx identifiers and meaning; ensure no Chinese characters remain in field names, variable names or strings used for assertions/logging.test/src/test/groovy/org/zstack/test/integration/utils/IPv6UtilsCase.groovy-6-92 (1)
6-92:⚠️ Potential issue | 🟡 Minor测试注释请统一为英文。
新增测试注释包含中文,不符合仓库语言规范。
As per coding guidelines:
代码里不应当有有中文,包括报错、注释等都应当使用正确的、无拼写错误的英文来写。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/src/test/groovy/org/zstack/test/integration/utils/IPv6UtilsCase.groovy` around lines 6 - 92, The test file IPv6UtilsCase contains Chinese comments and messages; update all human-readable text to English by replacing class/method block comments and assertion messages in methods testBuildUrlIpv4, testBuildUrlIpv6, testBracketIpv6Idempotent, testNormalizeIpv6, testIsValidMnIpLinkLocal, testIsValidMnIpInvalid, and the TP-014 placeholder with clear, correct English (e.g., convert the Chinese descriptions and assert messages to concise English), ensuring no Chinese characters remain in comments, strings, or assertion messages while keeping the existing method names and test logic unchanged.test/src/test/groovy/org/zstack/test/integration/core/MnIpv6M3Case.groovy-8-218 (1)
8-218:⚠️ Potential issue | 🟡 Minor这份测试里的中文注释和输出文案需要清掉。
目前类注释、方法注释、断言消息和日志基本都用了中文,和仓库要求的英文代码文本不一致。
As per coding guidelines "代码里不应当有有中文,包括报错、注释等都应当使用正确的、无拼写错误的英文来写".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/src/test/groovy/org/zstack/test/integration/core/MnIpv6M3Case.groovy` around lines 8 - 218, The file contains Chinese comments and messages that must be converted to English; update the class-level Javadoc for MnIpv6M3Case, all method comments (e.g., testIpmiIpv6AcceptedInInterceptor, testIpmiAddressFullLengthIpv6, testIpmiInvalidAddressRejected, testConsoleBracketIpv6, testConsoleVncTokenUrl, testConsoleDualStackVip, testBmDpuCallbackIpBracket, testColoQemuUrlIpv6Bracket), all assertion messages and logger.info strings to concise, correct English equivalents (keep the original intent/phrasing but in English), and ensure no Chinese remains in any string literals; preserve test logic and identifiers (NetworkUtils, IPv6NetworkUtils, IPv6Utils, variable names like fullIpv6, bracketed, vncAddr, consoleUrl, callbackUrl, builtUrl) and correct any spelling mistakes while doing the replacements.test/src/test/groovy/org/zstack/test/integration/network/ipv6/MnIpv6StorageMigrationCase.groovy-7-135 (1)
7-135:⚠️ Potential issue | 🟡 Minor测试文件里引入了大量中文注释、日志和断言文案。
仓库规则要求代码文本统一使用英文,这里整份用例都偏离了这个约定,后续检索、模板生成和统一维护都会变差。建议把注释、
logger.info(...)和断言消息统一改成英文。As per coding guidelines "代码里不应当有有中文,包括报错、注释等都应当使用正确的、无拼写错误的英文来写".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/src/test/groovy/org/zstack/test/integration/network/ipv6/MnIpv6StorageMigrationCase.groovy` around lines 7 - 135, The file MnIpv6StorageMigrationCase contains Chinese comments, logger messages and assertion messages; replace all Chinese text with clear, correct English across class-level Javadoc, method comments (e.g., testNfsCidrIpv6NotRejected, testIsIpInCidrIpv6Match, testIsIpInCidrNoMatchFallback, testBuildAddrIpv6BracketFormat, testCephMonAddrIpv6Format, testCheckMigrateNetworkCidrFallback), all logger.info(...) messages and all assertion message strings so they are English, concise and free of typos; keep the original test logic and string values (IPv6 samples) unchanged, and ensure assertion messages and log lines accurately describe the TP-032...TP-038 checks for easier future search and template generation.test/src/test/groovy/org/zstack/test/integration/kvm/host/KvmHostIpv6Case.groovy-14-172 (1)
14-172:⚠️ Potential issue | 🟡 Minor这里的注释和测试文案需要统一改成英文。
当前新增用例里的类注释、行内注释、断言消息和日志都包含中文,不符合仓库的统一文本规范。
As per coding guidelines "代码里不应当有有中文,包括报错、注释等都应当使用正确的、无拼写错误的英文来写".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/src/test/groovy/org/zstack/test/integration/kvm/host/KvmHostIpv6Case.groovy` around lines 14 - 172, Replace all Chinese texts in the KvmHostIpv6Case class (class-level Javadoc and all inline comments, assertion messages, and logger messages) with clear English equivalents; specifically update the class comment and messages inside methods testManagementIpColumnLength, testAddHostWithIpv6Passes, testFullIpv6NormalizedBeforeConnect, testLinkLocalIpv6Rejected, testInvalidIpRejected, and testFullIpv6FitsInColumn so assertions and logger.info use English phrases (e.g., "managementIp should have `@Column` annotation", "IPv6 address should pass validation", "full-expanded IPv6 should normalize and pass validation", "link-local IPv6 should be rejected", "invalid IP format should be rejected", "column length >= 39, no truncation for full-expanded IPv6") and ensure spelling/grammar are correct and consistent with repository guidelines.test/src/test/groovy/org/zstack/test/integration/network/ipv6/MnIpv6StorageMigrationCase.groovy-47-60 (1)
47-60:⚠️ Potential issue | 🟡 MinorTP-032 现在没有覆盖真实的 NFS 校验链路。
这个用例只验证了
NetworkUtils.isIpInCidr(),并没有经过 NFS 主存储创建/API interceptor 路径。若回归点出在 NFS 参数校验而不是 CIDR 工具方法,这里仍会通过,和用例标题描述不一致。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/src/test/groovy/org/zstack/test/integration/network/ipv6/MnIpv6StorageMigrationCase.groovy` around lines 47 - 60, The test currently only calls NetworkUtils.isIpInCidr and doesn't exercise the NFS primary storage creation/validation path; update the MnIpv6StorageMigrationCase.testNfsCidrIpv6NotRejected to perform an actual NFS primary-storage create through the system API/interceptor (instead of only calling NetworkUtils.isIpInCidr) using the IPv6 CIDR "2001:db8::/64" and IPv6 address "2001:db8::1", assert that the create call succeeds (no INVALID_ARGUMENT_ERROR) and that the created PrimaryStorageInventory reflects the provided CIDR/IP, and ensure proper cleanup of the created storage; keep references to NetworkUtils.isIpInCidr only for a secondary sanity check if desired.test/src/test/java/org/zstack/test/compute/vmnic/VmNicLifecycleManagerDispatchTest.java-378-397 (1)
378-397:⚠️ Potential issue | 🟡 Minor测试名声明校验顺序,但断言并未覆盖顺序。
这里创建并传入了
seq,但最终只验证了三个实现都被调用一次,没有比较实际先后。若分发顺序被打乱,这个用例依然会通过。建议显式断言a/b/c的调用序号,或者把测试名改成只描述“全部实现都会被调用”。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/src/test/java/org/zstack/test/compute/vmnic/VmNicLifecycleManagerDispatchTest.java` around lines 378 - 397, The test multiImpl_invocation_order_follows_registration_order currently only checks that each MockNicLifecycle (a, b, c) has one failedMigrateCalls entry but does not assert invocation order; update the test to assert order by verifying the sequence numbers captured via the shared AtomicInteger seq (or by checking the timestamps/indices recorded in each MockNicLifecycle.failedMigrateCalls) so that a was invoked first, then b, then c (e.g., assert a recorded 1, b recorded 2, c recorded 3), or alternatively rename the test to reflect only "all implementations are invoked" if you prefer not to check order; ensure you reference multiImpl_invocation_order_follows_registration_order, MockNicLifecycle, seq, failedMigrateCalls, and mgr.failedToMigrateVm when making the change.test/src/test/java/org/zstack/test/compute/vmnic/VmNicLifecycleKvmBridgeTest.java-63-65 (1)
63-65:⚠️ Potential issue | 🟡 Minor这里的注释比断言覆盖范围更大。
当前只验证了
thdf没有被调用,无法证明 “no DB call”。这条注释会让后续读代码的人误以为查询路径也被覆盖了;建议把注释收窄到已断言的行为,或补上对应验证。As per coding guidelines "对于较长的注释,需要仔细校对并随代码更新,确保内容正确。"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/src/test/java/org/zstack/test/compute/vmnic/VmNicLifecycleKvmBridgeTest.java` around lines 63 - 65, The comment "no DB call, no thread facade call" overclaims; update it to reflect only what is asserted (replace with "no thread facade call") or add an explicit verification for the database mock used in this test so the DB absence is covered; specifically keep the Assert.assertTrue(done.get()) and Mockito.verifyNoInteractions(thdf) as-is and either change the comment to reference only the verified behavior or add a Mockito.verifyNoInteractions(<your DB mock variable>) / Mockito.verifyZeroInteractions(<your DB mock variable>) to assert no DB calls occurred.test/src/test/java/org/zstack/test/compute/vmnic/VmNicLifecycleManagerDispatchTest.java-136-150 (1)
136-150:⚠️ Potential issue | 🟡 Minor这个用例没有真正校验 NIC 过滤结果。
当前只断言了调用发生且目标主机是
h-dst,但没有校验传给扩展的 NIC 集合里是否只剩l3-a的两块 NIC。若管理器错误地把n2一并传下去,这个测试仍会通过。建议补充对 NIC 数量/UUID 的断言,或让MockNicLifecycle记录preMigrate的 NIC 参数。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/src/test/java/org/zstack/test/compute/vmnic/VmNicLifecycleManagerDispatchTest.java` around lines 136 - 150, The test currently only checks that preMigrate was called and the destination host, but not which NICs were passed; update the test to assert the NIC collection passed into the MockNicLifecycle.preMigrate invocation contains only the two l3-a NICs. Modify or use MockNicLifecycle to record the NIC list (e.g., a preMigrateNics field captured during preMigrate), then after mgr.preMigrateVm(...) assert m.preMigrateNics.size()==2 and that it contains the UUIDs of "n1" and "n3" (and does not contain "n2"); keep existing checks for preMigrateCalls and host.core/src/main/java/org/zstack/core/rest/RESTFacadeImpl.java-994-994 (1)
994-994:⚠️ Potential issue | 🟡 Minor行内注释应使用英文。
建议的修复
- // 检测裸 IPv6 URL 模式:scheme://hex:chars:port/path(IP 未被方括号包裹) + // Detect bare IPv6 URL pattern: scheme://hex:chars:port/path (IP not wrapped in brackets)As per coding guidelines:
**/*.*: 代码里不应当有有中文,包括报错、注释等都应当使用正确的、无拼写错误的英文来写🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@core/src/main/java/org/zstack/core/rest/RESTFacadeImpl.java` at line 994, Replace the Chinese inline comment in RESTFacadeImpl near the IPv6 detection logic with English; specifically change the comment "// 检测裸 IPv6 URL 模式:scheme://hex:chars:port/path(IP 未被方括号包裹)" to an English equivalent such as "// Detect bare IPv6 URL pattern: scheme://hex:chars:port/path (IP not enclosed in brackets)" and scan RESTFacadeImpl for any other Chinese comments or messages and convert them to proper English.core/src/main/java/org/zstack/core/rest/RESTFacadeImpl.java-984-989 (1)
984-989:⚠️ Potential issue | 🟡 Minor注释应使用英文,不要使用中文。
根据编码规范要求,代码中的注释应当使用英文。当前 Javadoc 和行内注释使用了中文,需要改为英文。
建议的修复
- /** - * 检测并修复裸 IPv6(无方括号)的 callbackUrl。 - * 正常路径下 URL 应由 {`@link` IPv6Utils#buildUrl} 生成,此方法作为兜底防御层。 - * <p> - * 示例:http://2001:db8::1:8080/path → http://[2001:db8::1]:8080/path - */ + /** + * Detects and corrects bare IPv6 (without brackets) in callbackUrl. + * Normally URLs should be generated by {`@link` IPv6Utils#buildUrl}; this method acts as a fallback defense layer. + * <p> + * Example: http://2001:db8::1:8080/path → http://[2001:db8::1]:8080/path + */ private static String sanitizeCallbackUrl(String url) {As per coding guidelines:
**/*.*: 代码里不应当有有中文,包括报错、注释等都应当使用正确的、无拼写错误的英文来写🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@core/src/main/java/org/zstack/core/rest/RESTFacadeImpl.java` around lines 984 - 989, Translate the Chinese Javadoc block in RESTFacadeImpl that documents the method which detects and fixes bare IPv6 (no brackets) callback URLs into clear English; update the comment above the method referenced (the Javadoc mentioning IPv6Utils#buildUrl) to describe that this is a fallback that ensures URLs like "http://2001:db8::1:8080/path" are converted to "http://[2001:db8::1]:8080/path", and ensure wording is grammatically correct and free of Chinese characters.plugin/applianceVm/src/main/java/org/zstack/appliancevm/ApplianceVmFacadeImpl.java-405-407 (1)
405-407:⚠️ Potential issue | 🟡 Minor行内注释应使用英文。
建议的修复
} catch (Exception ignore) { - // CIDR 类型与 IP 类型不匹配时忽略 + // Ignore when CIDR type does not match IP type }As per coding guidelines:
**/*.*: 代码里不应当有有中文,包括报错、注释等都应当使用正确的、无拼写错误的英文来写🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@plugin/applianceVm/src/main/java/org/zstack/appliancevm/ApplianceVmFacadeImpl.java` around lines 405 - 407, Replace the Chinese inline comment in the catch block inside ApplianceVmFacadeImpl (the catch (Exception ignore) that currently reads "// CIDR 类型与 IP 类型不匹配时忽略") with an English comment such as "// ignore when CIDR and IP types do not match" (or similar clear English wording); ensure the comment is accurate and follows coding guidelines for English-only comments in ApplianceVmFacadeImpl.plugin/applianceVm/src/main/java/org/zstack/appliancevm/ApplianceVmFacadeImpl.java-365-371 (1)
365-371:⚠️ Potential issue | 🟡 MinorJavadoc 注释应使用英文。
根据编码规范要求,代码注释应当使用英文而非中文。
建议的修复
- /** - * F-009: 从本机网卡中选取与 VR 管理网 CIDR 同子网的 MN IP。 - * 支持 IPv4/IPv6 双栈;若无匹配则回退到 Platform.getManagementServerIp()。 - * - * `@param` vrManagementCidr VR 管理网的网络 CIDR,例如 "192.168.1.0/24" 或 "2001:db8::/64" - * `@return` 选定的 MN IP(裸地址,无括号) - */ + /** + * F-009: Select MN IP from local interfaces that matches the VR management CIDR. + * Supports IPv4/IPv6 dual-stack; falls back to Platform.getManagementServerIp() if no match. + * + * `@param` vrManagementCidr VR management network CIDR, e.g., "192.168.1.0/24" or "2001:db8::/64" + * `@return` selected MN IP (bare address, no brackets) + */ private String getMnIpForVr(String vrManagementCidr) {As per coding guidelines:
**/*.*: 代码里不应当有有中文,包括报错、注释等都应当使用正确的、无拼写错误的英文来写🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@plugin/applianceVm/src/main/java/org/zstack/appliancevm/ApplianceVmFacadeImpl.java` around lines 365 - 371, Replace the Chinese Javadoc block above the method in ApplianceVmFacadeImpl that selects the MN IP from local NICs matching the VR management CIDR with an equivalent English Javadoc: describe the behavior (select MN IP from host NIC in same subnet as the VR management CIDR, supports IPv4/IPv6 dual stack, fallback to Platform.getManagementServerIp()), keep the `@param` vrManagementCidr description (e.g. "VR management network CIDR, e.g. \"192.168.1.0/24\" or \"2001:db8::/64\"") and the `@return` description ("selected MN IP, bare address without brackets"), and ensure no Chinese text remains in the comment.plugin/applianceVm/src/main/java/org/zstack/appliancevm/ApplianceVmFacadeImpl.java-529-530 (1)
529-530:⚠️ Potential issue | 🟡 Minor行内注释应使用英文。
建议的修复
- // F-009: 根据 VR 管理网 CIDR 选取与其同子网的 MN IP,支持多 IP/双栈主机 + // F-009: Select MN IP on the same subnet as VR management CIDR, supports multi-IP/dual-stack hosts String vrMgmtCidr;As per coding guidelines:
**/*.*: 代码里不应当有有中文,包括报错、注释等都应当使用正确的、无拼写错误的英文来写🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@plugin/applianceVm/src/main/java/org/zstack/appliancevm/ApplianceVmFacadeImpl.java` around lines 529 - 530, Replace the Chinese inline comment in ApplianceVmFacadeImpl (the comment starting with "F-009: 根据 VR 管理网 CIDR 选取与其同子网的 MN IP,支持多 IP/双栈主机") with an accurate English comment, e.g. explain that this block selects MN IPs in the same subnet as the VR management network CIDR and supports multi-IP/dual-stack hosts so the codebase follows the guideline of using English for comments and error text.core/src/main/java/org/zstack/core/Platform.java-895-901 (1)
895-901:⚠️ Potential issue | 🟡 Minor代码注释应使用英文。
♻️ 建议修改
- // F-002: 支持 IPv6 — 枚举所有网卡,按 PREFER_IPV6 配置决定优先级 + // F-002: IPv6 support - enumerate all NICs, prioritize based on PREFER_IPV6 config boolean preferIpv6 = false; try { preferIpv6 = NetworkGlobalConfig.PREFER_IPV6.value(Boolean.class); } catch (Exception ignored) { - // GlobalConfig 可能在静态初始化阶段还未就绪,安全降级为 false + // GlobalConfig may not be ready during static init phase, safely fallback to false }根据编码规范:代码里不应当有中文。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@core/src/main/java/org/zstack/core/Platform.java` around lines 895 - 901, Replace the Chinese inline comment above the preferIpv6 logic in class Platform with an English comment; specifically update the comment that currently reads "F-002: 支持 IPv6 — 枚举所有网卡,按 PREFER_IPV6 配置决定优先级" (and the following Chinese sentence) to an equivalent English description explaining the purpose: that this block checks NetworkGlobalConfig.PREFER_IPV6 to prefer IPv6 when enumerating network interfaces and gracefully defaults to false if GlobalConfig is not ready; keep the try/catch and variable preferIpv6 and the reference to NetworkGlobalConfig.PREFER_IPV6.value(Boolean.class) unchanged.core/src/main/java/org/zstack/core/Platform.java-400-409 (1)
400-409:⚠️ Potential issue | 🟡 Minor代码注释应使用英文。
根据编码规范,代码中不应包含中文,包括注释。请将中文注释改为英文。
♻️ 建议修改
- /** - * F-010: JGroups initial_hosts IPv6 括号修复。 - * IPv6 地址在 JGroups 中须使用 [addr][port] 格式,IPv4 使用 addr[port] 格式。 - */ + /** + * F-010: JGroups initial_hosts IPv6 bracket fix. + * IPv6 addresses in JGroups must use [addr][port] format, IPv4 uses addr[port] format. + */ private static String jgroupsAddr(String ip, String port) {根据编码规范:代码里不应当有中文,包括报错、注释等都应当使用正确的、无拼写错误的英文来写。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@core/src/main/java/org/zstack/core/Platform.java` around lines 400 - 409, Replace the Chinese Javadoc comment above the jgroupsAddr method with an English comment; update the block that currently reads "F-010: JGroups initial_hosts IPv6 括号修复。IPv6 地址在 JGroups 中须使用 [addr][port] 格式,IPv4 使用 addr[port] 格式。" to a clear English equivalent (e.g., "F-010: Fix JGroups initial_hosts brackets for IPv6. IPv6 addresses must use [addr][port] format in JGroups; IPv4 uses addr[port].") so the jgroupsAddr(String ip, String port) method and its behavior are documented in English without changing the method implementation.core/src/main/java/org/zstack/core/Platform.java-827-872 (1)
827-872:⚠️ Potential issue | 🟡 Minor方法中包含中文注释,请改为英文。
多处中文注释需要改为英文:
- Line 828-829: 方法 Javadoc
- Line 837: 示例行注释
- Line 857: 计算网络地址前缀注释
- Line 863: 解析失败注释
♻️ 建议修改示例
- /** - * F-003: 当管理 IP 为 IPv6 时,通过 'ip -6 addr show' 解析对应的网络 CIDR。 - */ + /** + * F-003: When management IP is IPv6, parse the corresponding network CIDR via 'ip -6 addr show'. + */ private static String getManagementServerCidrIpv6(String mnIp) { try { Linux.ShellResult result = Linux.shell("ip -6 addr show"); if (result.getExitCode() != 0) { logger.warn(String.format("failed to run 'ip -6 addr show', exit code: %d", result.getExitCode())); return null; } - // 示例行: " inet6 2001:db8::1/64 scope global" + // Example line: " inet6 2001:db8::1/64 scope global" for (String line : result.getStdout().split("\n")) {根据编码规范:代码里不应当有中文。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@core/src/main/java/org/zstack/core/Platform.java` around lines 827 - 872, Replace the Chinese comments in the getManagementServerCidrIpv6 method with concise English equivalents: update the Javadoc above getManagementServerCidrIpv6 to English describing the method purpose, change the inline example comment (currently showing an example inet6 line) to English, replace the comment before computing the network address prefix (near IPv6Network.fromString(...).getFirst()) with an English note about computing the network/prefix, and change the catch comment that skips the current line on parse failure to an English comment indicating the parser will continue to the next line; keep all logic and referenced symbols (Linux.shell, InetAddress, IPv6Network, logger) unchanged.core/src/main/java/org/zstack/core/Platform.java-804-807 (1)
804-807:⚠️ Potential issue | 🟡 Minor代码注释应使用英文。
♻️ 建议修改
- // F-003: IPv6 管理 IP 走独立分支 + // F-003: IPv6 management IP uses separate branch if (IPv6NetworkUtils.isIpv6Address(mgtIp)) {根据编码规范:代码里不应当有中文。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@core/src/main/java/org/zstack/core/Platform.java` around lines 804 - 807, Replace the Chinese inline comment above the IPv6 branch with an English comment; in Platform.java where the code checks IPv6NetworkUtils.isIpv6Address(mgtIp) and calls getManagementServerCidrIpv6(mgtIp), change the comment "IPv6 管理 IP 走独立分支" to an English equivalent such as "IPv6 management IP follows a separate branch" so all code comments use English and clearly describe the branch handling for mgtIp.test/src/test/groovy/org/zstack/test/integration/core/ZSha2Ipv6Case.groovy-6-17 (1)
6-17:⚠️ Potential issue | 🟡 Minor类和方法注释应使用英文而非中文
与
MnIpv6Case.groovy相同,此文件的类级 Javadoc 和方法注释使用了中文。根据编码规范,所有注释都应使用英文。📝 建议的修改示例
/** - * TP-042~046: ZSha2 高可用 IPv6 支持测试 - * - * 全部为纯单元测试,无需 Spring 上下文。 - * - * 覆盖: - * TP-042 - ZSha2Helper.isMaster() grep pattern " ip/" 正确匹配 IPv6 VIP + * TP-042~046: ZSha2 HA IPv6 Support Tests + * + * Pure unit tests, no Spring context required. + * + * Coverage: + * TP-042 - ZSha2Helper.isMaster() grep pattern " ip/" correctly matches IPv6 VIPAs per coding guidelines: "代码里不应当有有中文,包括报错、注释等都应当使用正确的、无拼写错误的英文来写"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/src/test/groovy/org/zstack/test/integration/core/ZSha2Ipv6Case.groovy` around lines 6 - 17, Replace all Chinese class- and method-level comments in ZSha2Ipv6Case (including the file header block and any method Javadocs) with equivalent English descriptions; mirror the style and wording used in MnIpv6Case.groovy where applicable, ensuring the Javadoc summarizes the test scope (TP-042..TP-046) and each method comment describes its specific test case in clear English, with no Chinese characters remaining.test/src/test/groovy/org/zstack/test/integration/core/MnIpv6Case.groovy-14-34 (1)
14-34:⚠️ Potential issue | 🟡 Minor类和方法注释应使用英文而非中文
根据编码规范,代码中不应包含中文,包括注释、报错信息等都应使用正确的英文。当前文件中存在大量中文注释(如类级 Javadoc、方法注释
// 纯单元 / 静态方法测试,无需 Spring等)。建议将所有中文注释翻译为英文,例如:
📝 建议的修改示例
/** - * TP-001~007, TP-021~024, TP-030~031: 管理节点 IPv6 支持核心测试 - * - * 全部为纯单元 / 反射测试,无需 Spring 上下文。 - * 由 CoreLibraryTest.runSubCases() 自动发现并运行。 - * - * 覆盖: - * TP-001 - NetworkGlobalConfig.PREFER_IPV6 默认值为 false - * ... + * TP-001~007, TP-021~024, TP-030~031: Management Node IPv6 Support Core Tests + * + * Pure unit/reflection tests, no Spring context required. + * Auto-discovered and run by CoreLibraryTest.runSubCases(). + * + * Coverage: + * TP-001 - NetworkGlobalConfig.PREFER_IPV6 default value is false + * ... */As per coding guidelines: "代码里不应当有有中文,包括报错、注释等都应当使用正确的、无拼写错误的英文来写"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/src/test/groovy/org/zstack/test/integration/core/MnIpv6Case.groovy` around lines 14 - 34, Translate all Chinese comments in the MnIpv6Case class to English: replace the class-level Javadoc and any inline or method comments (e.g., descriptions about being pure unit/reflective tests, the TP-001...TP-031 test coverage lines) with clear, idiomatic English while preserving the original meaning and test identifiers; update references to functions and behaviors mentioned (Platform.getManagementServerIp, getManagementServerCidr, sanitizeCallbackUrl, Platform.getManagementServerId, jgroupsAddr) if they appear in comments, ensure spelling and grammar are correct, and keep the test item list and expected behaviors intact so reviewers can still map each TP number to its assertion.test/src/test/groovy/org/zstack/test/integration/appliancevm/ApplianceVmIpv6Case.groovy-10-22 (1)
10-22:⚠️ Potential issue | 🟡 Minor类和方法注释应使用英文而非中文
与其他测试文件一致,此文件的类级 Javadoc 和方法注释使用了中文。根据编码规范,所有注释都应使用英文。
📝 建议的修改示例
/** - * TP-025~029: ApplianceVmFacadeImpl.getMnIpForVr CIDR 匹配逻辑测试 - * - * getMnIpForVr 是私有实例方法,通过反射调用。 - * 不依赖 Spring 上下文(方法内部只使用标准 Java 网络 API 和静态工具方法)。 - * - * 覆盖: - * TP-025 - 使用当前 MN 的 IPv4 CIDR 应返回 MN 的 IP 地址 + * TP-025~029: ApplianceVmFacadeImpl.getMnIpForVr CIDR Matching Logic Tests + * + * getMnIpForVr is a private instance method, invoked via reflection. + * No Spring context dependency (method internally uses only standard Java network APIs and static utility methods). + * + * Coverage: + * TP-025 - Using current MN's IPv4 CIDR should return MN's IP addressAs per coding guidelines: "代码里不应当有有中文,包括报错、注释等都应当使用正确的、无拼写错误的英文来写"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/src/test/groovy/org/zstack/test/integration/appliancevm/ApplianceVmIpv6Case.groovy` around lines 10 - 22, Replace all Chinese Javadoc and method comments in this test file with clear English equivalents while preserving the original meanings of the tests; specifically update the class-level Javadoc in ApplianceVmIpv6Case and any method-level comments that describe the TP-025..TP-029 cases and the use of the private getMnIpForVr via reflection to English, ensuring terminology like "CIDR", "fallback to Platform.getManagementServerIp()", "IPv4/IPv6", and "no square brackets" are used consistently and without spelling errors.test/src/test/groovy/org/zstack/test/integration/core/MnIpv6M4Case.groovy-6-17 (1)
6-17:⚠️ Potential issue | 🟡 Minor类注释和方法注释包含中文,违反编码规范。
根据编码规范,代码中不应包含中文,包括注释都应使用英文。本文件的类级别 Javadoc(第 6-17 行)以及多个方法中的注释(如第 22、27、31、45-52、77-81 行等)均使用了中文,需要改为英文。
📝 建议修改类级别注释为英文
/** - * TP-083~089: 管理节点 IPv6 M4 Premium 支持测试 - * - * 全部为纯单元测试,无需 Spring 上下文。 - * 覆盖以下测试点: - * TP-083 - ZWatch InfluxDB URL 含 IPv6 方括号(buildUrl vs String.format 对比) - * TP-084 - Prometheus remote_write URL 含 IPv6 方括号(路径拼接正确) - * TP-085 - Grafana 数据源 URL 含 IPv6 方括号(buildUrl vs String.format 对比) - * TP-087 - License HTTP URL 含 IPv6 方括号(bracketIpv6 + buildHttpsUrl) - * TP-088 - Keycloak 容器名 IPv6 地址 sanitize(冒号替换为短横线) - * TP-089 - SSO CAS URL 含 IPv6 方括号(bracketIpv6 用于 HTTPS URL 拼接) + * TP-083~089: Management Node IPv6 M4 Premium Support Tests + * + * All tests are pure unit tests, no Spring context required. + * Covers the following test points: + * TP-083 - ZWatch InfluxDB URL with IPv6 brackets (buildUrl vs String.format comparison) + * TP-084 - Prometheus remote_write URL with IPv6 brackets (path concatenation correctness) + * TP-085 - Grafana datasource URL with IPv6 brackets (buildUrl vs String.format comparison) + * TP-087 - License HTTP URL with IPv6 brackets (bracketIpv6 + buildHttpsUrl) + * TP-088 - Keycloak container name IPv6 address sanitize (colon replaced with hyphen) + * TP-089 - SSO CAS URL with IPv6 brackets (bracketIpv6 for HTTPS URL construction) */As per coding guidelines:
**/*.*: 代码里不应当有有中文,包括报错、注释等都应当使用正确的、无拼写错误的英文来写🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/src/test/groovy/org/zstack/test/integration/core/MnIpv6M4Case.groovy` around lines 6 - 17, Replace all Chinese text in the class-level Javadoc and inline comments in the MnIpv6M4Case test class with concise English equivalents: update the class Javadoc at the top of MnIpv6M4Case to describe the IPv6 M4 Premium test coverage in English, and convert all Chinese comments inside each test method (the ones describing TP-083…TP-089, and other inline notes) to clear English phrases; preserve the original intent and technical details but remove any Chinese characters in comments, exception messages or annotations so the class and its test methods contain only English commentary.
🧹 Nitpick comments (11)
header/src/main/java/org/zstack/header/network/l3/L3NetworkInventory.java (1)
426-428: 建议抽取公共判定入口,避免 Inventory/VO 逻辑漂移。这里与
L3NetworkVO.enableIpAddressAllocation()的新增分支是重复实现。建议在L3NetworkType增加统一方法,两个调用点直接复用,降低后续维护分叉风险。♻️ 建议改法(需同步修改 3 个文件)
--- a/header/src/main/java/org/zstack/header/network/l3/L3NetworkType.java +++ b/header/src/main/java/org/zstack/header/network/l3/L3NetworkType.java @@ + public static boolean isIpAddressAllocationEnabled(String typeName) { + L3NetworkType type = types.get(typeName); + return type == null || type.isIpAddressAllocationEnabled(); + } --- a/header/src/main/java/org/zstack/header/network/l3/L3NetworkInventory.java +++ b/header/src/main/java/org/zstack/header/network/l3/L3NetworkInventory.java @@ - if (L3NetworkType.hasType(getType())) { - return L3NetworkType.valueOf(getType()).isIpAddressAllocationEnabled(); - } - return true; + return L3NetworkType.isIpAddressAllocationEnabled(getType()); --- a/header/src/main/java/org/zstack/header/network/l3/L3NetworkVO.java +++ b/header/src/main/java/org/zstack/header/network/l3/L3NetworkVO.java @@ - if (L3NetworkType.hasType(getType())) { - return L3NetworkType.valueOf(getType()).isIpAddressAllocationEnabled(); - } - return true; + return L3NetworkType.isIpAddressAllocationEnabled(getType());🤖 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/network/l3/L3NetworkInventory.java` around lines 426 - 428, The logic deciding whether IP address allocation is enabled is duplicated between L3NetworkInventory (method using getType()) and L3NetworkVO.enableIpAddressAllocation(); add a single public utility method on L3NetworkType (e.g. static boolean isIpAddressAllocationEnabled(String type) or an instance helper) that encapsulates the type check and valueOf logic, then update L3NetworkInventory (the block calling L3NetworkType.hasType/getType/valueOf) and L3NetworkVO.enableIpAddressAllocation() to call that new L3NetworkType method so both reuse the same implementation and avoid divergent logic.plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java (2)
4183-4189: 建议将 ZNS 网桥字面量提取为常量。Line [4184]-[4185] 直接使用
"br-int"、"openvswitch",后续维护和复用成本较高,建议统一为常量。♻️ 建议修改
public class KVMHost extends HostBase implements Host { + private static final String ZNS_BRIDGE_NAME = "br-int"; + private static final String ZNS_BRIDGE_PORT_TYPE = "openvswitch"; @@ - if (L2NetworkConstant.VSWITCH_TYPE_ZNS.equals(l2inv.getvSwitchType())) { - to.setBridgeName("br-int"); - to.setBridgePortType("openvswitch"); + if (L2NetworkConstant.VSWITCH_TYPE_ZNS.equals(l2inv.getvSwitchType())) { + to.setBridgeName(ZNS_BRIDGE_NAME); + to.setBridgePortType(ZNS_BRIDGE_PORT_TYPE); String ifaceId = VmSystemTags.IFACE_ID.getTokenByResourceUuid( nic.getUuid(), VmSystemTags.IFACE_ID_TOKEN);As per coding guidelines “避免使用魔法值(Magic Value):直接使用未经定义的数值或字符串应替换为枚举或常量。”
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java` around lines 4183 - 4189, Extract the hard-coded ZNS bridge values into constants and replace the literals in the KVMHost code: define meaningful constants (e.g., ZNS_BRIDGE_NAME = "br-int" and ZNS_BRIDGE_PORT_TYPE = "openvswitch") and use them where L2NetworkConstant.VSWITCH_TYPE_ZNS is handled (the block that calls to.setBridgeName(...) and to.setBridgePortType(...)); update any related references such as the ifaceId handling (VmSystemTags.IFACE_ID usage) to use these constants so the magic strings are centralized for reuse and maintenance.
4579-4580: 建议将 VNC 监听地址字面量提取为常量。Line [4579] 的
"::"与"0.0.0.0"建议常量化,避免重复硬编码和后续改动遗漏。♻️ 建议修改
public class KVMHost extends HostBase implements Host { + private static final String VNC_LISTEN_ADDRESS_IPV6 = "::"; + private static final String VNC_LISTEN_ADDRESS_IPV4 = "0.0.0.0"; @@ - String vncListenAddr = IPv6NetworkUtils.isIpv6Address(self.getManagementIp()) ? "::" : "0.0.0.0"; + String vncListenAddr = IPv6NetworkUtils.isIpv6Address(self.getManagementIp()) + ? VNC_LISTEN_ADDRESS_IPV6 + : VNC_LISTEN_ADDRESS_IPV4; cmd.setVncListenAddress(vncListenAddr);As per coding guidelines “避免使用魔法值(Magic Value):直接使用未经定义的数值或字符串应替换为枚举或常量。”
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java` around lines 4579 - 4580, Extract the literal VNC listen address strings "::" and "0.0.0.0" into descriptive constants (e.g., IPV6_ANY and IPV4_ANY) in the KVMHost class (or a nearby constants holder), then replace the current inline usage where vncListenAddr is assigned and passed to cmd.setVncListenAddress with those constants to avoid magic values.network/src/main/java/org/zstack/network/l2/L2NetworkApiInterceptor.java (1)
4-4: 未使用的导入。
org.apache.poi.util.StringUtil未被使用,代码中实际使用的是org.apache.commons.lang.StringUtils。♻️ 建议移除未使用的导入
-import org.apache.poi.util.StringUtil;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@network/src/main/java/org/zstack/network/l2/L2NetworkApiInterceptor.java` at line 4, 在 L2NetworkApiInterceptor 类中移除未使用的导入 org.apache.poi.util.StringUtil,因为代码实际使用的是 org.apache.commons.lang.StringUtils;打开文件查找 import org.apache.poi.util.StringUtil 并删除该行,确认项目仍编译通过且所有对 StringUtils 的引用仍指向 org.apache.commons.lang.StringUtils(若不存在则添加该导入)。compute/src/main/java/org/zstack/compute/vm/VmReturnReleaseNicFlow.java (1)
40-43: 未使用的注入字段。
vmMgr字段被注入但在此类中未被使用。♻️ 建议移除未使用的注入
`@Autowired` protected PluginRegistry pluginRgty; - `@Autowired` - protected VmInstanceManager vmMgr;🤖 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/VmReturnReleaseNicFlow.java` around lines 40 - 43, The field VmInstanceManager vmMgr is injected into class VmReturnReleaseNicFlow but never used; remove the unused `@Autowired` protected VmInstanceManager vmMgr declaration from VmReturnReleaseNicFlow (leave PluginRegistry pluginRgty intact) to eliminate the dead injection and related imports, or alternatively use vmMgr where intended if removal is incorrect—prefer removing the unused vmMgr field and its import.compute/src/main/java/org/zstack/compute/vm/VmDetachNicFlow.java (2)
44-47: 未使用的注入字段。
vmMgr字段被注入但在此类中未被使用。♻️ 建议移除未使用的注入
- `@Autowired` - private VmInstanceManager vmMgr; `@Autowired` private PluginRegistry pluginRgty;🤖 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/VmDetachNicFlow.java` around lines 44 - 47, The field vmMgr (private VmInstanceManager vmMgr) in class VmDetachNicFlow is injected but never used; remove the unused `@Autowired` field declaration (vmMgr) from VmDetachNicFlow and clean up any now-unused imports related to VmInstanceManager to avoid compilation warnings; if vmMgr is actually needed for future logic, alternatively replace the unused field with a clear TODO usage in the detach flow where relevant, but prefer removing the unused injection to keep the class clean.
120-146: 代码重复:建议提取公共方法。
callReleaseSdnNics方法与VmReturnReleaseNicFlow中的实现几乎完全相同。考虑将此逻辑提取到公共工具类(如VmNicHelper或新建SdnNicReleaseHelper)中以减少代码重复。🤖 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/VmDetachNicFlow.java` around lines 120 - 146, The callReleaseSdnNics method duplicates logic present in VmReturnReleaseNicFlow; extract the shared While<> loop and extension invocation into a new helper (e.g., VmNicHelper.releaseSdnNics or SdnNicReleaseHelper.releaseSdnNics) that accepts List<VmNicInventory>, PluginRegistry/extension registry (or obtain it internally), and a Completion, then replace the implementations in VmDetachNicFlow.callReleaseSdnNics and the method in VmReturnReleaseNicFlow to delegate to that new helper; ensure the helper preserves the same behavior (logging on fail, wcomp.done() semantics, and final completion.success()) and update imports/usages accordingly.docs/modules/network/pages/networkResource/ZnsIntegration.adoc (1)
1-911: 文档语言建议。根据编码规范,代码及相关文档应使用英文。此设计文档使用中文撰写,建议考虑是否需要提供英文版本以便更广泛的团队协作。作为内部设计文档,这可能是可接受的,取决于团队约定。
文档内容本身结构清晰,完整描述了 ZNS SDN 控制器的集成方案,包括资源映射、API 协议、生命周期管理和版本兼容性策略。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/modules/network/pages/networkResource/ZnsIntegration.adoc` around lines 1 - 911, The document is written in Chinese but our code/doc conventions require English — produce an English version of this design doc (keep the original Chinese as an appendix or side-by-side), translating all headings, prose, API examples and code snippets including symbols like Cms, Segment, ZnsControllerVO, ZnsTransportZoneVO, X-Api-Versions and API paths (e.g. /zns/api/v1/segments) exactly as-is; preserve all technical details, examples, and tables, keep code blocks and JSON/header examples unchanged except for translated comments/labels, and add a short note at the top indicating the document language(s) and location of the original Chinese text.compute/src/main/java/org/zstack/compute/vm/VmAllocateSdnNicFlow.java (1)
88-99: rollback() 中spec可能为 null 导致 NPE在
rollback方法中,直接从data.get()获取spec后立即调用spec.getDestNics()(第 91 行),但未先检查spec是否为 null。虽然在正常流程中spec应该存在,但在异常场景下可能缺失。建议在调用
getDestNics()前先检查spec是否为 null:🛡️ 建议的防御性修复
`@Override` public void rollback(final FlowRollback chain, Map data) { final VmInstanceSpec spec = (VmInstanceSpec) data.get(VmInstanceConstant.Params.VmInstanceSpec.toString()); + if (spec == null) { + chain.rollback(); + return; + } final List<VmNicInventory> nics = spec.getDestNics(); List<AfterAllocateSdnNicExtensionPoint> exts = pluginRgty.getExtensionList(AfterAllocateSdnNicExtensionPoint.class); - if (exts.isEmpty() || nics == null || nics.isEmpty()) { + if (exts.isEmpty() || nics == null || nics.isEmpty()) { chain.rollback(); return; }🤖 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/VmAllocateSdnNicFlow.java` around lines 88 - 99, The rollback() method can NPE because it assumes spec from data.get(VmInstanceConstant.Params.VmInstanceSpec.toString()) is non-null; add a null check for the VmInstanceSpec before calling spec.getDestNics(): if spec is null, invoke chain.rollback() and return early; otherwise proceed to obtain nics and the AfterAllocateSdnNicExtensionPoint list via pluginRgty.getExtensionList and continue existing logic.plugin/sdnController/src/main/java/org/zstack/sdnController/SdnControllerManagerImpl.java (1)
655-761: 三个方法存在大量重复代码,建议提取公共逻辑。
afterAllocateSdnNic、rollbackSdnNic和releaseSdnNics方法中构建nicMaps的逻辑几乎完全相同(遍历 NICs、查询 L3/L2、检查 SDN 跳过条件、获取 controller UUID)。主要差异仅在于:
afterAllocateSdnNic在controllerUuid == null时调用completion.fail()- 其他两个方法在
controllerUuid == null时continue建议提取公共的
buildNicMapsForSdn()辅助方法以减少重复代码。♻️ 建议的重构方案
/** * Build controller-to-NIC mappings for SDN operations. * `@param` nics the NICs to process * `@param` failOnMissingController if true, returns null when controller UUID is missing (error case) * `@param` missingControllerError output parameter for error when failOnMissingController is true * `@return` the NIC mappings, or null if failOnMissingController and a required controller is missing */ private Map<String, List<VmNicInventory>> buildNicMapsForSdn( List<VmNicInventory> nics, boolean failOnMissingController, ErrorCode[] missingControllerError) { Map<String, List<VmNicInventory>> nicMaps = new HashMap<>(); for (VmNicInventory nic : nics) { L3NetworkVO l3Vo = dbf.findByUuid(nic.getL3NetworkUuid(), L3NetworkVO.class); if (l3Vo == null) { continue; } L2NetworkVO l2VO = dbf.findByUuid(l3Vo.getL2NetworkUuid(), L2NetworkVO.class); if (l2VO == null || shouldSkipSdnForNic(l2VO)) { continue; } String controllerUuid = L2NetworkSystemTags.L2_NETWORK_SDN_CONTROLLER_UUID.getTokenByResourceUuid( l2VO.getUuid(), L2NetworkSystemTags.L2_NETWORK_SDN_CONTROLLER_UUID_TOKEN); if (controllerUuid == null) { if (failOnMissingController) { missingControllerError[0] = operr(ORG_ZSTACK_SDNCONTROLLER_10006, "sdn l2 network[uuid:%s] has not attached controller", l2VO.getUuid()); return null; } continue; } nicMaps.computeIfAbsent(controllerUuid, k -> new ArrayList<>()).add(nic); } return nicMaps; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@plugin/sdnController/src/main/java/org/zstack/sdnController/SdnControllerManagerImpl.java` around lines 655 - 761, The three methods afterAllocateSdnNic, rollbackSdnNic and releaseSdnNics duplicate NIC-to-controller mapping logic; extract that into a single helper (e.g. buildNicMapsForSdn) that takes List<VmNicInventory> and a flag failOnMissingController (and an out ErrorCode slot) and returns Map<String,List<VmNicInventory>> or null on error; call this helper from afterAllocateSdnNic with failOnMissingController=true and when it returns null call completion.fail(...) with the provided error, and call it from rollbackSdnNic/releaseSdnNics with failOnMissingController=false and proceed only if the returned map is non-empty. Ensure the helper performs the L3/L2 lookups, shouldSkipSdnForNic check, and controller UUID extraction and aggregates nicMaps exactly as in the original code.core/src/main/java/org/zstack/core/rest/webhook/WebhookCallbackClient.java (1)
60-69:PendingEntry直接依赖ThreadFacadeImpl.TimeoutTaskReceipt实现类,存在架构耦合问题。
PendingEntry类中的timeoutReceipt字段类型为ThreadFacadeImpl.TimeoutTaskReceipt。虽然此模式在多个文件中使用(如RESTFacadeImpl、AsyncTimer、CloudBusImpl2等),但这违反了抽象原则——ThreadFacade接口已经在其方法签名中暴露了TimeoutTaskReceipt,却被定义在实现类ThreadFacadeImpl内部。建议将
TimeoutTaskReceipt从ThreadFacadeImpl中提取出来,作为ThreadFacade接口的顶级嵌套接口或独立公共接口,以确保接口不依赖于实现类的内部结构,降低整体的架构耦合度。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@core/src/main/java/org/zstack/core/rest/webhook/WebhookCallbackClient.java` around lines 60 - 69, PendingEntry currently types its timeoutReceipt as the implementation class ThreadFacadeImpl.TimeoutTaskReceipt, creating an unnecessary coupling; change the API to use an abstraction by moving TimeoutTaskReceipt out of ThreadFacadeImpl and declaring it on the ThreadFacade interface (or as a top-level public interface), then update PendingEntry to reference ThreadFacade.TimeoutTaskReceipt (or the new public interface) and adjust other usages in RESTFacadeImpl, AsyncTimer, CloudBusImpl2, and any other callers to import and use the new interface type instead of the implementation inner class.
| private void validate(APIAddHostMsg msg) { | ||
| if (!NetworkUtils.isIpv4Address(msg.getManagementIp()) && !NetworkUtils.isHostname(msg.getManagementIp())) { | ||
| throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_COMPUTE_HOST_10113, "managementIp[%s] is neither an IPv4 address nor a valid hostname", msg.getManagementIp())); | ||
| String ip = msg.getManagementIp(); | ||
| if (!NetworkUtils.isHostname(ip) && !IPv6Utils.isValidManagementIp(ip)) { | ||
| throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_COMPUTE_HOST_10113, | ||
| "managementIp[%s] is not a valid IPv4/IPv6 address or hostname", ip)); | ||
| } | ||
| // Normalize IPv6 to RFC 5952 compressed form; IPv4/hostname returned as-is | ||
| msg.setManagementIp(IPv6Utils.normalizeIpv6(ip)); |
There was a problem hiding this comment.
IPv6 规范化只落在 Add 路径,会把 Update 去重绕开。
这里把 APIAddHostMsg.managementIp 规范化成 RFC 5952 了,但同一个类里的 APIUpdateHostMsg 仍然按原始字符串做重复检查。这样库里如果已经存了 2001:db8::1,后续再用全写地址 2001:0db8:0000:...:0001 更新另一台主机时,就可能绕过去重并留下同一地址的两种写法。建议把 management IP 的规范化提到 add/update 共用入口,再做校验和持久化。
🤖 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/host/HostApiInterceptor.java` around
lines 124 - 131, The normalization of IPv6 (IPv6Utils.normalizeIpv6) is only
applied in validate(APIAddHostMsg) causing duplicate-host checks to miss
equivalent IPv6 representations; move the normalization into a shared path used
by both add and update flows (e.g., extract a private helper like
normalizeManagementIp(String) and call it from validate(APIAddHostMsg) and
validate(APIUpdateHostMsg) or a common validate entry in HostApiInterceptor) so
that both APIAddHostMsg and APIUpdateHostMsg have
msg.setManagementIp(normalized) before any duplicate-check or persistence; also
ensure the same validation (NetworkUtils.isHostname /
IPv6Utils.isValidManagementIp) is applied prior to normalization.
| public void releaseVmResource(VmInstanceSpec spec, Completion completion) { | ||
| if (spec.getCurrentVmOperation() == VmInstanceConstant.VmOperation.Reboot) { | ||
| // Reboot does not change host; resources remain valid | ||
| completion.success(); | ||
| return; | ||
| } | ||
| doCleanup(spec, new NoErrorCompletion(completion) { | ||
| @Override | ||
| public void done() { | ||
| completion.success(); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| private void doCleanup(VmInstanceSpec spec, NoErrorCompletion completion) { | ||
| List<VmNicInventory> allNics = spec.getDestNics(); | ||
| if (allNics == null || allNics.isEmpty() || getExtensions().isEmpty()) { | ||
| completion.done(); | ||
| return; | ||
| } | ||
| if (spec.getDestHost() == null) { | ||
| completion.done(); | ||
| return; | ||
| } | ||
| String hostUuid = spec.getDestHost().getUuid(); | ||
| runCleanup(getExtensions().iterator(), hostUuid, allNics, completion); | ||
| } |
There was a problem hiding this comment.
释放路径不应该只依赖 destHost。
releaseVmResource() 最终从 spec.getDestHost() 取清理宿主机;对 stop/destroy 这类“从当前宿主机释放资源”的流程,destHost 很容易为空,Line 446-448 就会直接跳过清理,留下 host-bound NIC 资源。这里至少需要回退到当前/最后一次运行宿主机。
🤖 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/VmNicLifecycleManager.java`
around lines 426 - 452, The cleanup currently skips when spec.getDestHost() is
null, which misses releasing host-bound NICs for stop/destroy flows; update
doCleanup to fallback to the VM's current/last host when destHost is null by
obtaining the host UUID from the VM inventory (e.g.,
spec.getVmInventory().getHostUuid() or similar accessor) and use that hostUuid
for runCleanup; ensure you still null-check the fallback and only skip when no
hostUuid can be resolved.
| public void afterMigrateVm(VmInstanceInventory inv, String srcHostUuid, | ||
| NoErrorCompletion completion) { | ||
| // notify hook - no routing | ||
| completion.done(); | ||
| } | ||
|
|
||
| @Override | ||
| public void failedToMigrateVm(VmInstanceInventory inv, String destHostUuid, | ||
| ErrorCode reason, NoErrorCompletion completion) { | ||
| if (getExtensions().isEmpty()) { | ||
| completion.done(); | ||
| return; | ||
| } | ||
| List<VmNicInventory> allNics = inv.getVmNics(); | ||
| if (allNics == null || allNics.isEmpty()) { | ||
| completion.done(); | ||
| return; | ||
| } | ||
| String srcHostUuid = inv.getHostUuid(); | ||
| runFailedMigrate(getExtensions().iterator(), srcHostUuid, destHostUuid, allNics, | ||
| completion); | ||
| } | ||
|
|
||
| // postMigrate hook - fail-logged since migration already completed | ||
| @Override | ||
| public void postMigrateVm(VmInstanceInventory inv, String destHostUuid, | ||
| Completion completion) { | ||
| if (getExtensions().isEmpty()) { | ||
| completion.success(); | ||
| return; | ||
| } | ||
| List<VmNicInventory> allNics = inv.getVmNics(); | ||
| if (allNics == null || allNics.isEmpty()) { | ||
| completion.success(); | ||
| return; | ||
| } | ||
| String srcHostUuid = inv.getHostUuid(); | ||
| runPostMigrate(getExtensions().iterator(), srcHostUuid, destHostUuid, allNics, | ||
| new NoErrorCompletion(completion) { | ||
| @Override | ||
| public void done() { | ||
| completion.success(); | ||
| } | ||
| }); | ||
| } |
There was a problem hiding this comment.
迁移后的清理丢失了真实的源宿主机。
这里把 afterMigrateVm(..., String srcHostUuid, ...) 直接忽略了,到了 postMigrateVm(...) 又用 inv.getHostUuid() 当 srcHostUuid 传给 runPostMigrate(...)。迁移成功后 inv.getHostUuid() 通常已经是目的宿主机,而默认 postMigrate() 会 cleanupFromHost(srcHostUuid, ...),这会把新宿主机上的 NIC 资源删掉,旧宿主机资源反而残留。
🤖 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/VmNicLifecycleManager.java`
around lines 553 - 597, The post-migration cleanup is using inv.getHostUuid()
(which is already the destination host) instead of the real source host passed
into afterMigrateVm, causing cleanup of the new host; fix by persisting the
original srcHostUuid in afterMigrateVm and using that persisted value in
postMigrateVm when calling runPostMigrate. Concretely: add a thread-safe map
field (e.g., Map<String,String> vmSrcHostMap) keyed by VM UUID, have
afterMigrateVm(VmInstanceInventory inv, String srcHostUuid, ...) store
vmSrcHostMap.put(inv.getUuid(), srcHostUuid) and return immediately, then change
postMigrateVm(...) to read and remove the entry (String srcHostUuid =
vmSrcHostMap.remove(inv.getUuid()); if null fallback to inv.getHostUuid()), and
pass that srcHostUuid into runPostMigrate(invocations) so cleanupFromHost runs
against the real source host. Ensure the map is cleaned up in both postMigrateVm
and failedToMigrate/error paths to avoid leaks.
| for (AfterUpdateIpRangeExtensionPoint ext : pluginRgty.getExtensionList(AfterUpdateIpRangeExtensionPoint.class)) { | ||
| ext.afterUpdateIpRange(oldIpr, newIpr); | ||
| } |
There was a problem hiding this comment.
隔离扩展点异常,避免“已更新成功但 API 返回失败”的部分成功状态。
vo 已在 Line 1014 落库;这里任一扩展抛出运行时异常,都会中断事件发布并最终走错误回复路径。结果就是 IP range 实际已经更新,但调用方看到的是失败,重试还可能放大副作用。建议在这里捕获并记录扩展异常,或者把允许失败的校验前移到持久化之前。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@network/src/main/java/org/zstack/network/l3/L3BasicNetwork.java` around lines
1018 - 1020, The AfterUpdateIpRangeExtensionPoint callbacks invoked via
pluginRgty.getExtensionList(...).forEach are allowed to throw runtime exceptions
which can cause the API to return failure even though the IP range (vo) was
already persisted; wrap the loop calling ext.afterUpdateIpRange(oldIpr, newIpr)
in a try/catch that catches Throwable for each extension invocation, log the
exception with context (which extension, oldIpr.id/newIpr.id) using the class
logger, and continue to the next extension so failures in extension points do
not abort the API response; alternatively, if some extension failures must block
the operation, perform those validations before persisting the vo in methods
handling the update.
| }).then(new NoRollbackFlow() { | ||
| @Override | ||
| public void run(FlowTrigger trigger, Map data) { | ||
| List<AfterSetL3NetworkMtuExtensionPoint> exts = | ||
| pluginRgty.getExtensionList(AfterSetL3NetworkMtuExtensionPoint.class); | ||
| if (exts.isEmpty()) { | ||
| trigger.next(); | ||
| return; | ||
| } | ||
|
|
||
| L3NetworkInventory l3Inv = L3NetworkInventory.valueOf(dbf.findByUuid(msg.getL3NetworkUuid(), L3NetworkVO.class)); | ||
| new While<>(exts).each((ext, wcompl) -> { | ||
| ext.afterSetL3NetworkMtu(l3Inv, msg.getMtu(), new Completion(wcompl) { | ||
| @Override | ||
| public void success() { | ||
| wcompl.done(); | ||
| } | ||
|
|
||
| @Override | ||
| public void fail(ErrorCode errorCode) { | ||
| wcompl.addError(errorCode); | ||
| wcompl.allDone(); | ||
| } | ||
| }); | ||
| }).run(new WhileDoneCompletion(trigger) { | ||
| @Override | ||
| public void done(ErrorCodeList errorCodeList) { | ||
| if (errorCodeList.getCauses().isEmpty()) { | ||
| trigger.next(); | ||
| } else { | ||
| trigger.fail(errorCodeList.getCauses().get(0)); | ||
| } | ||
| } | ||
| }); | ||
| } |
There was a problem hiding this comment.
这里新增了一个不可补偿的失败点。
这段逻辑是在前一个 flow 已经把 SDN DHCP 更新成功之后才执行的。只要任一扩展在 Line 206-208 返回失败,流程就会进入 error handler,但现有回滚只处理本地 MTU tag;前面已经成功的 SDN DHCP 更新、以及已经执行成功的扩展都不会回滚,oldmtu == null 时甚至还会保留新 tag。结果就是 API 返回失败,但系统状态可能已经部分生效。
建议把这些 hook 放到所有不可逆副作用之前,或者补齐对应的补偿回滚逻辑。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@network/src/main/java/org/zstack/network/l3/L3NetworkManagerImpl.java` around
lines 187 - 221, The new AfterSetL3NetworkMtuExtensionPoint invocation in
L3NetworkManagerImpl (the While loop calling ext.afterSetL3NetworkMtu for each
extension) introduces an un-compensated failure point after SDN DHCP updates;
update the code so either (a) move the extension hooks to execute before any
irreversible side effects (e.g., before the SDN DHCP update step), or (b)
implement a compensation path executed when any ext.afterSetL3NetworkMtu fails
that reverts prior irreversible changes: roll back the SDN DHCP update, undo any
extensions that already succeeded, and restore the MTU tag when oldmtu == null.
Ensure the compensation is wired into the same flow (the NoRollbackFlow block
and WhileDoneCompletion) so trigger.fail leads to running the compensation
logic.
| new While<>(exts).each((ext, wcompl) -> { | ||
| ext.afterSetL3NetworkMtu(l3Inv, msg.getMtu(), new Completion(wcompl) { | ||
| @Override | ||
| public void success() { | ||
| wcompl.done(); | ||
| } | ||
|
|
||
| @Override | ||
| public void fail(ErrorCode errorCode) { | ||
| wcompl.addError(errorCode); | ||
| wcompl.allDone(); | ||
| } | ||
| }); | ||
| }).run(new WhileDoneCompletion(trigger) { | ||
| @Override | ||
| public void done(ErrorCodeList errorCodeList) { | ||
| if (errorCodeList.getCauses().isEmpty()) { | ||
| trigger.next(); | ||
| } else { | ||
| trigger.fail(errorCodeList.getCauses().get(0)); | ||
| } |
There was a problem hiding this comment.
不要在首个扩展失败时提前结束整个遍历。
Line 208 的 wcompl.allDone() 会在第一个失败后直接终止 While,后面的 AfterSetL3NetworkMtuExtensionPoint 不会再执行,ErrorCodeList 也基本失去聚合意义。对这种 MTU 后置同步钩子,这会让同一次变更在不同插件里只执行一部分。
建议修改
new While<>(exts).each((ext, wcompl) -> {
ext.afterSetL3NetworkMtu(l3Inv, msg.getMtu(), new Completion(wcompl) {
`@Override`
public void success() {
wcompl.done();
}
`@Override`
public void fail(ErrorCode errorCode) {
wcompl.addError(errorCode);
- wcompl.allDone();
+ wcompl.done();
}
});
}).run(new WhileDoneCompletion(trigger) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@network/src/main/java/org/zstack/network/l3/L3NetworkManagerImpl.java` around
lines 198 - 218, The code calls wcompl.allDone() inside the Completion.fail
handler which prematurely terminates the While loop when the first
AfterSetL3NetworkMtuExtensionPoint fails; change the fail handler for the
Completion passed to ext.afterSetL3NetworkMtu(...) so that it records the error
via wcompl.addError(errorCode) but does NOT call wcompl.allDone(), allowing the
While<> to continue invoking remaining extensions and let the
WhileDoneCompletion aggregate all errors (i.e., remove the wcompl.allDone() call
and only call wcompl.done() on success and addError on failure).
| List<VmInstanceVO> runningVms = Q.New(VmInstanceVO.class) | ||
| .eq(VmInstanceVO_.hostUuid, hostUuid) | ||
| .eq(VmInstanceVO_.state, VmInstanceState.Running) | ||
| .list(); | ||
| allExpectedNics = runningVms.stream() | ||
| .flatMap(vm -> VmNicInventory.valueOf(vm.getVmNics()).stream()) | ||
| .collect(Collectors.toList()); |
There was a problem hiding this comment.
避免在 ping 热路径加载完整 VM 实体。
这里每次 ping 都先查所有 VmInstanceVO,再展开 vm.getVmNics()。如果 vmNics 是延迟加载,这里会退化成 N+1 查询;即便不是,也会在高频路径上把整张 VM 实体图读出来,但实际只需要 NIC 信息。建议直接按 hostUuid/state 查询 NIC 侧的最小字段集合。
As per coding guidelines "仅查询实际需要的列,避免加载整个实体。禁止循环里套查询,避免嵌套查询带来的性能问题。"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@plugin/kvm/src/main/java/org/zstack/kvm/VmNicLifecycleKvmBridge.java` around
lines 53 - 59, The code in VmNicLifecycleKvmBridge builds allExpectedNics by
loading full VmInstanceVOs then calling vm.getVmNics(), which can cause N+1
queries or unnecessary entity loading; instead query the NIC side directly:
replace the Q.New(VmInstanceVO.class)... +
VmNicInventory.valueOf(vm.getVmNics()) logic with a single query against VmNicVO
(or the NIC inventory table), selecting only the minimal columns needed for
VmNicInventory (e.g., nic uuid, mac, ip, l3NetworkUuid, vmInstanceUuid) and
filter NICs by their VM’s hostUuid and by VmInstanceState.Running via a join or
subquery so you avoid loading full VmInstanceVO objects and eliminate
nested/looped queries that cause the N+1 problem.
| GlobalConfigMarkDown markDown = getExistGlobalConfigMarkDown(mdPath) | ||
| List<String> missingFields = [] | ||
| if (markDown.desc_CN.isEmpty()) missingFields.add("desc_CN") | ||
| if (markDown.name_CN.isEmpty()) missingFields.add("name_CN") | ||
| if (markDown.valueRangeRemark.isEmpty()) missingFields.add("valueRangeRemark") | ||
| if (markDown.defaultValueRemark.isEmpty()) missingFields.add("defaultValueRemark") | ||
| if (markDown.resourcesGranularitiesRemark.isEmpty()) missingFields.add("resourcesGranularitiesRemark") | ||
| if (markDown.additionalRemark.isEmpty()) missingFields.add("additionalRemark") | ||
| if (markDown.backgroundInformation.isEmpty()) missingFields.add("backgroundInformation") | ||
| if (markDown.isUIExposed.isEmpty()) missingFields.add("isUIExposed") | ||
| if (markDown.isCLIExposed.isEmpty()) missingFields.add("isCLIExposed") |
There was a problem hiding this comment.
缺失整个章节时,这里的校验会漏报。
GlobalConfigMarkDown 的这些字段有默认占位值;如果有人直接把某个必填章节整段删掉,文件里已经没有 PLACEHOLDER,但解析后的字段仍然是默认占位字符串,.isEmpty() 会返回 false。这样不完整的 markdown 可能会被误判为通过。
💡 建议修改
GlobalConfigMarkDown markDown = getExistGlobalConfigMarkDown(mdPath)
+ def isMissing = { String value ->
+ value == null || value.isEmpty() || value.startsWith(PLACEHOLDER)
+ }
List<String> missingFields = []
- if (markDown.desc_CN.isEmpty()) missingFields.add("desc_CN")
- if (markDown.name_CN.isEmpty()) missingFields.add("name_CN")
- if (markDown.valueRangeRemark.isEmpty()) missingFields.add("valueRangeRemark")
- if (markDown.defaultValueRemark.isEmpty()) missingFields.add("defaultValueRemark")
- if (markDown.resourcesGranularitiesRemark.isEmpty()) missingFields.add("resourcesGranularitiesRemark")
- if (markDown.additionalRemark.isEmpty()) missingFields.add("additionalRemark")
- if (markDown.backgroundInformation.isEmpty()) missingFields.add("backgroundInformation")
- if (markDown.isUIExposed.isEmpty()) missingFields.add("isUIExposed")
- if (markDown.isCLIExposed.isEmpty()) missingFields.add("isCLIExposed")
+ if (isMissing(markDown.desc_CN)) missingFields.add("desc_CN")
+ if (isMissing(markDown.name_CN)) missingFields.add("name_CN")
+ if (isMissing(markDown.valueRangeRemark)) missingFields.add("valueRangeRemark")
+ if (isMissing(markDown.defaultValueRemark)) missingFields.add("defaultValueRemark")
+ if (isMissing(markDown.resourcesGranularitiesRemark)) missingFields.add("resourcesGranularitiesRemark")
+ if (isMissing(markDown.additionalRemark)) missingFields.add("additionalRemark")
+ if (isMissing(markDown.backgroundInformation)) missingFields.add("backgroundInformation")
+ if (isMissing(markDown.isUIExposed)) missingFields.add("isUIExposed")
+ if (isMissing(markDown.isCLIExposed)) missingFields.add("isCLIExposed")📝 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.
| GlobalConfigMarkDown markDown = getExistGlobalConfigMarkDown(mdPath) | |
| List<String> missingFields = [] | |
| if (markDown.desc_CN.isEmpty()) missingFields.add("desc_CN") | |
| if (markDown.name_CN.isEmpty()) missingFields.add("name_CN") | |
| if (markDown.valueRangeRemark.isEmpty()) missingFields.add("valueRangeRemark") | |
| if (markDown.defaultValueRemark.isEmpty()) missingFields.add("defaultValueRemark") | |
| if (markDown.resourcesGranularitiesRemark.isEmpty()) missingFields.add("resourcesGranularitiesRemark") | |
| if (markDown.additionalRemark.isEmpty()) missingFields.add("additionalRemark") | |
| if (markDown.backgroundInformation.isEmpty()) missingFields.add("backgroundInformation") | |
| if (markDown.isUIExposed.isEmpty()) missingFields.add("isUIExposed") | |
| if (markDown.isCLIExposed.isEmpty()) missingFields.add("isCLIExposed") | |
| GlobalConfigMarkDown markDown = getExistGlobalConfigMarkDown(mdPath) | |
| def isMissing = { String value -> | |
| value == null || value.isEmpty() || value.startsWith(PLACEHOLDER) | |
| } | |
| List<String> missingFields = [] | |
| if (isMissing(markDown.desc_CN)) missingFields.add("desc_CN") | |
| if (isMissing(markDown.name_CN)) missingFields.add("name_CN") | |
| if (isMissing(markDown.valueRangeRemark)) missingFields.add("valueRangeRemark") | |
| if (isMissing(markDown.defaultValueRemark)) missingFields.add("defaultValueRemark") | |
| if (isMissing(markDown.resourcesGranularitiesRemark)) missingFields.add("resourcesGranularitiesRemark") | |
| if (isMissing(markDown.additionalRemark)) missingFields.add("additionalRemark") | |
| if (isMissing(markDown.backgroundInformation)) missingFields.add("backgroundInformation") | |
| if (isMissing(markDown.isUIExposed)) missingFields.add("isUIExposed") | |
| if (isMissing(markDown.isCLIExposed)) missingFields.add("isCLIExposed") |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@rest/src/main/resources/scripts/RestDocumentationGenerator.groovy` around
lines 851 - 861, The current validation in RestDocumentationGenerator.groovy
uses isEmpty() on fields from GlobalConfigMarkDown returned by
getExistGlobalConfigMarkDown(mdPath), but those fields may contain default
placeholder text so a deleted section is not detected; update the checks for
each field (desc_CN, name_CN, valueRangeRemark, defaultValueRemark,
resourcesGranularitiesRemark, additionalRemark, backgroundInformation,
isUIExposed, isCLIExposed) to treat placeholder/default values as missing (e.g.,
null or blank or equal to the known placeholder token/placeholder regex) instead
of only using isEmpty(), so the code flags a missing entire section correctly.
Ensure you reference the placeholder constant or literal used by
GlobalConfigMarkDown when adding the extra equality/regex checks.
| public static boolean isValidManagementIp(String ip) { | ||
| if (ip == null || ip.isEmpty()) { | ||
| return false; | ||
| } | ||
| try { | ||
| InetAddress addr = InetAddress.getByName(ip); | ||
| return !addr.isLoopbackAddress() && !addr.isLinkLocalAddress(); | ||
| } catch (UnknownHostException e) { | ||
| return false; |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Java 的 InetAddress.getByName(String) 是否会接受主机名并执行 DNS 解析,而不仅仅是 IP 字面量?
💡 Result:
Yes, Java's InetAddress.getByName(String) accepts hostnames and performs DNS resolution (via the system-wide resolver, which uses DNS, hosts file, etc.), not just IP literals. For hostnames like "www.example.com", it resolves to IP addresses using network naming services such as DNS. For literal IP addresses, it only validates the format without resolution.
Citations:
- 1: https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/net/InetAddress.html
- 2: https://docs.oracle.com/en/java/javase/24/docs/api/java.base/java/net/InetAddress.html
- 3: https://docs.oracle.com/javase/8/docs/api/java/net/InetAddress.html
🏁 Script executed:
#!/bin/bash
# Search for isIpv4Address and isIpv6Address method definitions
rg "def isIpv4Address|def isIpv6Address|boolean isIpv4Address|boolean isIpv6Address" -t javaRepository: MatheMatrix/zstack
Length of output: 298
🏁 Script executed:
#!/bin/bash
# Check for Chinese text in IPv6Utils.java file
fd "IPv6Utils.java" -x cat -n {} | grep -E "[\u4e00-\u9fff]|[\x80-\xFF]{2,}" | head -20Repository: MatheMatrix/zstack
Length of output: 854
🏁 Script executed:
#!/bin/bash
# Search for NetworkUtils class and its validation methods
fd "NetworkUtils.java" -t java -x head -150 {} | grep -A 2 "isValidIPAddress\|isIpv4"Repository: MatheMatrix/zstack
Length of output: 234
🏁 Script executed:
#!/bin/bash
# Find and display isIpv4Address implementation
rg -A 8 "public static boolean isIpv4Address" utils/src/main/java/org/zstack/utils/network/NetworkUtils.javaRepository: MatheMatrix/zstack
Length of output: 388
🏁 Script executed:
#!/bin/bash
# Find and display isIpv6Address implementation
rg -A 8 "public static boolean isIpv6Address" utils/src/main/java/org/zstack/utils/network/IPv6NetworkUtils.javaRepository: MatheMatrix/zstack
Length of output: 268
🏁 Script executed:
#!/bin/bash
# Check the exact content of IPv6Utils.java around lines 119-127
sed -n '110,135p' utils/src/main/java/org/zstack/utils/network/IPv6Utils.java | cat -nRepository: MatheMatrix/zstack
Length of output: 803
🏁 Script executed:
#!/bin/bash
# Check for any Chinese characters in the full IPv6Utils.java file
grep -n "[[:print:]]*[\x80-\xFF]" utils/src/main/java/org/zstack/utils/network/IPv6Utils.java | head -20Repository: MatheMatrix/zstack
Length of output: 998
isValidManagementIp 会错误地接受主机名作为管理 IP 地址。
InetAddress.getByName(ip) 会通过 DNS 解析主机名,而非仅校验 IP 字面量;这会导致 example.com 等主机名被错误地接受为有效的管理 IP。应先用 isIpv4Address() 和 isIpv6Address() 进行格式校验,再做 loopback 和 link-local 地址的检查。同时,注释中的中文不符合规范,应改为英文。
建议修改
public static boolean isValidManagementIp(String ip) {
+ // Validates if the IP can be used as a management IP (IPv4 or IPv6).
+ // Rejects link-local and loopback addresses.
+ // Examples:
+ // Valid IPv4 → true
+ // Valid IPv6 (not fe80::, not ::1) → true
+ // fe80::/::1/invalid format/null/empty → false
if (ip == null || ip.isEmpty()) {
return false;
}
+ // Verify IP format first to reject hostnames
+ if (!isIpv4Address(ip) && !isIpv6Address(ip)) {
+ return false;
+ }
try {
+ // Handle IPv6 zone ID (e.g., "fe80::1%eth0")
+ String stripped = ip.contains("%") ? ip.split("%")[0] : ip;
- InetAddress addr = InetAddress.getByName(ip);
+ InetAddress addr = InetAddress.getByName(stripped);
return !addr.isLoopbackAddress() && !addr.isLinkLocalAddress();
} catch (UnknownHostException e) {
return false;
}
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@utils/src/main/java/org/zstack/utils/network/IPv6Utils.java` around lines 119
- 127, The isValidManagementIp method currently accepts hostnames because it
calls InetAddress.getByName; update isValidManagementIp to first validate the
input is a literal IP by calling isIpv4Address(ip) or isIpv6Address(ip) and
return false otherwise, then parse the literal (e.g., InetAddress.getByName or
InetAddress.getByAddress) and perform the existing !addr.isLoopbackAddress() &&
!addr.isLinkLocalAddress() checks; also replace the existing Chinese comment in
IPv6Utils.isValidManagementIp with an English comment describing the intent
(only accept literal IPv4/IPv6 addresses, not hostnames) so the method behavior
and documentation match.
| public static boolean isIpInCidr(String ip, String cidr) { | ||
| if (ip == null || cidr == null) return false; | ||
| if (isIpv4Address(ip)) return isIpv4InCidr(ip, cidr); | ||
| if (IPv6NetworkUtils.isIpv6Address(ip)) return IPv6NetworkUtils.isIpv6InCidrRange(ip, cidr); | ||
| return false; |
There was a problem hiding this comment.
混合地址族时这里还会抛异常,不会按预期返回 false。
当 ip 是 IPv4、cidr 是 IPv6 时,这里会进入 isIpv4InCidr();该路径只适用于 IPv4 CIDR。结果是新 helper 在 IPv4/IPv6 不匹配场景下仍可能直接异常,而不是像本次 PR 目标那样返回 false。NfsApiParamChecker 这类新调用方会被这个根因影响。
🛠️ 建议修复
public static boolean isIpInCidr(String ip, String cidr) {
- if (ip == null || cidr == null) return false;
- if (isIpv4Address(ip)) return isIpv4InCidr(ip, cidr);
- if (IPv6NetworkUtils.isIpv6Address(ip)) return IPv6NetworkUtils.isIpv6InCidrRange(ip, cidr);
- return false;
+ if (ip == null || cidr == null) {
+ return false;
+ }
+
+ if (isIpv4Address(ip)) {
+ return isCidr(cidr, IPv6Constants.IPv4) && isIpv4InCidr(ip, cidr);
+ }
+
+ if (IPv6NetworkUtils.isIpv6Address(ip)) {
+ return isCidr(cidr, IPv6Constants.IPv6) && IPv6NetworkUtils.isIpv6InCidrRange(ip, cidr);
+ }
+
+ return false;
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@utils/src/main/java/org/zstack/utils/network/NetworkUtils.java` around lines
564 - 568, isIpInCidr currently only checks the IP's family and directly calls
isIpv4InCidr or IPv6NetworkUtils.isIpv6InCidrRange, which throws when the CIDR
is of the opposite family; fix by verifying the CIDR's address family before
delegating: if isIpv4Address(ip) then ensure the cidr is an IPv4 CIDR before
calling isIpv4InCidr, and if IPv6NetworkUtils.isIpv6Address(ip) ensure the cidr
is IPv6 before calling IPv6NetworkUtils.isIpv6InCidrRange; if families mismatch,
return false. Update isIpInCidr to use the existing predicates (isIpv4Address,
isIpv4InCidr, IPv6NetworkUtils.isIpv6Address,
IPv6NetworkUtils.isIpv6InCidrRange) or add a small helper to detect CIDR family
and short-circuit to false on mismatch.
Resolves: ZSTAC-79206 Add TP number prefix to 59 Groovy test methods across 9 test case files so method names align with TP-001~096 traceability matrix in Func Spec. Change-Id: I9aee46759327162b1a0ed3da9277c9be695ae0d8
There was a problem hiding this comment.
Actionable comments posted: 12
🧹 Nitpick comments (2)
test/src/test/groovy/org/zstack/test/integration/core/MnIpv6M3Case.groovy (1)
55-69: 建议直接断言拦截器入口行为,而不只复刻布尔条件这两段当前只验证
isIpv4/isIpv6组合逻辑,未直接调用BaremetalChassisApiInterceptor.check()。若拦截器后续新增额外校验,这些用例可能继续通过但无法发现回归。建议改为“构造请求 + 调用拦截器 + 断言放行/拒绝”。Also applies to: 89-102
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/src/test/groovy/org/zstack/test/integration/core/MnIpv6M3Case.groovy` around lines 55 - 69, The test currently only asserts NetworkUtils.isIpv4Address/IPv6NetworkUtils.isIpv6Address combinations and should instead exercise the actual interceptor; update testTP062_ipmiIpv6AcceptedInInterceptor (and the similar block around lines 89-102) to construct a minimal IPMI request/context, invoke BaremetalChassisApiInterceptor.check(...) with that request (or the appropriate interceptor entry method), and assert the interceptor allows the request (no exception or returns true); use the real interceptor class/method names (BaremetalChassisApiInterceptor.check) so the test fails on any future additional validation rather than just the raw isIpv4/isIpv6 booleans.test/src/test/groovy/org/zstack/test/integration/core/MnIpv6Case.groovy (1)
192-201: TP-022 断言过弱,建议校验具体修正结果。仅断言
result != null无法有效防回归;建议直接断言裸 IPv6 URL 被规范化为带方括号格式(或至少断言结果与输入不同且包含[])。可参考的断言收敛方式
- assert result != null : "TP-022: sanitizeCallbackUrl should not return null for bare IPv6 URL" + assert result == "http://[2001:db8::1]:8080/callback" : + "TP-022: bare IPv6 callback URL should be bracket-normalized, got: $result"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/src/test/groovy/org/zstack/test/integration/core/MnIpv6Case.groovy` around lines 192 - 201, The test TP-022 (testTP022_sanitizeCallbackUrlBareIpv6) currently only asserts result != null; update it to assert the expected normalization of a bare IPv6 URL by invoking RESTFacadeImpl.sanitizeCallbackUrl and checking that the returned string is the bracketed IPv6 form (e.g., "http://[2001:db8::1]:8080/callback") or at minimum that the result differs from the input and contains both '[' and ']' characters; use the sanitizeCallbackUrl method reference and replace the weak null check with a concrete assertion on the normalized value or the presence of brackets.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@test/src/test/groovy/org/zstack/test/integration/appliancevm/ApplianceVmIpv6Case.groovy`:
- Around line 11-22: Update all Chinese comments and Javadoc in the
ApplianceVmIpv6Case class to fluent, correct English: replace the class-level
Javadoc (describing TP-025–TP-029 and the purpose of testing
ApplianceVmFacadeImpl.getMnIpForVr), all inline comments inside test methods,
and any other Chinese annotations/comments (including those referenced around
the TP cases and the noted locations) with concise English descriptions and
correct spelling/grammar; ensure test intent, expected behaviors (e.g., CIDR
matching, fallbacks to Platform.getManagementServerIp(), bracketless IP return,
and invalid CIDR behavior), and any TODOs/assertion explanations are preserved
accurately in the new English text.
- Around line 112-117: The test uses an environment-dependent CIDR
"10.99.88.0/24" which can match real host interfaces; change the unmatchedCidr
constant in the test method (where callGetMnIpForVr is invoked) to a
documentation/reserved block that won't exist in real networks (e.g.,
"192.0.2.0/24" per RFC5737) so callGetMnIpForVr(unmatchedCidr) deterministically
falls back to mnIp; update the string assignment for unmatchedCidr and keep the
same assertions around callGetMnIpForVr and mnIp.
In `@test/src/test/groovy/org/zstack/test/integration/core/MnIpv6Case.groovy`:
- Around line 14-262: The file MnIpv6Case contains extensive Chinese comments,
assertion messages and logger texts; replace all Chinese text with clear English
equivalents across the class header comment and inside each test method
(including messages in assertions, logger.info/warn calls and inline comments)
while keeping the original intent and test names (e.g.,
testTP001_preferIpv6DefaultValue, testTP002_preferIpv6CategoryAndName,
testTP003_getManagementServerIpNonNull, testTP004_getManagementServerIpIpv4,
testTP005_getManagementServerIpFallback,
testTP006_getManagementServerCidrFormat, testTP007_getManagementServerCidrValid,
testTP021_sanitizeCallbackUrlIpv4, testTP022_sanitizeCallbackUrlBareIpv6,
testTP023_getManagementServerIdNonNull, testTP024_getManagementServerIdStable,
testTP030_jgroupsAddrIpv6, testTP031_jgroupsAddrIpv4); ensure assertion strings
and logger messages are idiomatic English and retain unique identifiers like
PREFER_IPV6, Platform.getManagementServerIp(),
Platform.getManagementServerCidr(), RESTFacadeImpl.sanitizeCallbackUrl(), and
Platform.jgroupsAddr() so tests behave identically.
- Around line 121-133: The test testTP005_getManagementServerIpFallback
currently never toggles the PREFER_IPV6 flag, so update the test to explicitly
enable PREFER_IPV6=true before calling Platform.getManagementServerIp(),
simulate or ensure no IPv6 interfaces are present (or mock the environment used
by Platform.getManagementServerIp()), then call Platform.getManagementServerIp()
inside the try/catch and assert it returns a non-null IPv4 fallback (e.g.,
verify the returned string does not contain ':'), and finally restore the
original PREFER_IPV6 setting in a finally block to avoid side effects; reference
symbols: testTP005_getManagementServerIpFallback,
Platform.getManagementServerIp(), and PREFER_IPV6.
In `@test/src/test/groovy/org/zstack/test/integration/core/MnIpv6M3Case.groovy`:
- Around line 43-50: Several test invocations have mismatched TP numbers between
the called method names and their inline comments (e.g.,
testTP065_ipmiAddressFullLengthIpv6() commented as TP-064,
testTP064_consoleDualStackVip() commented as TP-069, and similar mismatches
around lines with testTP062_ipmiIpv6AcceptedInInterceptor,
testTP066_ipmiInvalidAddressRejected, testTP067_consoleBracketIpv6,
testTP069_consoleVncTokenUrl, testTP076_bmDpuCallbackIpBracket,
testTP077_coloQemuUrlIpv6Bracket and other occurrences noted); update either the
method names or the inline TP comments so each call and its comment share the
same TP identifier (for example rename methods to match the comment TP or change
the comment to the method’s TP) across the listed methods
(testTP062_ipmiIpv6AcceptedInInterceptor, testTP065_ipmiAddressFullLengthIpv6,
testTP066_ipmiInvalidAddressRejected, testTP067_consoleBracketIpv6,
testTP069_consoleVncTokenUrl, testTP064_consoleDualStackVip,
testTP076_bmDpuCallbackIpBracket, testTP077_coloQemuUrlIpv6Bracket and the other
noted lines) to keep the TP numbering consistent for traceability.
- Around line 8-23: The file-level and inline comments in the MnIpv6M3Case
Groovy test (class MnIpv6M3Case) contain Chinese text that violates repository
policy; update all class-level Javadoc, method comments, and inline comments
(including the header block and the locations noted around lines corresponding
to the case descriptions and method comments) to clear, idiomatic English that
conveys the same meaning (e.g., translate test purpose, covered TPs, and inline
notes), and also convert any Chinese text in exception/error messages or log
strings to English; ensure no Chinese characters remain in comments, strings, or
annotations before committing.
In `@test/src/test/groovy/org/zstack/test/integration/core/ZSha2Ipv6Case.groovy`:
- Around line 37-38: The TP numbers are swapped between the two test methods:
rename testTP043_isMasterGrepPatternMatchesIpv6 and
testTP042_isMasterGrepLogicWithVip so that each method name reflects the correct
TP number (i.e., method that implements TP-042 should be named testTP042_... and
the TP-043 implementation should be testTP043_...); update all call sites and
references (including the other occurrences noted for the same pair) and any
inline comments so the invocation order, method names, and TP comments are
consistent across the file and test reports.
- Around line 6-17: Replace all Chinese comments and Javadoc in the
ZSha2Ipv6Case.groovy test class with clear, correctly spelled English
equivalents; update the file-level Javadoc block and every inline/comment block
(e.g., descriptions for TP-042..TP-046 and other occurrences referenced in the
review) so they convey the same meaning in English, preserving test intent and
tags, and ensure no Chinese characters remain in method/class comments, test
descriptions or inline notes within the ZSha2Ipv6Case class.
In
`@test/src/test/groovy/org/zstack/test/integration/kvm/host/KvmHostIpv6Case.groovy`:
- Around line 14-171: The file KvmHostIpv6Case contains Chinese comments,
assertion messages, and logger texts across the test methods (class
KvmHostIpv6Case and methods testTP015_managementIpColumnLength,
testTP016_addHostWithIpv6Passes, testTP017_fullIpv6NormalizedBeforeConnect,
testTP018_linkLocalIpv6Rejected, testTP019_invalidIpRejected,
testTP020_fullIpv6FitsInColumn) — replace all Chinese text with clear, idiomatic
English equivalents for comments, assert messages, and logger.info messages
(e.g. "TP-015: managementIp should have `@Column` annotation", "TP-016: IPv6
address should pass validation...", "TP-018: link-local IPv6 should be
rejected", etc.), keeping the same assertions and logging calls and leaving
symbol names (HostAO.managementIp,
HostAO.class.getDeclaredField("managementIp"), AddKVMHostAction,
SysErrors.INVALID_ARGUMENT_ERROR) unchanged.
In
`@test/src/test/groovy/org/zstack/test/integration/network/ipv6/MnIpv6StorageMigrationCase.groovy`:
- Around line 7-35: Replace all Chinese comments in the
MnIpv6StorageMigrationCase class with clear, correct English equivalents: update
the class-level Javadoc and all inline/comments inside methods setup(),
environment(), clean(), and any comments between lines ~47-135 to English (e.g.,
describe the test purpose, that these are pure unit/static tests with no Spring
context or environment dependencies, and that no cleanup is required). Ensure
terminology matches original intent (IPv6 storage migration tests, list of
TP-032..TP-038 cases) and fix any spelling/grammar issues while keeping comment
content semantically identical.
- Around line 38-45: The test() method calls TP-032/033/034/035/036/038 but
omits TP-037 while the file header claims TP-032~038; either add the missing
TP-037 test invocation (implement or call a method named
testTP037_<description>() and invoke it from test()) to ensure the full
TP-032~038 range is covered, or update the file header/range comment to
accurately reflect the actual tests executed (e.g., TP-032~036,038) and remove
any reference to TP-037; locate the test() method and the surrounding TP comment
to apply the change.
In
`@test/src/test/groovy/org/zstack/test/integration/network/vxlan/VxlanIpv6Case.groovy`:
- Around line 12-104: Update VxlanIpv6Case to replace all Chinese text with
English: change class/method comments, Javadoc blocks, inline comments,
assertion messages, and logger.info messages in methods
testTP039_vxlanAcceptsIpv6VtepIp, testTP040_vtepVoColumnLength,
checkVtepIpColumnLength, and testTP041_invalidVtepIpRejected to clear, idiomatic
English (no Chinese characters or Pinyin); keep the logic and identifiers
(VxlanIpv6Case, testTP039_vxlanAcceptsIpv6VtepIp, testTP040_vtepVoColumnLength,
checkVtepIpColumnLength, testTP041_invalidVtepIpRejected, VtepVO, RemoteVtepVO,
vtepIp) unchanged while updating only the human-readable strings.
---
Nitpick comments:
In `@test/src/test/groovy/org/zstack/test/integration/core/MnIpv6Case.groovy`:
- Around line 192-201: The test TP-022 (testTP022_sanitizeCallbackUrlBareIpv6)
currently only asserts result != null; update it to assert the expected
normalization of a bare IPv6 URL by invoking RESTFacadeImpl.sanitizeCallbackUrl
and checking that the returned string is the bracketed IPv6 form (e.g.,
"http://[2001:db8::1]:8080/callback") or at minimum that the result differs from
the input and contains both '[' and ']' characters; use the sanitizeCallbackUrl
method reference and replace the weak null check with a concrete assertion on
the normalized value or the presence of brackets.
In `@test/src/test/groovy/org/zstack/test/integration/core/MnIpv6M3Case.groovy`:
- Around line 55-69: The test currently only asserts
NetworkUtils.isIpv4Address/IPv6NetworkUtils.isIpv6Address combinations and
should instead exercise the actual interceptor; update
testTP062_ipmiIpv6AcceptedInInterceptor (and the similar block around lines
89-102) to construct a minimal IPMI request/context, invoke
BaremetalChassisApiInterceptor.check(...) with that request (or the appropriate
interceptor entry method), and assert the interceptor allows the request (no
exception or returns true); use the real interceptor class/method names
(BaremetalChassisApiInterceptor.check) so the test fails on any future
additional validation rather than just the raw isIpv4/isIpv6 booleans.
🪄 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: Path: http://open.zstack.ai:20001/code-reviews/zstack-cloud.yaml (via .coderabbit.yaml)
Review profile: CHILL
Plan: Pro
Run ID: 34128131-c834-43e3-8ba1-7e3978e36802
📒 Files selected for processing (9)
test/src/test/groovy/org/zstack/test/integration/appliancevm/ApplianceVmIpv6Case.groovytest/src/test/groovy/org/zstack/test/integration/core/MnIpv6Case.groovytest/src/test/groovy/org/zstack/test/integration/core/MnIpv6M3Case.groovytest/src/test/groovy/org/zstack/test/integration/core/MnIpv6M4Case.groovytest/src/test/groovy/org/zstack/test/integration/core/ZSha2Ipv6Case.groovytest/src/test/groovy/org/zstack/test/integration/kvm/host/KvmHostIpv6Case.groovytest/src/test/groovy/org/zstack/test/integration/network/ipv6/MnIpv6StorageMigrationCase.groovytest/src/test/groovy/org/zstack/test/integration/network/vxlan/VxlanIpv6Case.groovytest/src/test/groovy/org/zstack/test/integration/utils/IPv6UtilsCase.groovy
🚧 Files skipped from review as they are similar to previous changes (2)
- test/src/test/groovy/org/zstack/test/integration/utils/IPv6UtilsCase.groovy
- test/src/test/groovy/org/zstack/test/integration/core/MnIpv6M4Case.groovy
| * TP-025~029: ApplianceVmFacadeImpl.getMnIpForVr CIDR 匹配逻辑测试 | ||
| * | ||
| * getMnIpForVr 是私有实例方法,通过反射调用。 | ||
| * 不依赖 Spring 上下文(方法内部只使用标准 Java 网络 API 和静态工具方法)。 | ||
| * | ||
| * 覆盖: | ||
| * TP-025 - 使用当前 MN 的 IPv4 CIDR 应返回 MN 的 IP 地址 | ||
| * TP-026 - null CIDR → fallback 到 Platform.getManagementServerIp() | ||
| * TP-027 - 不匹配的 CIDR → fallback 到 Platform.getManagementServerIp() | ||
| * TP-028 - 返回的 IP 地址不含方括号(裸地址) | ||
| * TP-029 - 无效 CIDR → fallback,不抛异常 | ||
| */ |
There was a problem hiding this comment.
请将中文注释与文档字符串统一改为英文
当前类中多处注释/Javadoc 使用了中文,不符合仓库语言规范。请统一改为正确、无拼写错误的英文描述。
As per coding guidelines: **/*.*: 代码里不应当有有中文,包括报错、注释等都应当使用正确的、无拼写错误的英文来写
Also applies to: 25-28, 32-33, 37-38, 42-43, 57-58, 69-70, 76-77, 96-97, 108-109, 122-123, 133-134, 143-143
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@test/src/test/groovy/org/zstack/test/integration/appliancevm/ApplianceVmIpv6Case.groovy`
around lines 11 - 22, Update all Chinese comments and Javadoc in the
ApplianceVmIpv6Case class to fluent, correct English: replace the class-level
Javadoc (describing TP-025–TP-029 and the purpose of testing
ApplianceVmFacadeImpl.getMnIpForVr), all inline comments inside test methods,
and any other Chinese annotations/comments (including those referenced around
the TP cases and the noted locations) with concise English descriptions and
correct spelling/grammar; ensure test intent, expected behaviors (e.g., CIDR
matching, fallbacks to Platform.getManagementServerIp(), bracketless IP return,
and invalid CIDR behavior), and any TODOs/assertion explanations are preserved
accurately in the new English text.
| // 使用一个极不可能匹配当前主机任何网卡的 CIDR | ||
| String unmatchedCidr = "10.99.88.0/24" | ||
| String result = callGetMnIpForVr(unmatchedCidr) | ||
| assert result != null : "TP-027: getMnIpForVr(unmatched CIDR) should return non-null IP" | ||
| assert result == mnIp : | ||
| "TP-027: getMnIpForVr('$unmatchedCidr') should fallback to MN IP, expected '$mnIp', got '$result'" |
There was a problem hiding this comment.
TP-027 的未命中 CIDR 用例有环境相关误报风险
10.99.88.0/24 在真实私网环境可能被实际网卡命中,测试会偶发失败(返回匹配网卡 IP 而非 fallback)。建议改为按实现逻辑“必然被跳过/不匹配”的网段。
建议修改
void testTP027_unmatchedCidrFallback() {
String mnIp = Platform.getManagementServerIp()
- // 使用一个极不可能匹配当前主机任何网卡的 CIDR
- String unmatchedCidr = "10.99.88.0/24"
+ // 选择会被实现逻辑过滤掉的网段,避免环境偶发命中:
+ // - IPv4 场景使用 IPv6 link-local(方法内会跳过 link-local 地址)
+ // - IPv6 场景使用 IPv4 loopback(方法内会跳过 loopback 地址)
+ String unmatchedCidr = mnIp?.contains(":") ? "127.0.0.0/8" : "fe80::/10"
String result = callGetMnIpForVr(unmatchedCidr)
assert result != null : "TP-027: getMnIpForVr(unmatched CIDR) should return non-null IP"
assert result == mnIp :📝 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.
| // 使用一个极不可能匹配当前主机任何网卡的 CIDR | |
| String unmatchedCidr = "10.99.88.0/24" | |
| String result = callGetMnIpForVr(unmatchedCidr) | |
| assert result != null : "TP-027: getMnIpForVr(unmatched CIDR) should return non-null IP" | |
| assert result == mnIp : | |
| "TP-027: getMnIpForVr('$unmatchedCidr') should fallback to MN IP, expected '$mnIp', got '$result'" | |
| void testTP027_unmatchedCidrFallback() { | |
| String mnIp = Platform.getManagementServerIp() | |
| // Use network segments that will be filtered out by implementation logic | |
| // to avoid environment-dependent false matches: | |
| // - For IPv4 scenarios: use IPv6 link-local (skipped by method implementation) | |
| // - For IPv6 scenarios: use IPv4 loopback (skipped by method implementation) | |
| String unmatchedCidr = mnIp?.contains(":") ? "127.0.0.0/8" : "fe80::/10" | |
| String result = callGetMnIpForVr(unmatchedCidr) | |
| assert result != null : "TP-027: getMnIpForVr(unmatched CIDR) should return non-null IP" | |
| assert result == mnIp : | |
| "TP-027: getMnIpForVr('$unmatchedCidr') should fallback to MN IP, expected '$mnIp', got '$result'" | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@test/src/test/groovy/org/zstack/test/integration/appliancevm/ApplianceVmIpv6Case.groovy`
around lines 112 - 117, The test uses an environment-dependent CIDR
"10.99.88.0/24" which can match real host interfaces; change the unmatchedCidr
constant in the test method (where callGetMnIpForVr is invoked) to a
documentation/reserved block that won't exist in real networks (e.g.,
"192.0.2.0/24" per RFC5737) so callGetMnIpForVr(unmatchedCidr) deterministically
falls back to mnIp; update the string assignment for unmatchedCidr and keep the
same assertions around callGetMnIpForVr and mnIp.
| /** | ||
| * TP-001~007, TP-021~024, TP-030~031: 管理节点 IPv6 支持核心测试 | ||
| * | ||
| * 全部为纯单元 / 反射测试,无需 Spring 上下文。 | ||
| * 由 CoreLibraryTest.runSubCases() 自动发现并运行。 | ||
| * | ||
| * 覆盖: | ||
| * TP-001 - NetworkGlobalConfig.PREFER_IPV6 默认值为 false | ||
| * TP-002 - NetworkGlobalConfig.PREFER_IPV6 category 和 name 正确 | ||
| * TP-003 - Platform.getManagementServerIp() 在 IPv4-only 环境返回非 null IP | ||
| * TP-004 - PREFER_IPV6=false(默认)时返回 IPv4(CI 环境验证格式) | ||
| * TP-005 - PREFER_IPV6=true 时能回退到 IPv4(无 IPv6 接口不抛异常) | ||
| * TP-006 - getManagementServerCidr() 非 null 或不抛异常(IPv4 环境返回 CIDR 格式) | ||
| * TP-007 - CIDR 格式合法(包含 "/",prefix <= 32/128) | ||
| * TP-021 - sanitizeCallbackUrl(IPv4 URL) → 原样返回 | ||
| * TP-022 - sanitizeCallbackUrl(裸 IPv6 URL) → 修正为带括号格式或原样保留 | ||
| * TP-023 - Platform.getManagementServerId() 返回非 null UUID 格式字符串 | ||
| * TP-024 - 连续两次调用返回相同 UUID(已持久化) | ||
| * TP-030 - jgroupsAddr(IPv6, port) → "[ip][port]" 格式 | ||
| * TP-031 - jgroupsAddr(IPv4, port) → "ip[port]" 格式 | ||
| */ | ||
| class MnIpv6Case extends SubCase { | ||
|
|
||
| @Override | ||
| void setup() { | ||
| // 纯单元 / 静态方法测试,无需 Spring | ||
| } | ||
|
|
||
| @Override | ||
| void environment() { | ||
| // 无环境依赖 | ||
| } | ||
|
|
||
| @Override | ||
| void clean() { | ||
| // 无需清理 | ||
| } | ||
|
|
||
| @Override | ||
| void test() { | ||
| testTP001_preferIpv6DefaultValue() // TP-001 | ||
| testTP002_preferIpv6CategoryAndName() // TP-002 | ||
| testTP003_getManagementServerIpNonNull() // TP-003 | ||
| testTP004_getManagementServerIpIpv4() // TP-004 | ||
| testTP005_getManagementServerIpFallback() // TP-005 | ||
| testTP006_getManagementServerCidrFormat() // TP-006 | ||
| testTP007_getManagementServerCidrValid() // TP-007 | ||
| testTP021_sanitizeCallbackUrlIpv4() // TP-021 | ||
| testTP022_sanitizeCallbackUrlBareIpv6() // TP-022 | ||
| testTP023_getManagementServerIdNonNull() // TP-023 | ||
| testTP024_getManagementServerIdStable() // TP-024 | ||
| testTP030_jgroupsAddrIpv6() // TP-030 | ||
| testTP031_jgroupsAddrIpv4() // TP-031 | ||
| } | ||
|
|
||
| // ===== F-001: GlobalConfig PREFER_IPV6 ===== | ||
|
|
||
| /** | ||
| * TP-001: NetworkGlobalConfig.PREFER_IPV6 默认值注解为 "false" | ||
| */ | ||
| void testTP001_preferIpv6DefaultValue() { | ||
| Field field = NetworkGlobalConfig.class.getDeclaredField("PREFER_IPV6") | ||
| GlobalConfigDef annotation = field.getAnnotation(GlobalConfigDef.class) | ||
| assert annotation != null : "TP-001: PREFER_IPV6 should have @GlobalConfigDef annotation" | ||
| assert annotation.defaultValue() == "false" : "TP-001: PREFER_IPV6 defaultValue should be 'false', got: ${annotation.defaultValue()}" | ||
| logger.info("TP-001: PREFER_IPV6 defaultValue = '${annotation.defaultValue()}'") | ||
| } | ||
|
|
||
| /** | ||
| * TP-002: NetworkGlobalConfig.PREFER_IPV6 的 category 和 name 正确 | ||
| */ | ||
| void testTP002_preferIpv6CategoryAndName() { | ||
| String category = NetworkGlobalConfig.PREFER_IPV6.getCategory() | ||
| String name = NetworkGlobalConfig.PREFER_IPV6.getName() | ||
| assert category == "network" : "TP-002: PREFER_IPV6 category should be 'network', got: $category" | ||
| assert name == "management.server.prefer.ipv6" : | ||
| "TP-002: PREFER_IPV6 name should be 'management.server.prefer.ipv6', got: $name" | ||
| logger.info("TP-002: PREFER_IPV6 category='$category', name='$name'") | ||
| } | ||
|
|
||
| // ===== F-002: Platform.getManagementServerIp ===== | ||
|
|
||
| /** | ||
| * TP-003: Platform.getManagementServerIp() 在 IPv4-only 环境返回非 null 地址 | ||
| */ | ||
| void testTP003_getManagementServerIpNonNull() { | ||
| String ip = Platform.getManagementServerIp() | ||
| assert ip != null : "TP-003: getManagementServerIp() should return non-null" | ||
| boolean isIp = NetworkUtils.isIpv4Address(ip) || IPv6NetworkUtils.isIpv6Address(ip) | ||
| assert isIp : "TP-003: getManagementServerIp() should return valid IP, got: $ip" | ||
| logger.info("TP-003: getManagementServerIp() = $ip") | ||
| } | ||
|
|
||
| /** | ||
| * TP-004: PREFER_IPV6=false(默认值)时,CI IPv4 环境返回 IPv4 格式 | ||
| */ | ||
| void testTP004_getManagementServerIpIpv4() { | ||
| String ip = Platform.getManagementServerIp() | ||
| assert ip != null : "TP-004: getManagementServerIp() should not be null" | ||
| // CI 环境为 IPv4-only,PREFER_IPV6 默认 false,返回 IPv4 地址 | ||
| logger.info("TP-004: management server IP = $ip (preferIpv6=false default)") | ||
| // 验证是合法的 IP 地址格式 | ||
| boolean isValidIp = NetworkUtils.isIpv4Address(ip) || IPv6NetworkUtils.isIpv6Address(ip) | ||
| assert isValidIp : "TP-004: should be valid IP, got: $ip" | ||
| } | ||
|
|
||
| /** | ||
| * TP-005: PREFER_IPV6=true 时(无 IPv6 接口)能回退到 IPv4,不抛异常 | ||
| */ | ||
| void testTP005_getManagementServerIpFallback() { | ||
| // Platform.getManagementServerIp() 内部异常安全降级;此处验证方法不抛出异常 | ||
| String ip = null | ||
| try { | ||
| ip = Platform.getManagementServerIp() | ||
| } catch (Exception e) { | ||
| assert false : "TP-005: getManagementServerIp() should not throw exception even when PREFER_IPV6=true with no IPv6, got: ${e.message}" | ||
| } | ||
| assert ip != null : "TP-005: getManagementServerIp() should return fallback IP, not null" | ||
| logger.info("TP-005: PREFER_IPV6 fallback returns $ip") | ||
| } | ||
|
|
||
| // ===== F-003: getManagementServerCidr ===== | ||
|
|
||
| /** | ||
| * TP-006: getManagementServerCidr() 不抛异常(IPv4 环境应返回 CIDR 格式字符串) | ||
| */ | ||
| void testTP006_getManagementServerCidrFormat() { | ||
| String cidr = null | ||
| try { | ||
| cidr = Platform.getManagementServerCidr() | ||
| } catch (Exception e) { | ||
| assert false : "TP-006: getManagementServerCidr() should not throw, got: ${e.message}" | ||
| } | ||
| // cidr 在 CI 环境可能为 null(当 management IP 不在 ip add 输出中时),跳过 null 断言 | ||
| if (cidr != null) { | ||
| assert cidr.contains("/") : "TP-006: CIDR should contain '/', got: $cidr" | ||
| } | ||
| logger.info("TP-006: getManagementServerCidr() = $cidr") | ||
| } | ||
|
|
||
| /** | ||
| * TP-007: CIDR 格式合法(包含 "/",prefix <= 32 for IPv4 / <= 128 for IPv6) | ||
| */ | ||
| void testTP007_getManagementServerCidrValid() { | ||
| String cidr = Platform.getManagementServerCidr() | ||
| if (cidr == null) { | ||
| logger.warn("TP-007: getManagementServerCidr() returned null in this environment, skipping prefix validation") | ||
| return | ||
| } | ||
| assert cidr.contains("/") : "TP-007: CIDR should contain '/', got: $cidr" | ||
| String[] parts = cidr.split("/") | ||
| assert parts.length == 2 : "TP-007: CIDR should have exactly 2 parts, got: $cidr" | ||
| int prefix = Integer.parseInt(parts[1].trim()) | ||
| String network = parts[0] | ||
| if (NetworkUtils.isIpv4Address(network) || network.contains(".")) { | ||
| assert prefix >= 0 && prefix <= 32 : "TP-007: IPv4 prefix should be 0-32, got: $prefix" | ||
| } else { | ||
| assert prefix >= 0 && prefix <= 128 : "TP-007: IPv6 prefix should be 0-128, got: $prefix" | ||
| } | ||
| logger.info("TP-007: CIDR '$cidr' is valid (prefix=$prefix)") | ||
| } | ||
|
|
||
| // ===== F-007: RESTFacadeImpl.sanitizeCallbackUrl ===== | ||
|
|
||
| /** | ||
| * TP-021: sanitizeCallbackUrl(IPv4 URL) → 原样返回(IPv4 无括号变化) | ||
| */ | ||
| void testTP021_sanitizeCallbackUrlIpv4() { | ||
| Method method = RESTFacadeImpl.class.getDeclaredMethod("sanitizeCallbackUrl", String.class) | ||
| method.setAccessible(true) | ||
|
|
||
| String ipv4Url = "http://192.168.1.1:8080/callback" | ||
| String result = method.invoke(null, ipv4Url) as String | ||
| assert result == ipv4Url : "TP-021: IPv4 callback URL should be returned unchanged, got: $result" | ||
| logger.info("TP-021: sanitizeCallbackUrl('$ipv4Url') = '$result'") | ||
| } | ||
|
|
||
| /** | ||
| * TP-022: sanitizeCallbackUrl(裸 IPv6 URL) → 检测裸 IPv6 并修正(或原样保留 + WARN) | ||
| */ | ||
| void testTP022_sanitizeCallbackUrlBareIpv6() { | ||
| Method method = RESTFacadeImpl.class.getDeclaredMethod("sanitizeCallbackUrl", String.class) | ||
| method.setAccessible(true) | ||
|
|
||
| String bareIpv6Url = "http://2001:db8::1:8080/callback" | ||
| String result = method.invoke(null, bareIpv6Url) as String | ||
| assert result != null : "TP-022: sanitizeCallbackUrl should not return null for bare IPv6 URL" | ||
| logger.info("TP-022: sanitizeCallbackUrl('$bareIpv6Url') = '$result'") | ||
| } | ||
|
|
||
| // ===== F-008: UUID 持久化 ===== | ||
|
|
||
| /** | ||
| * TP-023: Platform.getManagementServerId() 返回非 null 的 UUID 格式字符串 | ||
| */ | ||
| void testTP023_getManagementServerIdNonNull() { | ||
| String msId = Platform.getManagementServerId() | ||
| // msId 由 UUID.nameUUIDFromBytes(getManagementServerIp().getBytes()) 生成,去掉 "-" 后为 32 位十六进制字符串 | ||
| if (msId != null) { | ||
| assert msId.length() == 32 : "TP-023: management server ID should be 32-char hex UUID, got length: ${msId.length()}" | ||
| assert msId.matches("[0-9a-f]+") : "TP-023: management server ID should be lowercase hex, got: $msId" | ||
| logger.info("TP-023: getManagementServerId() = $msId") | ||
| } else { | ||
| // 在无 Spring 初始化的单元测试中 msId 可能为 null,记录警告 | ||
| logger.warn("TP-023: getManagementServerId() returned null (Platform may not be fully initialized)") | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * TP-024: 连续两次调用 getManagementServerId() 返回相同 UUID(已持久化) | ||
| */ | ||
| void testTP024_getManagementServerIdStable() { | ||
| String id1 = Platform.getManagementServerId() | ||
| String id2 = Platform.getManagementServerId() | ||
| if (id1 != null) { | ||
| assert id1 == id2 : "TP-024: getManagementServerId() should return stable UUID, got: '$id1' vs '$id2'" | ||
| logger.info("TP-024: getManagementServerId() is stable: $id1") | ||
| } else { | ||
| logger.warn("TP-024: getManagementServerId() returned null twice (Platform may not be fully initialized)") | ||
| } | ||
| } | ||
|
|
||
| // ===== F-010: JGroups IPv6 括号修复 ===== | ||
|
|
||
| /** | ||
| * TP-030: jgroupsAddr(IPv6, port) → "[2001:db8::1][7805]" | ||
| */ | ||
| void testTP030_jgroupsAddrIpv6() { | ||
| Method method = Platform.class.getDeclaredMethod("jgroupsAddr", String.class, String.class) | ||
| method.setAccessible(true) | ||
|
|
||
| String result = method.invoke(null, "2001:db8::1", "7805") as String | ||
| assert result == "[2001:db8::1][7805]" : | ||
| "TP-030: IPv6 jgroupsAddr should use [addr][port] format, got: $result" | ||
| logger.info("TP-030: jgroupsAddr('2001:db8::1', '7805') = '$result'") | ||
| } | ||
|
|
||
| /** | ||
| * TP-031: jgroupsAddr(IPv4, port) → "192.168.1.1[7805]"(IPv4 不加括号) | ||
| */ | ||
| void testTP031_jgroupsAddrIpv4() { | ||
| Method method = Platform.class.getDeclaredMethod("jgroupsAddr", String.class, String.class) | ||
| method.setAccessible(true) | ||
|
|
||
| String result = method.invoke(null, "192.168.1.1", "7805") as String | ||
| assert result == "192.168.1.1[7805]" : | ||
| "TP-031: IPv4 jgroupsAddr should use addr[port] format, got: $result" | ||
| logger.info("TP-031: jgroupsAddr('192.168.1.1', '7805') = '$result'") | ||
| } |
There was a problem hiding this comment.
请将该文件中的中文注释/断言/日志全部替换为英文。
目前中文文本覆盖范围较大,直接违反仓库统一规范,建议在当前提交中统一修正。
As per coding guidelines 代码里不应当有有中文,包括报错、注释等都应当使用正确的、无拼写错误的英文来写.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@test/src/test/groovy/org/zstack/test/integration/core/MnIpv6Case.groovy`
around lines 14 - 262, The file MnIpv6Case contains extensive Chinese comments,
assertion messages and logger texts; replace all Chinese text with clear English
equivalents across the class header comment and inside each test method
(including messages in assertions, logger.info/warn calls and inline comments)
while keeping the original intent and test names (e.g.,
testTP001_preferIpv6DefaultValue, testTP002_preferIpv6CategoryAndName,
testTP003_getManagementServerIpNonNull, testTP004_getManagementServerIpIpv4,
testTP005_getManagementServerIpFallback,
testTP006_getManagementServerCidrFormat, testTP007_getManagementServerCidrValid,
testTP021_sanitizeCallbackUrlIpv4, testTP022_sanitizeCallbackUrlBareIpv6,
testTP023_getManagementServerIdNonNull, testTP024_getManagementServerIdStable,
testTP030_jgroupsAddrIpv6, testTP031_jgroupsAddrIpv4); ensure assertion strings
and logger messages are idiomatic English and retain unique identifiers like
PREFER_IPV6, Platform.getManagementServerIp(),
Platform.getManagementServerCidr(), RESTFacadeImpl.sanitizeCallbackUrl(), and
Platform.jgroupsAddr() so tests behave identically.
| * TP-005: PREFER_IPV6=true 时(无 IPv6 接口)能回退到 IPv4,不抛异常 | ||
| */ | ||
| void testTP005_getManagementServerIpFallback() { | ||
| // Platform.getManagementServerIp() 内部异常安全降级;此处验证方法不抛出异常 | ||
| String ip = null | ||
| try { | ||
| ip = Platform.getManagementServerIp() | ||
| } catch (Exception e) { | ||
| assert false : "TP-005: getManagementServerIp() should not throw exception even when PREFER_IPV6=true with no IPv6, got: ${e.message}" | ||
| } | ||
| assert ip != null : "TP-005: getManagementServerIp() should return fallback IP, not null" | ||
| logger.info("TP-005: PREFER_IPV6 fallback returns $ip") | ||
| } |
There was a problem hiding this comment.
TP-005 当前没有真正验证 “PREFER_IPV6=true 的回退行为”。
方法名与注释声明了 PREFER_IPV6=true 场景,但测试体内并未切换该配置,实际只是在默认配置下重复调用 getManagementServerIp(),会导致用例目标与断言对象不一致。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@test/src/test/groovy/org/zstack/test/integration/core/MnIpv6Case.groovy`
around lines 121 - 133, The test testTP005_getManagementServerIpFallback
currently never toggles the PREFER_IPV6 flag, so update the test to explicitly
enable PREFER_IPV6=true before calling Platform.getManagementServerIp(),
simulate or ensure no IPv6 interfaces are present (or mock the environment used
by Platform.getManagementServerIp()), then call Platform.getManagementServerIp()
inside the try/catch and assert it returns a non-null IPv4 fallback (e.g.,
verify the returned string does not contain ':'), and finally restore the
original PREFER_IPV6 setting in a finally block to avoid side effects; reference
symbols: testTP005_getManagementServerIpFallback,
Platform.getManagementServerIp(), and PREFER_IPV6.
| /** | ||
| * TP-062~069, TP-076, TP-077: 管理节点 IPv6 M3 支持测试 | ||
| * | ||
| * 全部为纯单元测试,无需 Spring 上下文。 | ||
| * 由 CoreLibraryTest.runSubCases() 自动发现并运行。 | ||
| * | ||
| * 覆盖: | ||
| * TP-062 - AddBaremetalChassisAction 接受 IPv6 IPMI 地址 | ||
| * TP-064 - ipmiAddress 字段可存储完整 IPv6 地址(39 字符) | ||
| * TP-065 - 非法 IPMI 地址被拒绝 | ||
| * TP-066 - Console Proxy URL 使用 IPv6 括号 | ||
| * TP-067 - VNC Token URL hostname 含 IPv6 括号 | ||
| * TP-069 - 双栈 MN 下 Console URL 使用管理 VIP | ||
| * TP-076 - BM V2 DPU 回调 IP IPv6 括号 | ||
| * TP-077 - COLO QEMU URL IPv6 括号 | ||
| */ |
There was a problem hiding this comment.
请将中文注释统一改为英文(当前违反仓库编码规范)
本文件在类注释、方法注释和行内注释中包含大量中文(例如 Line 9、Line 53、Line 139 等),与仓库规则冲突,建议在本次提交中统一替换为准确英文表述。
示例修正(节选)
- * TP-062~069, TP-076, TP-077: 管理节点 IPv6 M3 支持测试
+ * TP-062~069, TP-076, TP-077: Management-node IPv6 M3 support tests
- * 全部为纯单元测试,无需 Spring 上下文。
+ * Pure unit tests only; no Spring context required.
- // ===== TP-062: AddBaremetalChassisAction 接受 IPv6 IPMI 地址 =====
+ // ===== TP-062: AddBaremetalChassisAction accepts IPv6 IPMI address =====As per coding guidelines: 代码里不应当有有中文,包括报错、注释等都应当使用正确的、无拼写错误的英文来写.
Also applies to: 53-57, 67-67, 72-72, 75-75, 87-90, 98-98, 105-112, 136-140, 157-161, 177-181, 197-201, 213-213
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@test/src/test/groovy/org/zstack/test/integration/core/MnIpv6M3Case.groovy`
around lines 8 - 23, The file-level and inline comments in the MnIpv6M3Case
Groovy test (class MnIpv6M3Case) contain Chinese text that violates repository
policy; update all class-level Javadoc, method comments, and inline comments
(including the header block and the locations noted around lines corresponding
to the case descriptions and method comments) to clear, idiomatic English that
conveys the same meaning (e.g., translate test purpose, covered TPs, and inline
notes), and also convert any Chinese text in exception/error messages or log
strings to English; ensure no Chinese characters remain in comments, strings, or
annotations before committing.
| testTP043_isMasterGrepPatternMatchesIpv6() // TP-042 | ||
| testTP042_isMasterGrepLogicWithVip() // TP-043 |
There was a problem hiding this comment.
TP 编号与方法命名映射反了,建议直接修正。
Line 37-38 目前依赖注释“互换”来对齐 TP,但 Line 48/71 的方法名仍与各自 TP 描述不一致,容易导致追踪矩阵和测试报表误判。建议把方法名与调用顺序统一为同一 TP 编号语义。
🔧 建议修改
@@
- testTP043_isMasterGrepPatternMatchesIpv6() // TP-042
- testTP042_isMasterGrepLogicWithVip() // TP-043
+ testTP042_isMasterGrepPatternMatchesIpv6() // TP-042
+ testTP043_isMasterGrepLogicWithVip() // TP-043
@@
- void testTP043_isMasterGrepPatternMatchesIpv6() {
+ void testTP042_isMasterGrepPatternMatchesIpv6() {
@@
- void testTP042_isMasterGrepLogicWithVip() {
+ void testTP043_isMasterGrepLogicWithVip() {Also applies to: 48-49, 71-72
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@test/src/test/groovy/org/zstack/test/integration/core/ZSha2Ipv6Case.groovy`
around lines 37 - 38, The TP numbers are swapped between the two test methods:
rename testTP043_isMasterGrepPatternMatchesIpv6 and
testTP042_isMasterGrepLogicWithVip so that each method name reflects the correct
TP number (i.e., method that implements TP-042 should be named testTP042_... and
the TP-043 implementation should be testTP043_...); update all call sites and
references (including the other occurrences noted for the same pair) and any
inline comments so the invocation order, method names, and TP comments are
consistent across the file and test reports.
| /** | ||
| * TP-015~020: KVM 宿主机 IPv6 管理 IP 测试 | ||
| * | ||
| * 覆盖: | ||
| * TP-015 - managementIp 列长度足够存储 39 字符全展开 IPv6(@Column length >= 39) | ||
| * TP-016 - 以合法 IPv6 调用 AddKVMHostAction:拦截器不因 INVALID_ARGUMENT_ERROR 拒绝 | ||
| * TP-017 - 全展开 IPv6 经 interceptor 规范化后不触发 INVALID_ARGUMENT_ERROR | ||
| * TP-018 - 链路本地地址 "fe80::1%eth0" 被拒绝(INVALID_ARGUMENT_ERROR) | ||
| * TP-019 - 非法格式 "not-an-ip!!" 被拒绝(INVALID_ARGUMENT_ERROR) | ||
| * TP-020 - 39 字符全展开 IPv6 不被 DB 截断(与 TP-015 列长度验证合并) | ||
| */ | ||
| class KvmHostIpv6Case extends SubCase { | ||
|
|
||
| EnvSpec env | ||
| ClusterInventory cluster | ||
|
|
||
| @Override | ||
| void setup() { | ||
| useSpring(KvmTest.springSpec) | ||
| } | ||
|
|
||
| @Override | ||
| void environment() { | ||
| env = HostEnv.noHostBasicEnv() | ||
| } | ||
|
|
||
| @Override | ||
| void clean() { | ||
| env.delete() | ||
| } | ||
|
|
||
| @Override | ||
| void test() { | ||
| env.create { | ||
| cluster = env.inventoryByName("cluster") as ClusterInventory | ||
|
|
||
| testTP015_managementIpColumnLength() // TP-015 | ||
| testTP016_addHostWithIpv6Passes() // TP-016 | ||
| testTP017_fullIpv6NormalizedBeforeConnect() // TP-017 | ||
| testTP018_linkLocalIpv6Rejected() // TP-018 | ||
| testTP019_invalidIpRejected() // TP-019 | ||
| testTP020_fullIpv6FitsInColumn() // TP-020 | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * TP-015: HostVO.managementIp 列(继承自 HostAO)接受 39 字符全展开 IPv6 不截断。 | ||
| * 验证 @Column(length = ...) >= 39。 | ||
| */ | ||
| void testTP015_managementIpColumnLength() { | ||
| Field field = HostAO.class.getDeclaredField("managementIp") | ||
| field.setAccessible(true) | ||
| Column col = field.getAnnotation(Column.class) | ||
| assert col != null : "TP-015: managementIp should have @Column annotation" | ||
| assert col.length() >= 39 : "TP-015: managementIp column length ${col.length()} is too short for 39-char full-expanded IPv6" | ||
| logger.info("TP-015: managementIp @Column length = ${col.length()}, sufficient for IPv6") | ||
| } | ||
|
|
||
| /** | ||
| * TP-016: 以合法 IPv6 地址 "2001:db8::10" 调用 AddKVMHostAction。 | ||
| * API 拦截器不因 INVALID_ARGUMENT_ERROR 拒绝 IPv6(连接失败是预期行为)。 | ||
| */ | ||
| void testTP016_addHostWithIpv6Passes() { | ||
| def action = new AddKVMHostAction() | ||
| action.sessionId = adminSession() | ||
| action.clusterUuid = cluster.uuid | ||
| action.managementIp = "2001:db8::10" | ||
| action.name = "kvm-ipv6-compressed" | ||
| action.username = "root" | ||
| action.password = "password" | ||
| def res = action.call() | ||
|
|
||
| // IPv6 校验通过后才会尝试连接;连接失败不是 INVALID_ARGUMENT_ERROR | ||
| if (res.error != null) { | ||
| assert res.error.code != SysErrors.INVALID_ARGUMENT_ERROR.toString() : | ||
| "TP-016: IPv6 address should pass validation (interceptor should not return INVALID_ARGUMENT_ERROR), got: ${res.error.code} - ${res.error.description}" | ||
| } | ||
| logger.info("TP-016: AddKVMHostAction with IPv6 passed API validation (error=${res.error?.code})") | ||
| } | ||
|
|
||
| /** | ||
| * TP-017: 全展开 IPv6 地址输入,经 HostApiInterceptor.normalizeIpv6 规范化,不触发 INVALID_ARGUMENT_ERROR。 | ||
| * 规范化:2001:0db8:0000:0000:0000:0000:0000:0001 → 2001:db8::1 | ||
| */ | ||
| void testTP017_fullIpv6NormalizedBeforeConnect() { | ||
| String fullIpv6 = "2001:0db8:0000:0000:0000:0000:0000:0001" | ||
| def action = new AddKVMHostAction() | ||
| action.sessionId = adminSession() | ||
| action.clusterUuid = cluster.uuid | ||
| action.managementIp = fullIpv6 | ||
| action.name = "kvm-ipv6-full" | ||
| action.username = "root" | ||
| action.password = "password" | ||
| def res = action.call() | ||
|
|
||
| // normalizeIpv6 后的压缩地址可通过 isValidManagementIp 校验,不返回 INVALID_ARGUMENT_ERROR | ||
| if (res.error != null) { | ||
| assert res.error.code != SysErrors.INVALID_ARGUMENT_ERROR.toString() : | ||
| "TP-017: full-expanded IPv6 should normalize and pass validation, got: ${res.error.code}" | ||
| } | ||
| logger.info("TP-017: full-expanded IPv6 normalized before connect (error=${res.error?.code})") | ||
| } | ||
|
|
||
| /** | ||
| * TP-018: 链路本地地址 "fe80::1%eth0" 应被 HostApiInterceptor 拒绝。 | ||
| * 期望错误码:SysErrors.INVALID_ARGUMENT_ERROR | ||
| */ | ||
| void testTP018_linkLocalIpv6Rejected() { | ||
| def action = new AddKVMHostAction() | ||
| action.sessionId = adminSession() | ||
| action.clusterUuid = cluster.uuid | ||
| action.managementIp = "fe80::1%eth0" | ||
| action.name = "kvm-ipv6-linklocal" | ||
| action.username = "root" | ||
| action.password = "password" | ||
| def res = action.call() | ||
|
|
||
| assert res.error != null : "TP-018: link-local IPv6 should be rejected" | ||
| assert res.error.code == SysErrors.INVALID_ARGUMENT_ERROR.toString() : | ||
| "TP-018: expected INVALID_ARGUMENT_ERROR for link-local IPv6, got: ${res.error.code}" | ||
| logger.info("TP-018: link-local IPv6 correctly rejected with ${res.error.code}") | ||
| } | ||
|
|
||
| /** | ||
| * TP-019: 非法格式 "not-an-ip!!" 应被 HostApiInterceptor 拒绝。 | ||
| * 期望错误码:SysErrors.INVALID_ARGUMENT_ERROR | ||
| */ | ||
| void testTP019_invalidIpRejected() { | ||
| def action = new AddKVMHostAction() | ||
| action.sessionId = adminSession() | ||
| action.clusterUuid = cluster.uuid | ||
| action.managementIp = "not-an-ip!!" | ||
| action.name = "kvm-invalid-ip" | ||
| action.username = "root" | ||
| action.password = "password" | ||
| def res = action.call() | ||
|
|
||
| assert res.error != null : "TP-019: invalid IP format should be rejected" | ||
| assert res.error.code == SysErrors.INVALID_ARGUMENT_ERROR.toString() : | ||
| "TP-019: expected INVALID_ARGUMENT_ERROR for invalid IP, got: ${res.error.code}" | ||
| logger.info("TP-019: invalid IP correctly rejected with ${res.error.code}") | ||
| } | ||
|
|
||
| /** | ||
| * TP-020: 39 字符全展开 IPv6 不被 DB 截断。 | ||
| * 与 TP-015 合并验证 @Column length >= 39。 | ||
| * 全展开 IPv6 最长为 "2001:0db8:0000:0000:0000:0000:0000:0001" = 39 字符。 | ||
| */ | ||
| void testTP020_fullIpv6FitsInColumn() { | ||
| String fullIpv6 = "2001:0db8:0000:0000:0000:0000:0000:0001" | ||
| assert fullIpv6.length() == 39 : "Precondition: full-expanded IPv6 should be 39 chars" | ||
|
|
||
| Field field = HostAO.class.getDeclaredField("managementIp") | ||
| field.setAccessible(true) | ||
| Column col = field.getAnnotation(Column.class) | ||
| assert col.length() >= fullIpv6.length() : | ||
| "TP-020: managementIp column length ${col.length()} is insufficient for 39-char full-expanded IPv6" | ||
| logger.info("TP-020: column length ${col.length()} >= 39, no truncation for full-expanded IPv6") |
There was a problem hiding this comment.
请将测试中的中文注释、断言文案与日志文案统一改为英文。
该文件包含大量中文文本(注释、assert message、logger 文案),与仓库编码规范不一致,建议在本 PR 内统一替换为英文,避免后续一致性和可维护性问题。
As per coding guidelines 代码里不应当有有中文,包括报错、注释等都应当使用正确的、无拼写错误的英文来写.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@test/src/test/groovy/org/zstack/test/integration/kvm/host/KvmHostIpv6Case.groovy`
around lines 14 - 171, The file KvmHostIpv6Case contains Chinese comments,
assertion messages, and logger texts across the test methods (class
KvmHostIpv6Case and methods testTP015_managementIpColumnLength,
testTP016_addHostWithIpv6Passes, testTP017_fullIpv6NormalizedBeforeConnect,
testTP018_linkLocalIpv6Rejected, testTP019_invalidIpRejected,
testTP020_fullIpv6FitsInColumn) — replace all Chinese text with clear, idiomatic
English equivalents for comments, assert messages, and logger.info messages
(e.g. "TP-015: managementIp should have `@Column` annotation", "TP-016: IPv6
address should pass validation...", "TP-018: link-local IPv6 should be
rejected", etc.), keeping the same assertions and logging calls and leaving
symbol names (HostAO.managementIp,
HostAO.class.getDeclaredField("managementIp"), AddKVMHostAction,
SysErrors.INVALID_ARGUMENT_ERROR) unchanged.
| /** | ||
| * TP-032~038: 存储迁移网络 IPv6 支持测试 | ||
| * | ||
| * 全部为纯单元 / 静态方法测试,无需 Spring 上下文。 | ||
| * | ||
| * 覆盖: | ||
| * TP-032 - NFS 主存储创建,存储 CIDR = IPv6 CIDR 验证不报 INVALID_ARGUMENT_ERROR | ||
| * TP-033 - NetworkUtils.isIpInCidr() 匹配 IPv6 地址在 IPv6 CIDR 内 | ||
| * TP-034 - isIpInCidr() 无匹配时返回 false(fallback 逻辑) | ||
| * TP-035 - Ceph MonUri 解析 [IPv6] 括号输入:buildAddr IPv6 → "[ip]:port" | ||
| * TP-036 - Ceph monAddr 输出格式 [ipv6]:port | ||
| * TP-038 - checkMigrateNetworkCidrOfHost fallback 逻辑 | ||
| */ | ||
| class MnIpv6StorageMigrationCase extends SubCase { | ||
|
|
||
| @Override | ||
| void setup() { | ||
| // 纯单元测试,无需 Spring | ||
| } | ||
|
|
||
| @Override | ||
| void environment() { | ||
| // 无环境依赖 | ||
| } | ||
|
|
||
| @Override | ||
| void clean() { | ||
| // 无需清理 | ||
| } |
There was a problem hiding this comment.
请将该文件中的中文注释统一改为英文
当前类注释、方法注释和多处行内注释使用了中文,违反仓库统一规范。请改为语义准确、拼写正确的英文表述。
As per coding guidelines **/*.*: - 代码里不应当有有中文,包括报错、注释等都应当使用正确的、无拼写错误的英文来写
Also applies to: 47-135
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@test/src/test/groovy/org/zstack/test/integration/network/ipv6/MnIpv6StorageMigrationCase.groovy`
around lines 7 - 35, Replace all Chinese comments in the
MnIpv6StorageMigrationCase class with clear, correct English equivalents: update
the class-level Javadoc and all inline/comments inside methods setup(),
environment(), clean(), and any comments between lines ~47-135 to English (e.g.,
describe the test purpose, that these are pure unit/static tests with no Spring
context or environment dependencies, and that no cleanup is required). Ensure
terminology matches original intent (IPv6 storage migration tests, list of
TP-032..TP-038 cases) and fix any spelling/grammar issues while keeping comment
content semantically identical.
| void test() { | ||
| testTP032_nfsCidrIpv6NotRejected() // TP-032 | ||
| testTP033_isIpInCidrIpv6Match() // TP-033 | ||
| testTP034_isIpInCidrNoMatchFallback() // TP-034 | ||
| testTP035_buildAddrIpv6BracketFormat() // TP-035 | ||
| testTP036_cephMonAddrIpv6Format() // TP-036 | ||
| testTP038_checkMigrateNetworkCidrFallback() // TP-038 | ||
| } |
There was a problem hiding this comment.
TP 编号链路有缺口:缺少 TP-037
test() 里执行了 TP-032/033/034/035/036/038,但没有 TP-037;同时文件头写的是 TP-032~038。建议补充 TP-037 对应用例,或把说明改为实际覆盖范围以保持追溯矩阵一致。
✏️ 若 TP-037 确认不在本用例内,可先修正文案
- * TP-032~038: 存储迁移网络 IPv6 支持测试
+ * TP-032~036, TP-038: 存储迁移网络 IPv6 支持测试🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@test/src/test/groovy/org/zstack/test/integration/network/ipv6/MnIpv6StorageMigrationCase.groovy`
around lines 38 - 45, The test() method calls TP-032/033/034/035/036/038 but
omits TP-037 while the file header claims TP-032~038; either add the missing
TP-037 test invocation (implement or call a method named
testTP037_<description>() and invoke it from test()) to ensure the full
TP-032~038 range is covered, or update the file header/range comment to
accurately reflect the actual tests executed (e.g., TP-032~036,038) and remove
any reference to TP-037; locate the test() method and the surrounding TP comment
to apply the change.
| /** | ||
| * TP-039~041: VXLAN IPv6 vtepIp 支持测试 | ||
| * | ||
| * 全部为纯单元 / 反射测试,无需 Spring 上下文。 | ||
| * | ||
| * 覆盖: | ||
| * TP-039 - VxlanPoolApiInterceptor 接受 IPv6 vtepIp(isIpv6Address 返回 true) | ||
| * TP-040 - VtepVO.vtepIp 和 RemoteVtepVO.vtepIp 列长度 >= 39(支持 IPv6) | ||
| * TP-041 - 非法格式 "not-an-ip" 被校验逻辑拒绝 | ||
| */ | ||
| class VxlanIpv6Case extends SubCase { | ||
|
|
||
| @Override | ||
| void setup() { | ||
| // 纯单元测试,无需 Spring | ||
| } | ||
|
|
||
| @Override | ||
| void environment() { | ||
| // 无环境依赖 | ||
| } | ||
|
|
||
| @Override | ||
| void clean() { | ||
| // 无需清理 | ||
| } | ||
|
|
||
| @Override | ||
| void test() { | ||
| testTP039_vxlanAcceptsIpv6VtepIp() // TP-039 | ||
| testTP040_vtepVoColumnLength() // TP-040 | ||
| testTP041_invalidVtepIpRejected() // TP-041 | ||
| } | ||
|
|
||
| /** | ||
| * TP-039: VxlanPoolApiInterceptor 校验逻辑接受 IPv6 vtepIp。 | ||
| * 拦截器内部使用 NetworkUtils.isIpv4Address || IPv6NetworkUtils.isIpv6Address 判断合法性。 | ||
| * 直接验证 isIpv6Address("2001:db8::1") 返回 true。 | ||
| */ | ||
| void testTP039_vxlanAcceptsIpv6VtepIp() { | ||
| // TP-039: IPv6 地址被 isIpv6Address 认可,拦截器不拒绝 | ||
| String ipv6VtepIp = "2001:db8::1" | ||
| assert IPv6NetworkUtils.isIpv6Address(ipv6VtepIp) : | ||
| "TP-039: isIpv6Address should return true for valid IPv6 vtepIp '$ipv6VtepIp'" | ||
| // 合法 IPv4 同样被接受 | ||
| assert NetworkUtils.isIpv4Address("192.168.1.100") : | ||
| "TP-039: isIpv4Address should return true for valid IPv4 vtepIp" | ||
| // 拦截器的复合校验:IPv4 或 IPv6 均合法 | ||
| boolean ipv6Valid = NetworkUtils.isIpv4Address(ipv6VtepIp) || IPv6NetworkUtils.isIpv6Address(ipv6VtepIp) | ||
| assert ipv6Valid : "TP-039: VxlanPoolApiInterceptor composite check should accept IPv6 vtepIp" | ||
| logger.info("TP-039: IPv6 vtepIp '$ipv6VtepIp' accepted by VxlanPoolApiInterceptor validation logic") | ||
| } | ||
|
|
||
| /** | ||
| * TP-040: VtepVO.vtepIp 和 RemoteVtepVO.vtepIp @Column 无显式 length, | ||
| * 使用 JPA 默认 255(>= 39),足以存储全展开 IPv6。 | ||
| */ | ||
| void testTP040_vtepVoColumnLength() { | ||
| checkVtepIpColumnLength(VtepVO.class, "VtepVO") | ||
| checkVtepIpColumnLength(RemoteVtepVO.class, "RemoteVtepVO") | ||
| } | ||
|
|
||
| private void checkVtepIpColumnLength(Class<?> voClass, String className) { | ||
| Field field = voClass.getDeclaredField("vtepIp") | ||
| field.setAccessible(true) | ||
| Column col = field.getAnnotation(Column.class) | ||
| assert col != null : "TP-040: $className.vtepIp should have @Column annotation" | ||
|
|
||
| int length = col.length() | ||
| // JPA @Column 默认 length 为 255;若未显式设置则为 255 | ||
| // 全展开 IPv6 最长 39 字符,255 >= 39 即可 | ||
| assert length >= 39 : | ||
| "TP-040: $className.vtepIp @Column length $length must be >= 39 to store full IPv6 address" | ||
| logger.info("TP-040: $className.vtepIp @Column length=$length (>= 39, IPv6-safe)") | ||
| } | ||
|
|
||
| /** | ||
| * TP-041: 非法格式 "not-an-ip" 既不是 IPv4 也不是 IPv6, | ||
| * VxlanPoolApiInterceptor 校验逻辑(IPv4 || IPv6)应返回 false。 | ||
| */ | ||
| void testTP041_invalidVtepIpRejected() { | ||
| String invalidIp = "not-an-ip" | ||
| // TP-041: 非法 IP 不通过 IPv4 检查 | ||
| assert !NetworkUtils.isIpv4Address(invalidIp) : | ||
| "TP-041: 'not-an-ip' should not be a valid IPv4 address" | ||
| // 非法 IP 不通过 IPv6 检查 | ||
| assert !IPv6NetworkUtils.isIpv6Address(invalidIp) : | ||
| "TP-041: 'not-an-ip' should not be a valid IPv6 address" | ||
| // 拦截器复合校验:两者均 false → 应被拒绝 | ||
| boolean valid = NetworkUtils.isIpv4Address(invalidIp) || IPv6NetworkUtils.isIpv6Address(invalidIp) | ||
| assert !valid : "TP-041: VxlanPoolApiInterceptor should reject invalid vtepIp 'not-an-ip'" | ||
| logger.info("TP-041: invalid vtepIp 'not-an-ip' correctly rejected") | ||
| } |
There was a problem hiding this comment.
请将该测试文件中的中文文本改为英文。
当前注释、断言信息和日志内容包含中文,不符合仓库统一编码规范;建议一次性改成英文以保持测试代码风格一致。
As per coding guidelines 代码里不应当有有中文,包括报错、注释等都应当使用正确的、无拼写错误的英文来写.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@test/src/test/groovy/org/zstack/test/integration/network/vxlan/VxlanIpv6Case.groovy`
around lines 12 - 104, Update VxlanIpv6Case to replace all Chinese text with
English: change class/method comments, Javadoc blocks, inline comments,
assertion messages, and logger.info messages in methods
testTP039_vxlanAcceptsIpv6VtepIp, testTP040_vtepVoColumnLength,
checkVtepIpColumnLength, and testTP041_invalidVtepIpRejected to clear, idiomatic
English (no Chinese characters or Pinyin); keep the logic and identifiers
(VxlanIpv6Case, testTP039_vxlanAcceptsIpv6VtepIp, testTP040_vtepVoColumnLength,
checkVtepIpColumnLength, testTP041_invalidVtepIpRejected, VtepVO, RemoteVtepVO,
vtepIp) unchanged while updating only the human-readable strings.
ZSTAC-79206 M1/M2/M3 Java 实现
Resolves: ZSTAC-79206
sync from gitlab !9731