Skip to content

[network]: ZSTAC-79206 管理网络 IPv6 支持 M1/M2/M3 (Java)#3858

Open
zstack-robot-1 wants to merge 34 commits intofeature-mgt-ip6from
sync/shixin.ruan/shixin.ruan-ZSTAC-79206@@5
Open

[network]: ZSTAC-79206 管理网络 IPv6 支持 M1/M2/M3 (Java)#3858
zstack-robot-1 wants to merge 34 commits intofeature-mgt-ip6from
sync/shixin.ruan/shixin.ruan-ZSTAC-79206@@5

Conversation

@zstack-robot-1
Copy link
Copy Markdown
Collaborator

ZSTAC-79206 M1/M2/M3 Java 实现

Resolves: ZSTAC-79206

sync from gitlab !9731

ruansteve and others added 30 commits March 25, 2026 11:25
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
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 25, 2026

Walkthrough

此次变更引入 IPv6 支持、VM NIC 生命周期管理框架和 SDN NIC 分配/释放扩展点,重构 NIC 分配流程并添加多处插件回调与测试覆盖,涉及 compute、network、core、plugin(kvm、sdn、ceph 等)、utils、header、docs 与大量测试代码与 DB 迁移脚本。

Changes

Cohort / File(s) Summary
基础网络与工具
utils/src/main/java/org/zstack/utils/network/IPv6Utils.java, utils/src/main/java/org/zstack/utils/network/NetworkUtils.java, core/src/main/java/org/zstack/core/config/NetworkGlobalConfig.java, core/src/main/java/org/zstack/core/Platform.java
新增 IPv6 工具(URL/addr 构建、归一化、管理 IP 验证)、通用 IP-in-CIDR 检查、管理节点发现改为 IPv6 感知并新增 PREFER_IPV6 全局配置。
VM NIC 生命周期管理器与全局配置
compute/src/main/java/org/zstack/compute/vm/VmNicLifecycleManager.java, compute/src/main/java/org/zstack/compute/vm/VmNicLifecycleGlobalConfig.java
新增 VmNicLifecycleManager,按扩展点调度主机级 NIC setup/cleanup/migrate/reconcile;新增 reconcile 超时全局配置。
KVM 主机桥接与定时桥接器
plugin/kvm/src/main/java/org/zstack/kvm/VmNicLifecycleKvmBridge.java, plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java, plugin/kvm/src/main/java/org/zstack/kvm/KVMAgentCommands.java
新增 KVM Ping 桥接以触发 NIC reconcile;增强主机 IPv6 感知、VNC 绑定地址、NicTO 支持 interfaceId/bridgePortType。
SDN NIC 分配/回滚/释放流程与扩展点
compute/src/main/java/org/zstack/compute/vm/VmAllocateSdnNicFlow.java, compute/src/main/java/org/zstack/compute/vm/VmReturnReleaseNicFlow.java, compute/src/main/java/org/zstack/compute/vm/VmDetachNicFlow.java, header/src/main/java/org/zstack/header/vm/AfterAllocateSdnNicExtensionPoint.java
新增 VmAllocateSdnNicFlow 并在卸载/释放流程中接入 AfterAllocateSdnNicExtensionPoint,调用插件完成 SDN 端口创建、回滚与释放,错误处理为 best-effort/汇总。
NIC 分配重构与扩展点调用
compute/src/main/java/org/zstack/compute/vm/VmAllocateNicFlow.java, header/src/main/java/org/zstack/header/vm/BeforeAllocateVmNicExtensionPoint.java, header/src/main/java/org/zstack/header/vm/AfterAllocateVmNicIpExtensionPoint.java
将 NIC 构建、MAC 计算、静态 IP 持久化与扩展点逻辑拆分为 helper,先持久化 VmNicVO 再调用 BeforeAllocate 扩展点,调整重复 MAC 错误收集与 rollback 行为。
VM 扩展点新增/发射与系统标签
compute/src/main/java/org/zstack/compute/vm/VmSystemTags.java, compute/src/main/java/org/zstack/compute/vm/VmInstanceExtensionPointEmitter.java, header/src/main/java/org/zstack/header/vm/AfterUpdateVmNicMacExtensionPoint.java, header/src/main/java/org/zstack/header/vm/AfterReleaseVmNicExtensionPoint.java, header/src/main/java/org/zstack/header/vm/BeforeAllocateVmNicExtensionPoint.java
新增 IFACE_ID 系统标签并指向 VmNicVO;添加 AfterUpdateVmNicMac 扩展点及其发射方法;新增若干 VM NIC 生命周期扩展点接口以覆盖 MAC/IP/释放 前后时序。
VM 实例/级联与流程链路调整
compute/src/main/java/org/zstack/compute/vm/VmInstanceBase.java, compute/src/main/java/org/zstack/compute/vm/VmCascadeExtension.java, compute/src/main/java/org/zstack/compute/host/HostApiInterceptor.java
在 NIC 附加流程中插入 SDN 分配步骤;修复 IP 范围删除时的 null 检查;Host API 接口改为接受 IPv6/主机名并归一化管理 IP。
L2/L3 与 VXLAN 验证增强
network/src/main/java/org/zstack/network/l2/L2NetworkApiInterceptor.java, network/src/main/java/org/zstack/network/l2/L2NoVlanNetwork.java, plugin/vxlan/.../VxlanPoolApiInterceptor.java, header/src/main/java/org/zstack/header/network/l2/L2NetworkConstant.java
新增 GENEVE 类型支持与验证,ZNS vSwitchType 特例跳过物理接口冲突校验;扩展常量(L2Geneve、VSWITCH_TYPE_ZNS、加速类型)。
L3 MTU 与 IPRange 扩展点
network/src/main/java/org/zstack/network/l3/L3NetworkManagerImpl.java, network/src/main/java/org/zstack/network/l3/L3BasicNetwork.java, header/src/main/java/org/zstack/header/network/l3/AfterSetL3NetworkMtuExtensionPoint.java, header/src/main/java/org/zstack/header/network/l3/AfterUpdateIpRangeExtensionPoint.java
添加 AfterSetL3NetworkMtu 与 AfterUpdateIpRange 扩展点,L3 管理逻辑在更新后触发插件回调并聚合错误。
SDN 控制器管理调整
plugin/sdnController/src/main/java/org/zstack/sdnController/SdnControllerManagerImpl.java, plugin/sdnController/.../SdnControllerFactory.java, plugin/sdnController/.../SdnControllerBase.java
移除 persistSdnController 接口方法,委托控制器实现处理 SdnController VO 持久化/删除;采用 AfterAllocateSdnNicExtensionPoint 替代旧生命周期钩子。
存储与 ApplianceVM IPv6 适配
plugin/ceph/.../Ceph*MonBase.java, plugin/applianceVm/src/main/java/org/zstack/appliancevm/ApplianceVmFacadeImpl.java, plugin/nfsPrimaryStorage/.../NfsApiParamChecker.java
使用 IPv6Utils 构建 Ceph mon 地址;VR 管理 IP 选择加入 CIDR 匹配;NFS 网关 CIDR 验证支持 IPv6。
REST/Webhook 与回调管理
core/src/main/java/org/zstack/core/rest/RESTFacadeImpl.java, core/src/main/java/org/zstack/core/rest/webhook/WebhookCallbackClient.java, core/src/main/java/org/zstack/core/rest/webhook/WebhookProtocol.java
REST callback URL 构建/纠正 IPv6 brackets;新增 WebhookCallbackClient 与 WebhookProtocol 泛型协议支持异步回调管理与超时。
DB 迁移与错误码
conf/db/upgrade/V5.5.18__schema.sql, utils/src/main/java/org/zstack/utils/clouderrorcode/CloudOperationsErrorCode.java
新增 ZNS 相关三表迁移;添加 ZNS 与若干 L2/VM 相关错误码常量。
大量测试用例与工具
test/..., testlib/...(多个新增文件,见 PR 列表)
新增/扩展大量 IPv6、VmNicLifecycle、SDN 相关单元与集成测试,以及测试助手(ApiHelper、Mock/Stub 扩展)。
其他小变更
.gitconfig/hooks/commit-msg, core/src/main/java/org/zstack/core/Platform.java(jgroups 地址格式等)、rest/src/main/resources/scripts/RestDocumentationGenerator.groovy
提交钩子扩展 JIRA regex,Platform 中多处 IPv6-aware 字符串构建与初始 hosts 格式化,文档生成器改为聚合错误报告等若干细节调整。

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 配置 并 继续后续步骤
Loading
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 流程)
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 分钟

