Skip to content

<fix>[core]: register IPv6 prefer config#4146

Open
MatheMatrix wants to merge 12 commits into
feature-5.5.28-IPv6-management-networkfrom
sync/shixin.ruan/shixin-ZSTAC-79206@@2
Open

<fix>[core]: register IPv6 prefer config#4146
MatheMatrix wants to merge 12 commits into
feature-5.5.28-IPv6-management-networkfrom
sync/shixin.ruan/shixin-ZSTAC-79206@@2

Conversation

@MatheMatrix
Copy link
Copy Markdown
Owner

Register management.server/prefer.ipv6 as a GlobalConfig so
QueryGlobalConfig and runtime updates work. Keep the startup system
property override and fall back to the legacy global property before
GlobalConfig is available.

Resolves: ZSTAC-85520

Change-Id: Ia59515ce9a9a29eeecb9b5ab7d83057823089884

sync from gitlab !10050

shixin.ruan added 12 commits May 27, 2026 12:43
Register management.server/prefer.ipv6 as a GlobalConfig so
QueryGlobalConfig and runtime updates work. Keep the startup system
property override and fall back to the legacy global property before
GlobalConfig is available.

Resolves: ZSTAC-85520

Change-Id: Ia59515ce9a9a29eeecb9b5ab7d83057823089884
Use bare IPv6 for ssh command targets. Keep bracketed IPv6 only for scp
host:path syntax.

Resolves: ZSTAC-85522

Change-Id: I0d655ccf654634edb109f9c8025a7b70dbf34da4
Use a callback URL in the same IP family as the virtual router
management NIC. This keeps IPv4-only vrouter agents reachable when MN
is dual-stack and its default management IP is IPv6.

Resolves: ZSTAC-85527

Change-Id: Iaa630fc59d30c2675db7bbe47cb1b7c8d58bb023
Console proxy returned the MN IPv6 address to clients but selected the
listen address from agentIp, which is 127.0.0.1 for the management-node
agent. Use the client-facing proxy hostname to choose the wildcard
listen address so IPv6 console URLs are reachable.

Resolves: ZSTAC-85595

Change-Id: Ief18c9b847f0e0c050ce993a50244533614fd2b8
Fixes ZSTAC-85605 and ZSTAC-85612.

SSH keeps raw IPv6 hosts while SCP brackets IPv6 paths.
Ansible callback checker now passes -6 for IPv6 nc and nmap.
Hotfix verified on 172.24.249.182.

Resolves: ZSTAC-79206

Change-Id: Iaa7204e638335c7bf1496b2cd5e0314081e598cb
VXLAN CIDR system tags can contain IPv6 values with double colon. Split
tag fields only outside token braces so patterned tag matching and
token extraction keep IPv6 CIDRs intact.

Resolves: ZSTAC-85618

Change-Id: Ie74a3ac89e1d728953bcaab74146d25e7a7e2edc
Remove the customer-facing management.server.prefer.ipv6 switch.\n\nUse
explicit management.server.ip first. When it is not configured, keep
IPv4 as the default and fall back to IPv6 only if no IPv4 is
available.\n\nWaterfall CR: ZSTAC-79206 CR-001

Change-Id: I14e3e6b3fdad2e4e109d4f9c9f3f344356866762
Build KVM agent HTTP URLs through the IPv6-safe host formatter so live
migration cleanup and migrate calls bracket IPv6 host addresses.

Resolves: ZSTAC-85636

Change-Id: I0c9fa1eecf7d6eef778cbb4568123d7bd3a836ec
Allow patterned system tag values to contain IPv6 token text.
Use dual-stack CIDR matching for migration network selection.
Add focused management IPv6 regression assertions.

Resolves: ZSTAC-85638

Change-Id: I32f3d1111aa3358fd26da6a3afb23111154f9058
Resolves: ZSTAC-79206

Change-Id: I485bade7bd875e2b492174f4c2e7e710fee1c0b3
Fixes ZSTAC-85690.\n\nAllow IPv6 ranges on System L3 networks for
management network IPv6.\nAlso allow virtual router offerings to use
IPv6 management L3.

Change-Id: If46c0ab86c018f72d0138df6363689f85dbecaeb
Parse system tag delimiters only outside token braces when matching
actual tag values.

Resolves: ZSTAC-85618

Change-Id: I7d0a9cf3146a513e56ba0d6d3bff3685bc4716e0
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 30, 2026

Review Change Stack

整体概述

此 PR 对 ZStack 的 IPv6 网络配置体系进行了深层重构,核心变化包括:

  • 将管理服务器 IP 选择从可配置 IPv6 偏好改为固定优先 IPv4 策略
  • 建立代理回调 URL 构造体系,虚拟路由器和 VyOS 通过请求头动态传递回调地址
  • 增强网络工具库对 IPv4/IPv6 双栈的支持(CIDR 筛选、主机名格式化)
  • 统一 agent URL 拼装方式,改进 SSH/SCP 目标格式化
  • 增强 Tag 解析对 token 内部分隔符的感知

Changes

IPv6 管理网络和回调 URL 体系

Layer / File(s) Summary
管理服务器 IP 选择策略和全局配置
core/src/main/java/org/zstack/core/Platform.java, core/src/main/java/org/zstack/core/CoreGlobalProperty.java
Platform.selectManagementServerIp 移除 preferIpv6 参数,固定优先返回 IPv4;删除 MANAGEMENT_SERVER_PREFER_IPV6 配置,新增 CHRONY_SERVERS 和 MN_VIP。
RESTFacade 回调 URL 构造接口和实现
header/src/main/java/org/zstack/header/rest/RESTFacade.java, core/src/main/java/org/zstack/core/rest/RESTFacadeImpl.java
RESTFacade 新增 buildCallbackUrl(hostName) 接口,RESTFacadeImpl 基于实例 port 和 path 为指定主机构造回调 URL。
虚拟路由器代理回调请求头体系
plugin/virtualRouterProvider/src/main/java/org/zstack/network/service/virtualrouter/VirtualRouterManager.java, plugin/virtualRouterProvider/src/main/java/org/zstack/network/service/virtualrouter/VirtualRouterManagerImpl.java
VirtualRouterManager 声明 buildAgentCallbackUrlHeaders();实现类根据管理网卡 IP 选择对应的管理服务器 IP,返回包含回调 URL 的请求头。
虚拟路由器异步 HTTP 调用注入回调头
plugin/virtualRouterProvider/src/main/java/org/zstack/network/service/virtualrouter/VirtualRouter.java, plugin/virtualRouterProvider/src/main/java/org/zstack/network/service/virtualrouter/lifecycle/VirtualRouterDeployAgentFlow.java, plugin/virtualRouterProvider/src/main/java/org/zstack/network/service/virtualrouter/vyos/VyosConnectFlow.java
VirtualRouter 的 ping 和异步 HTTP 调用、部署和连接流程均新增回调请求头参数。

网络工具库 IPv6 增强

Layer / File(s) Summary
IPv6 URL 处理工具方法
utils/src/main/java/org/zstack/utils/network/IPv6NetworkUtils.java
新增 stripHostUrlBrackets() 方法去除 IPv6 URL 方括号(如 [::1] → ::1)。
通用 CIDR 过滤和格式化
utils/src/main/java/org/zstack/utils/network/NetworkUtils.java
新增 filterIpsInCidr() 替代 IPv4 专用的 filterIpv4sInCidr();改进 fmtCidr() 使用通用网络地址解析。
Ceph 存储迁移 CIDR 过滤更新
plugin/ceph/src/main/java/org/zstack/storage/ceph/primary/CephPrimaryStorageBase.java
MON 迁移 IP 过滤改为调用通用的 filterIpsInCidr()。

SSH/SCP 和 Agent URL 构造

Layer / File(s) Summary
SSH 和 SCP 目标格式化
utils/src/main/java/org/zstack/utils/ssh/SshShell.java, utils/src/main/java/org/zstack/utils/ssh/Ssh.java
formatSshTarget() 改为用 stripHostUrlBrackets() 处理 IPv6;SSH 脚本构造改为调用该方法;SCP 命令改为使用 formatScpTarget()。
回调检查脚本 IPv6 支持
core/src/main/java/org/zstack/core/ansible/CallBackNetworkChecker.java
新增 buildCallbackCheckScript(),根据回调 IP 版本选择 ncat/nmap 选项(IPv6 时传入 -6)。
KVM Host Agent URL 统一构造
plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java
新增公共 buildAgentUrl(host, path) 方法,VM 迁移流程中多处 UriComponentsBuilder 手工拼装改为调用该方法。
ZBS 存储 URL 处理
plugin/zbs/src/main/java/org/zstack/storage/zbs/MdsUri.java, plugin/zbs/src/main/java/org/zstack/storage/zbs/ZbsAgentUrl.java
MdsUri 使用 stripHostUrlBrackets() 处理 IPv6 方括号;ZbsAgentUrl 改为用 IPv6NetworkUtils.buildHttpUrl() 处理 IPv6。