🐰✨ 我在代码林中跳跃,袜子叮当响,
IPv6 铺路连成桥,NIC 节点轻轻唱,
SDN 扩展点忙碌,生命周期有了向导,
插件齐心协作,测试花开满仓廊,
兔儿鼓掌欢呼:网络更宽,梦想更远。

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch sync/shixin.ruan/shixin.ruan-ZSTAC-79206@@5

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 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 重复导入了 L3NetworkInventoryL3NetworkVOSdnControllerL3,这些已在 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 重新拼出来的;一旦真正不一致的就是 categoryname,报错里给出的路径就不是当前正在校验的文件,汇总输出会把人带到错误位置。建议把调用方的 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 责任),降低实现歧义。

✍️ 建议修改
 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);
 }
As per coding guidelines: `接口方法不应有多余的修饰符(例如 public),且必须配有有效的 Javadoc 注释。`
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@header/src/main/java/org/zstack/header/vm/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 回调约定,避免扩展实现方误用。

✍️ 建议修改
 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);
 }
As per coding guidelines: `接口方法不应有多余的修饰符(例如 public),且必须配有有效的 Javadoc 注释。`
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@header/src/main/java/org/zstack/header/vm/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 | 🟡 Minor

TP-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 | 🟡 Minor

Javadoc 注释应使用英文。