控制台代理和网络验证

Layer / File(s) Summary
控制台代理监听主机名选择
console/src/main/java/org/zstack/console/ConsoleProxyBase.java
selectProxyListenHostname() 参数从 agentIp 改为 proxyHostname,基于代理 IP 版本选择通配监听地址。
L3 网络 IPv6 范围验证放松
network/src/main/java/org/zstack/network/l3/L3NetworkApiInterceptor.java
删除了对 System 类网络添加 IPv6 range 的限制。
虚拟路由器管理网络验证放松
plugin/virtualRouterProvider/src/main/java/org/zstack/network/service/virtualrouter/VirtualRouterApiInterceptor.java
删除了对管理网络必须支持 IPv4 的限制。

Tag 解析增强

Layer / File(s) Summary
Token-aware 系统标签解析
utils/src/main/java/org/zstack/utils/TagUtils.java
parse() 和 isMatch() 改为使用 token-aware 拆分,新增私有方法避免在花括号 token 内部误拆分。

测试用例更新和扩展

Layer / File(s) Summary
控制台代理 IPv6 测试
test/src/test/groovy/org/zstack/test/integration/console/ConsoleProxyCase.groovy
测试扩展,验证 IPv6 代理 IP 的更新、全局属性同步、监听主机名格式。
核心 IPv6 管理网络测试
test/src/test/groovy/org/zstack/test/integration/core/ManagementNetworkIpv6Case.groovy
测试大幅更新,移除 prefer ipv6 测试,新增控制台代理、KVM agent URL、SSH/SCP、回调脚本、Tag 匹配解析等多个 IPv6 场景。
ZBS 存储 IPv6 URL 测试
test/src/test/groovy/org/zstack/test/integration/storage/primary/addon/zbs/ZbsPrimaryStorageCase.groovy
新增 testZbsMdsUriAndAgentUrlSupportIpv6() 测试,验证 IPv6 主机名解析和 agent URL 生成。

Sequence Diagram(s)

sequenceDiagram
    participant VR as VirtualRouter
    participant VRM as VirtualRouterManagerImpl
    participant Platform
    participant REST as RESTFacade
    
    VR->>VRM: doPing()<br/>buildAgentCallbackUrlHeaders(mgmtIp)
    VRM->>Platform: getManagementServerIps()
    VRM->>Platform: selectManagementIpForAgent(mgmtIp)
    Platform-->>VRM: 匹配类型 IP
    VRM->>REST: buildCallbackUrl(selectedIp)
    REST-->>VRM: callback URL
    VRM-->>VR: {CALLBACK_URL: url}
    VR->>VR: asyncJsonPost(url, cmd, headers)
Loading

🎯 4 (Complex) | ⏱️ ~60 minutes

🐰 IPv6 的双栈舞蹈,从偏好到固定,
回调请求头穿梭虚拟路由间,
网络工具库全面升级,
Tag 解析智慧了,Token 不再迷茫,
从 Agent URL 到 SSH/SCP 目标,
一场网络地址的优雅重生! 🌐

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 1.27% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed 标题完全符合指定的格式要求,包含有效的前缀 和范围 [core],描述简洁准确,共40个字符,远低于72个字符限制。
Description check ✅ Passed 描述清晰准确地说明了PR的目的:注册IPv6偏好配置为GlobalConfig以支持查询和运行时更新,保留启动系统属性覆盖和遗留全局属性回退。内容直接相关于PR所做的核心更改。
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch sync/shixin.ruan/shixin-ZSTAC-79206@@2

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ast-grep (0.42.3)
plugin/ceph/src/main/java/org/zstack/storage/ceph/primary/CephPrimaryStorageBase.java
plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
plugin/zbs/src/main/java/org/zstack/storage/zbs/ZbsAgentUrl.java (1)

10-19: ⚡ Quick win

建议在路径拼接时统一处理前后斜杠,避免出现 //

当前实现在 sb/ 结尾且 path 也以 / 开头时会生成双斜杠,建议在 appendPath 中做一次归一化。