根据编码规范要求,代码注释应当使用英文而非中文。

建议的修复
-    /**
-     * 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 VIP

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/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 address

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/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: 三个方法存在大量重复代码,建议提取公共逻辑。

afterAllocateSdnNicrollbackSdnNicreleaseSdnNics 方法中构建 nicMaps 的逻辑几乎完全相同(遍历 NICs、查询 L3/L2、检查 SDN 跳过条件、获取 controller UUID)。主要差异仅在于:

  • afterAllocateSdnNiccontrollerUuid == null 时调用 completion.fail()
  • 其他两个方法在 controllerUuid == nullcontinue

建议提取公共的 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。虽然此模式在多个文件中使用(如 RESTFacadeImplAsyncTimerCloudBusImpl2 等),但这违反了抽象原则——ThreadFacade 接口已经在其方法签名中暴露了 TimeoutTaskReceipt,却被定义在实现类 ThreadFacadeImpl 内部。

建议将 TimeoutTaskReceiptThreadFacadeImpl 中提取出来,作为 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.

Comment on lines 124 to +131
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));
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

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.

Comment on lines +426 to +452
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);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

释放路径不应该只依赖 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.

Comment on lines +553 to +597
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();
}
});
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

迁移后的清理丢失了真实的源宿主机。

这里把 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.

Comment on lines +1018 to +1020
for (AfterUpdateIpRangeExtensionPoint ext : pluginRgty.getExtensionList(AfterUpdateIpRangeExtensionPoint.class)) {
ext.afterUpdateIpRange(oldIpr, newIpr);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

隔离扩展点异常,避免“已更新成功但 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.

Comment on lines +187 to +221
}).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));
}
}
});
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

这里新增了一个不可补偿的失败点。

这段逻辑是在前一个 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.

Comment on lines +198 to +218
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));
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

不要在首个扩展失败时提前结束整个遍历。

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).

Comment on lines +53 to +59
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());
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

避免在 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.

Comment on lines +851 to +861
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")
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

缺失整个章节时,这里的校验会漏报。

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.

Suggested change
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.

Comment on lines +119 to +127
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;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 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:


🏁 Script executed:

#!/bin/bash
# Search for isIpv4Address and isIpv6Address method definitions
rg "def isIpv4Address|def isIpv6Address|boolean isIpv4Address|boolean isIpv6Address" -t java

Repository: 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 -20

Repository: 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.java

Repository: 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.java

Repository: 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 -n

Repository: 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 -20

Repository: 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.

Comment on lines +564 to +568
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;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

混合地址族时这里还会抛异常,不会按预期返回 false

ip 是 IPv4、cidr 是 IPv6 时,这里会进入 isIpv4InCidr();该路径只适用于 IPv4 CIDR。结果是新 helper 在 IPv4/IPv6 不匹配场景下仍可能直接异常,而不是像本次 PR 目标那样返回 falseNfsApiParamChecker 这类新调用方会被这个根因影响。

🛠️ 建议修复
 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
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 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

📥 Commits

Reviewing files that changed from the base of the PR and between 9aee467 and bcfe36f.

📒 Files selected for processing (9)
  • test/src/test/groovy/org/zstack/test/integration/appliancevm/ApplianceVmIpv6Case.groovy
  • test/src/test/groovy/org/zstack/test/integration/core/MnIpv6Case.groovy
  • test/src/test/groovy/org/zstack/test/integration/core/MnIpv6M3Case.groovy
  • test/src/test/groovy/org/zstack/test/integration/core/MnIpv6M4Case.groovy
  • test/src/test/groovy/org/zstack/test/integration/core/ZSha2Ipv6Case.groovy
  • test/src/test/groovy/org/zstack/test/integration/kvm/host/KvmHostIpv6Case.groovy
  • test/src/test/groovy/org/zstack/test/integration/network/ipv6/MnIpv6StorageMigrationCase.groovy
  • test/src/test/groovy/org/zstack/test/integration/network/vxlan/VxlanIpv6Case.groovy
  • test/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

Comment on lines +11 to +22
* 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,不抛异常
*/
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