♻️ 建议修改
 private static void appendPath(StringBuilder sb, String path) {
     if (path == null || path.isEmpty()) {
         return;
     }

-    if (!path.startsWith("/")) {
-        sb.append("/");
-    }
-    sb.append(path);
+    boolean sbEndsWithSlash = sb.length() > 0 && sb.charAt(sb.length() - 1) == '/';
+    boolean pathStartsWithSlash = path.startsWith("/");
+
+    if (sbEndsWithSlash && pathStartsWithSlash) {
+        sb.append(path.substring(1));
+    } else if (!sbEndsWithSlash && !pathStartsWithSlash) {
+        sb.append('/').append(path);
+    } else {
+        sb.append(path);
+    }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugin/zbs/src/main/java/org/zstack/storage/zbs/ZbsAgentUrl.java` around
lines 10 - 19, appendPath currently can produce double slashes when sb ends with
'/' and path starts with '/'; update the appendPath(StringBuilder sb, String
path) to normalize boundaries by removing a trailing slash from sb or a leading
slash from path before appending so exactly one '/' separates segments (handle
null/empty path as before and ensure you still append a single '/' when needed).
Use the existing sb and path parameters and make this normalization inside
appendPath so callers need no changes.
header/src/main/java/org/zstack/header/rest/RESTFacade.java (1)

91-91: ⚡ Quick win

为新增接口方法补充 Javadoc。

RESTFacade 是公共接口,Line 91 新增方法缺少方法级文档,后续调用约束不够清晰。建议补充参数与返回值语义说明。

建议修改
+    /**
+     * Build callback URL with the given host name using current RESTFacade port/path.
+     *
+     * `@param` hostName callback host name or IP
+     * `@return` callback URL
+     */
     String buildCallbackUrl(String hostName);

As per coding guidelines "接口方法不应有多余的修饰符(例如 public),且必须配有有效的 Javadoc 注释。"

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@header/src/main/java/org/zstack/header/rest/RESTFacade.java` at line 91,
RESTFacade 接口中新加的方法 buildCallbackUrl(String hostName) 缺少方法级 Javadoc;请为方法 String
buildCallbackUrl(String hostName) 添加完整的 Javadoc,明确说明参数 hostName
的含义(例如主机名或主机地址、是否允许为 null/空、预期格式)以及返回值语义(例如返回用于回调的完整 URL、何时可能返回 null
或抛出异常),并确保接口方法不带多余的修饰符以符合编码规范。
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@utils/src/main/java/org/zstack/utils/TagUtils.java`:
- Around line 49-50: The fast-return in isMatch that does "if
(fmt.indexOf(TAG_DELIMITER) == -1) return fmt.equals(tag);" wrongly treats
templates like "{token}" as non-matching; instead, change this branch to
consider template tokens: when TAG_DELIMITER is absent in fmt, return true if
fmt.equals(tag) OR fmt itself is a single token template that should match any
valid tag value (e.g., fmt.matches("\\{[^}]+}")) and tag is non-empty (or
matches the expected token pattern). Update isMatch to use these checks
(referencing isMatch, fmt, tag, and TAG_DELIMITER) so token-template strings are
treated as matching rather than always false.

---

Nitpick comments:
In `@header/src/main/java/org/zstack/header/rest/RESTFacade.java`:
- Line 91: RESTFacade 接口中新加的方法 buildCallbackUrl(String hostName) 缺少方法级
Javadoc;请为方法 String buildCallbackUrl(String hostName) 添加完整的 Javadoc,明确说明参数
hostName 的含义(例如主机名或主机地址、是否允许为 null/空、预期格式)以及返回值语义(例如返回用于回调的完整 URL、何时可能返回 null
或抛出异常),并确保接口方法不带多余的修饰符以符合编码规范。

In `@plugin/zbs/src/main/java/org/zstack/storage/zbs/ZbsAgentUrl.java`:
- Around line 10-19: appendPath currently can produce double slashes when sb
ends with '/' and path starts with '/'; update the appendPath(StringBuilder sb,
String path) to normalize boundaries by removing a trailing slash from sb or a
leading slash from path before appending so exactly one '/' separates segments
(handle null/empty path as before and ensure you still append a single '/' when
needed). Use the existing sb and path parameters and make this normalization
inside appendPath so callers need no changes.
🪄 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: cab5d952-05ef-4595-87b2-6204fdd2f192

📥 Commits

Reviewing files that changed from the base of the PR and between bc00c9c and e40f7f8.

📒 Files selected for processing (25)
  • console/src/main/java/org/zstack/console/ConsoleProxyBase.java
  • core/src/main/java/org/zstack/core/CoreGlobalProperty.java
  • core/src/main/java/org/zstack/core/Platform.java
  • core/src/main/java/org/zstack/core/ansible/CallBackNetworkChecker.java
  • core/src/main/java/org/zstack/core/rest/RESTFacadeImpl.java
  • header/src/main/java/org/zstack/header/rest/RESTFacade.java
  • network/src/main/java/org/zstack/network/l3/L3NetworkApiInterceptor.java
  • plugin/ceph/src/main/java/org/zstack/storage/ceph/primary/CephPrimaryStorageBase.java
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java
  • plugin/virtualRouterProvider/src/main/java/org/zstack/network/service/virtualrouter/VirtualRouter.java
  • plugin/virtualRouterProvider/src/main/java/org/zstack/network/service/virtualrouter/VirtualRouterApiInterceptor.java
  • plugin/virtualRouterProvider/src/main/java/org/zstack/network/service/virtualrouter/VirtualRouterManager.java
  • plugin/virtualRouterProvider/src/main/java/org/zstack/network/service/virtualrouter/VirtualRouterManagerImpl.java
  • plugin/virtualRouterProvider/src/main/java/org/zstack/network/service/virtualrouter/lifecycle/VirtualRouterDeployAgentFlow.java
  • plugin/virtualRouterProvider/src/main/java/org/zstack/network/service/virtualrouter/vyos/VyosConnectFlow.java
  • plugin/zbs/src/main/java/org/zstack/storage/zbs/MdsUri.java
  • plugin/zbs/src/main/java/org/zstack/storage/zbs/ZbsAgentUrl.java
  • test/src/test/groovy/org/zstack/test/integration/console/ConsoleProxyCase.groovy
  • test/src/test/groovy/org/zstack/test/integration/core/ManagementNetworkIpv6Case.groovy
  • test/src/test/groovy/org/zstack/test/integration/storage/primary/addon/zbs/ZbsPrimaryStorageCase.groovy
  • utils/src/main/java/org/zstack/utils/TagUtils.java
  • utils/src/main/java/org/zstack/utils/network/IPv6NetworkUtils.java
  • utils/src/main/java/org/zstack/utils/network/NetworkUtils.java
  • utils/src/main/java/org/zstack/utils/ssh/Ssh.java
  • utils/src/main/java/org/zstack/utils/ssh/SshShell.java
💤 Files with no reviewable changes (3)
  • network/src/main/java/org/zstack/network/l3/L3NetworkApiInterceptor.java
  • core/src/main/java/org/zstack/core/CoreGlobalProperty.java
  • plugin/virtualRouterProvider/src/main/java/org/zstack/network/service/virtualrouter/VirtualRouterApiInterceptor.java

Comment on lines +49 to 50
if (fmt.indexOf(TAG_DELIMITER) == -1) {
return fmt.equals(tag);
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 | ⚡ Quick win

isMatch 的无分隔符快速返回引入了 token 模板匹配回归。

Line 49-50 现在直接 fmt.equals(tag),会把 "{token}" 这类合法模板错误判定为不匹配。

建议修复
+        if (tag == null) {
+            return false;
+        }
-        if (fmt.indexOf(TAG_DELIMITER) == -1) {
+        if (fmt.indexOf(TAG_DELIMITER) == -1 && !isTokenField(fmt)) {
             return fmt.equals(tag);
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@utils/src/main/java/org/zstack/utils/TagUtils.java` around lines 49 - 50, The
fast-return in isMatch that does "if (fmt.indexOf(TAG_DELIMITER) == -1) return
fmt.equals(tag);" wrongly treats templates like "{token}" as non-matching;
instead, change this branch to consider template tokens: when TAG_DELIMITER is
absent in fmt, return true if fmt.equals(tag) OR fmt itself is a single token
template that should match any valid tag value (e.g., fmt.matches("\\{[^}]+}"))
and tag is non-empty (or matches the expected token pattern). Update isMatch to
use these checks (referencing isMatch, fmt, tag, and TAG_DELIMITER) so
token-template strings are treated as matching rather than always false.

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.

1 participant