请将中文注释与文档字符串统一改为英文

当前类中多处注释/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.

Comment on lines +112 to +117
// 使用一个极不可能匹配当前主机任何网卡的 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'"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

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.

Suggested change
// 使用一个极不可能匹配当前主机任何网卡的 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.

Comment on lines +14 to +262
/**
* 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'")
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

请将该文件中的中文注释/断言/日志全部替换为英文。

目前中文文本覆盖范围较大,直接违反仓库统一规范,建议在当前提交中统一修正。

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.

Comment on lines +121 to +133
* 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")
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

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.

Comment on lines +8 to +23
/**
* 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 括号
*/
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

请将中文注释统一改为英文(当前违反仓库编码规范)

本文件在类注释、方法注释和行内注释中包含大量中文(例如 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.

Comment on lines +37 to +38
testTP043_isMasterGrepPatternMatchesIpv6() // TP-042
testTP042_isMasterGrepLogicWithVip() // TP-043
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

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.

Comment on lines +14 to +171
/**
* 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")
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

请将测试中的中文注释、断言文案与日志文案统一改为英文。

该文件包含大量中文文本(注释、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.

Comment on lines +7 to +35
/**
* 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() {
// 无需清理
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

请将该文件中的中文注释统一改为英文

当前类注释、方法注释和多处行内注释使用了中文,违反仓库统一规范。请改为语义准确、拼写正确的英文表述。

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.

Comment on lines +38 to +45
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
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

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.

Comment on lines +12 to +104
/**
* 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")
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

请将该测试文件中的中文文本改为英文。

当前注释、断言信息和日志内容包含中文,不符合仓库统一编码规范;建议一次性改成英文以保持测试代码风格一致。

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.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants