Skip to content

fix[zbs]: fail attach when host deploy failure ratio reaches threshold (ZSTAC-86222)#4473

Open
ZStack-Robot wants to merge 1 commit into
5.5.28from
sync/jin.ma/fix/ZSTAC-86222-attach-deploy-threshold@@2
Open

fix[zbs]: fail attach when host deploy failure ratio reaches threshold (ZSTAC-86222)#4473
ZStack-Robot wants to merge 1 commit into
5.5.28from
sync/jin.ma/fix/ZSTAC-86222-attach-deploy-threshold@@2

Conversation

@ZStack-Robot

Copy link
Copy Markdown
Collaborator

问题(ZSTAC-86222)

vhost-zbs 云主机创建失败。真正根因是 AttachPrimaryStorageToCluster 时 vhost target/client deploy 失败(如 free hugepage: 0docker not installed),但 ZbsStorageController.deployClient 把失败吞掉(只 logger.warntrigger.next()),导致 host 被标 Connected、attach 成功,而 vhost 实际没起来——后续创建 VM 才失败。

修复

  1. ZbsStorageController.deployClient:prepare-vhost-target-env 和 deploy-vhost-target 失败时改为 trigger.fail(原来吞错误)。
  2. ExternalPrimaryStorage.attachHook:串行遍历累计失败数,一旦 失败 host 比例 ≥ 阈值 立即 allDone 提前退出并判 attach 失败;未达阈值则继续,失败 host 记 Disconnected 由 periodic ping 自愈。
  3. 新增 GlobalConfig externalPrimaryStorage.attach.hostDeployFailureRatioThreshold(默认 0.3)。

测试

  • 新增 ZbsVhostAttachDeployFailureCase(达阈值 fail / 未达阈值 success+自愈)。
  • 修复既有 ZbsVhostVolumeCase.testProtocolRecoveryBackoff(1 host 环境,deploy 一直失败测自愈,调高阈值使 attach 不被新逻辑阻断)。
  • 本地实跑通过。

关联

premium 侧 @@2 同名分支补 GlobalConfig markdown 文档(GenerateGlobalConfigTemplateAndMarkDownCase)。

Resolves: ZSTAC-86222

sync from gitlab !10434

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

.coderabbit.yaml has a parsing error

The CodeRabbit configuration file in this repository has a parsing error and default settings were used instead. Please fix the error(s) in the configuration file. You can initialize chat with CodeRabbit to get help with the configuration file.

💥 Parsing errors (1)
Could not fetch remote config from http://open.zstack.ai:20001/code-reviews/zstack-cloud.yaml: TimeoutError: The operation was aborted due to timeout
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Walkthrough

本次变更调整了 ZBS vhost 部署失败的错误传播,并为外部主存储挂载引入失败比例阈值配置、校验、运行时判定与相关集成测试,同时补充了已有用例的配置。

Changes

Vhost 部署失败处理与挂载容错

Layer / File(s) Summary
Vhost 部署流程失败传播修正
plugin/zbs/src/main/java/org/zstack/storage/zbs/ZbsStorageController.java, utils/src/main/java/org/zstack/utils/clouderrorcode/CloudOperationsErrorCode.java
prepare-vhost-target-envdeploy-vhost-target 的失败回调改为直接终止 flow,并补充相关公开错误码常量。
新增失败比例阈值全局配置
storage/src/main/java/org/zstack/storage/addon/primary/ExternalPrimaryStorageGlobalConfig.java, storage/src/main/java/org/zstack/storage/addon/primary/ExternalPrimaryStorageFactory.java
新增 ATTACH_HOST_DEPLOY_FAILURE_RATIO_THRESHOLD 配置及其取值校验,限制阈值范围在 0 到 1 之间。
ExternalPrimaryStorage 挂载阈值容错
storage/src/main/java/org/zstack/storage/addon/primary/ExternalPrimaryStorage.java
attachHook() 记录成功部署主机、失败计数和阈值判断,并在成功分支使用成功主机激活心跳卷拓扑。
新增挂载失败阈值集成测试
test/src/test/groovy/org/zstack/test/integration/storage/primary/addon/zbs/ZbsVhostAttachDeployFailureCase.groovy, test/src/test/groovy/org/zstack/test/integration/storage/primary/addon/zbs/ZbsVhostVolumeCase.groovy
新增测试类覆盖阈值触发失败、低于阈值成功和失败主机后续自愈流程,并调整现有回退测试的阈值配置。

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ExternalPrimaryStorage
  participant DeployClient
  participant GlobalConfig
  participant HeartbeatVolume

  ExternalPrimaryStorage->>GlobalConfig: 读取 ATTACH_HOST_DEPLOY_FAILURE_RATIO_THRESHOLD
  loop 每台主机
    ExternalPrimaryStorage->>DeployClient: deployClient
    DeployClient-->>ExternalPrimaryStorage: 成功或失败回调
    alt 成功
      ExternalPrimaryStorage->>ExternalPrimaryStorage: 记录 deployedHosts
    else 失败
      ExternalPrimaryStorage->>ExternalPrimaryStorage: 更新状态为 Disconnected 并递增 failedCount
      ExternalPrimaryStorage->>ExternalPrimaryStorage: 比较 failedCount / totalHosts 与 threshold
    end
  end
  ExternalPrimaryStorage->>HeartbeatVolume: 用 deployedHosts.peek() 激活心跳卷拓扑
Loading

Poem

小兔蹦蹦看阈值,
一半成败各有期。
失败不再悄悄过,
成功主机领心脉。
🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed 标题准确概括了当主机部署失败比例达到阈值时阻止 attach 的核心改动。
Description check ✅ Passed 描述与实际改动一致,涵盖了失败处理、阈值控制、新配置和相关测试。
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ 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/jin.ma/fix/ZSTAC-86222-attach-deploy-threshold@@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.44.1)
utils/src/main/java/org/zstack/utils/clouderrorcode/CloudOperationsErrorCode.java

ast-grep timed out on this file


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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
plugin/zbs/src/main/java/org/zstack/storage/zbs/ZbsStorageController.java (1)

423-428: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

deploy-vhost-target 的 host 缺失分支应直接失败

同一条 vhost 流程里,activate/deactivategetKvmHost(h) == null 时都会返回错误;这里却只是 warntrigger.next(),会把“实际没部署成功”当成成功继续走下去。建议同样改为失败中断。

🐛 建议修复
                             KVMHostVO host = getKvmHost(h);
                             if (host == null) {
-                                logger.warn(String.format("cannot find kvm host[uuid:%s], skip vhost target deploy", h.getUuid()));
-                                trigger.next();
-                                return;
+                                trigger.fail(operr(ORG_ZSTACK_STORAGE_ZBS_10010, "cannot find kvm host[uuid:%s], unable to deploy vhost target", h.getUuid()));
+                                return;
                             }
🤖 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/ZbsStorageController.java`
around lines 423 - 428, The deploy-vhost-target path in ZbsStorageController
should not continue when getKvmHost(h) returns null, since that means the target
host is missing and the operation did not actually succeed. Update this branch
to fail the flow instead of only logging a warning and calling trigger.next(),
matching the existing activate/deactivate handling in the same class by
returning an error through the current trigger/error path and stopping further
execution.
storage/src/main/java/org/zstack/storage/addon/primary/ExternalPrimaryStorage.java (1)

140-190: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

避免始终使用 hostInventories.get(0) 激活心跳卷。
storage/src/main/java/org/zstack/storage/addon/primary/ExternalPrimaryStorage.java:190 这里在失败比例低于阈值时会继续放行,但第一个 host 可能正是 deployClient 失败的那个,activateHeartbeatVolumeForAttach 仍会在收尾阶段失败,抵消这次容错。应从成功部署的 host 中选一个,或在没有成功 host 时直接失败。

🤖 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
`@storage/src/main/java/org/zstack/storage/addon/primary/ExternalPrimaryStorage.java`
around lines 140 - 190, The attach flow in ExternalPrimaryStorage currently
always calls activateHeartbeatVolumeForAttach with hostInventories.get(0), which
can pick a host whose deployClient failed and cause a late attach failure.
Update the completion path in the WhileDoneCompletion for the attach/reconnect
loop to choose a host that successfully completed deployClient and
updateHostProtocolStatus, and pass that host to
activateHeartbeatVolumeForAttach; if no host succeeded, fail the operation
instead of defaulting to the first entry.
🧹 Nitpick comments (1)
plugin/zbs/src/main/java/org/zstack/storage/zbs/ZbsStorageController.java (1)

408-408: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

prepare-vhost-target-env 失败复用了 update-host-client-dependency 的错误码

Line 408 新增的失败路径复用了 ORG_ZSTACK_STORAGE_ZBS_10011(该错误码在 Line 374 已用于 update-host-client-dependency 步骤失败)。两处语义不同(依赖更新 vs vhost 环境准备),复用同一错误码会降低问题定位和日志可追溯性。建议为 prepare-vhost-target-env 失败分配独立的错误码常量。

🤖 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/ZbsStorageController.java` at
line 408, The prepare-vhost-target-env failure path in ZbsStorageController is
reusing the same error code as update-host-client-dependency, so assign a
separate error code constant for the vhost environment preparation failure and
update the trigger.fail(operr(...)) call to use that new symbol instead of
ORG_ZSTACK_STORAGE_ZBS_10011.
🤖 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.

Outside diff comments:
In `@plugin/zbs/src/main/java/org/zstack/storage/zbs/ZbsStorageController.java`:
- Around line 423-428: The deploy-vhost-target path in ZbsStorageController
should not continue when getKvmHost(h) returns null, since that means the target
host is missing and the operation did not actually succeed. Update this branch
to fail the flow instead of only logging a warning and calling trigger.next(),
matching the existing activate/deactivate handling in the same class by
returning an error through the current trigger/error path and stopping further
execution.

In
`@storage/src/main/java/org/zstack/storage/addon/primary/ExternalPrimaryStorage.java`:
- Around line 140-190: The attach flow in ExternalPrimaryStorage currently
always calls activateHeartbeatVolumeForAttach with hostInventories.get(0), which
can pick a host whose deployClient failed and cause a late attach failure.
Update the completion path in the WhileDoneCompletion for the attach/reconnect
loop to choose a host that successfully completed deployClient and
updateHostProtocolStatus, and pass that host to
activateHeartbeatVolumeForAttach; if no host succeeded, fail the operation
instead of defaulting to the first entry.

---

Nitpick comments:
In `@plugin/zbs/src/main/java/org/zstack/storage/zbs/ZbsStorageController.java`:
- Line 408: The prepare-vhost-target-env failure path in ZbsStorageController is
reusing the same error code as update-host-client-dependency, so assign a
separate error code constant for the vhost environment preparation failure and
update the trigger.fail(operr(...)) call to use that new symbol instead of
ORG_ZSTACK_STORAGE_ZBS_10011.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 30bc2223-925d-47f7-9a9c-74d8726cefc7

📥 Commits

Reviewing files that changed from the base of the PR and between 6eda856 and 4850ae0.

📒 Files selected for processing (5)
  • plugin/zbs/src/main/java/org/zstack/storage/zbs/ZbsStorageController.java
  • storage/src/main/java/org/zstack/storage/addon/primary/ExternalPrimaryStorage.java
  • storage/src/main/java/org/zstack/storage/addon/primary/ExternalPrimaryStorageGlobalConfig.java
  • test/src/test/groovy/org/zstack/test/integration/storage/primary/addon/zbs/ZbsVhostAttachDeployFailureCase.groovy
  • test/src/test/groovy/org/zstack/test/integration/storage/primary/addon/zbs/ZbsVhostVolumeCase.groovy

@MatheMatrix MatheMatrix force-pushed the sync/jin.ma/fix/ZSTAC-86222-attach-deploy-threshold@@2 branch 2 times, most recently from 484fdec to 31960a4 Compare July 7, 2026 04:22
@zstack-robot-2

Copy link
Copy Markdown
Collaborator

Comment from jin.ma:

感谢 review,已处理 CodeRabbit 意见并重推(amend,复用 Change-Id):

✅ 已修(Major)— ExternalPrimaryStorage.attachHookhostInventories.get(0) 激活心跳卷
确为真 bug:低于阈值放行时,若 host[0] 恰是 deploy 失败的那个,activateHeartbeatVolumeForAttach 会在坏 host 上失败,抵消阈值容错。已改为:deploy 成功的 host 收集到 ConcurrentLinkedQueue,收尾从中取一个激活;无成功 host 则直接 fail(新 error code ORG_ZSTACK_STORAGE_ADDON_PRIMARY_10041)。

✅ 已修(Nitpick)— prepare-vhost-target-env 失败复用 ZBS_10011
已分配独立错误码 ORG_ZSTACK_STORAGE_ZBS_10042,与 update-host-client-dependency(10011)区分。

⏭️ 暂不改(Major)— deploy-vhost-targetgetKvmHost(h)==null 分支
该分支本 MR 未改动(是既有代码),且 attach 层已有阈值容错 + periodic ping 自愈兜底缺失 host;为保持本 MR surgical,暂不扩大范围。如需统一 fail 语义,建议单独 issue 跟进。

验证:全量 build SUCCESS;ZbsVhostAttachDeployFailureCase 本地通过(Tests run: 1, Failures: 0)。

@MatheMatrix MatheMatrix force-pushed the sync/jin.ma/fix/ZSTAC-86222-attach-deploy-threshold@@2 branch from 31960a4 to 0763c5d Compare July 7, 2026 06:02
@ZStack-Robot

Copy link
Copy Markdown
Collaborator Author

Comment from jin.ma:

按 review 意见进一步简化了 attach 收尾逻辑(amend,复用 Change-Id):

  1. 去掉冗余报错:删掉「无成功 host 则 fail」分支和收尾的 failedCount > 0 汇总 warn(这些在阈值合法时不会触发,属多余)。改为每个 host deploy 失败时在 fail 回调里 logger.warn 一次即可。心跳卷仍从成功部署的 host(deployedHosts.peek())激活,规避 get(0) 可能选到失败 host 的问题。

  2. 阈值合法性校验:新增 0 < value < 1 的校验。因内置 @GlobalConfigValidation 的数值范围只支持 Long/Integer(对 Double 会 Long.parseLong 失败),改用 ExternalPrimaryStorageFactory.start()installValidateExtension 注册 validator(参照 HaGlobalConfig.CHECK_SUCCESS_RATIO 的做法)。非法值(≤0 或 ≥1)在更新配置时即被拒。

验证:全量 build SUCCESS;ZbsVhostAttachDeployFailureCase 本地通过(Tests run: 1, Failures: 0)。

@zstack-robot-2

Copy link
Copy Markdown
Collaborator

Comment from gitlab:

自上次添加REVIEWED标签(2026-07-07 14:13:04.000Z)后, 有新的COMMIT更新(2026-07-07 14:16:55.366Z), 所以移除了REVIEWED标签

@MatheMatrix MatheMatrix force-pushed the sync/jin.ma/fix/ZSTAC-86222-attach-deploy-threshold@@2 branch 2 times, most recently from 11ba6a6 to 8c9b4c5 Compare July 8, 2026 10:36
@ZStack-Robot

Copy link
Copy Markdown
Collaborator Author

Comment from gitlab:

自上次添加REVIEWED标签(2026-07-07 14:25:27.000Z)后, 有新的COMMIT更新(2026-07-08 18:36:12.186Z), 所以移除了REVIEWED标签

@MatheMatrix MatheMatrix force-pushed the sync/jin.ma/fix/ZSTAC-86222-attach-deploy-threshold@@2 branch from 8c9b4c5 to d060442 Compare July 8, 2026 19:17

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
storage/src/main/java/org/zstack/storage/addon/primary/ExternalPrimaryStorage.java (1)

181-190: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

阈值为 1 时先判空再激活心跳卷
attach.hostDeployFailureRatioThreshold=1 且所有主机都部署失败时,deployedHosts 为空,peek() 会返回 null,随后会把空主机传给 activateHeartbeatVolume(...)。这里应在激活前显式失败,避免 attach 流程直接走到 NPE。

🤖 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
`@storage/src/main/java/org/zstack/storage/addon/primary/ExternalPrimaryStorage.java`
around lines 181 - 190, In ExternalPrimaryStorage’s WhileDoneCompletion.done
path, add an explicit empty-check for deployedHosts before calling
activateHeartbeatVolumeForAttach, since when
attach.hostDeployFailureRatioThreshold is 1 and all deployments fail,
deployedHosts.peek() can be null and the attach flow may pass a null host into
heartbeat activation. If deployedHosts is empty, fail the provided completion
immediately with a clear error instead of continuing; otherwise proceed to
activateHeartbeatVolumeForAttach with the selected host.
🤖 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
`@storage/src/main/java/org/zstack/storage/addon/primary/ExternalPrimaryStorageFactory.java`:
- Around line 177-183: In ExternalPrimaryStorageFactory’s
ATTACH_HOST_DEPLOY_FAILURE_RATIO_THRESHOLD validation callback, normalize all
invalid-value failures to GlobalConfigException instead of letting
Double.parseDouble throw NumberFormatException or using
OperationFailureException for range checks. Update the installValidateExtension
lambda so both parsing and bounds validation report a consistent
GlobalConfigException with the existing config identifiers and message context,
keeping the global config update path handling validation failures uniformly.

---

Outside diff comments:
In
`@storage/src/main/java/org/zstack/storage/addon/primary/ExternalPrimaryStorage.java`:
- Around line 181-190: In ExternalPrimaryStorage’s WhileDoneCompletion.done
path, add an explicit empty-check for deployedHosts before calling
activateHeartbeatVolumeForAttach, since when
attach.hostDeployFailureRatioThreshold is 1 and all deployments fail,
deployedHosts.peek() can be null and the attach flow may pass a null host into
heartbeat activation. If deployedHosts is empty, fail the provided completion
immediately with a clear error instead of continuing; otherwise proceed to
activateHeartbeatVolumeForAttach with the selected host.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: ddd79905-e161-4fad-b5e4-5d40915cc314

📥 Commits

Reviewing files that changed from the base of the PR and between 4850ae0 and d060442.

📒 Files selected for processing (7)
  • plugin/zbs/src/main/java/org/zstack/storage/zbs/ZbsStorageController.java
  • storage/src/main/java/org/zstack/storage/addon/primary/ExternalPrimaryStorage.java
  • storage/src/main/java/org/zstack/storage/addon/primary/ExternalPrimaryStorageFactory.java
  • storage/src/main/java/org/zstack/storage/addon/primary/ExternalPrimaryStorageGlobalConfig.java
  • test/src/test/groovy/org/zstack/test/integration/storage/primary/addon/zbs/ZbsVhostAttachDeployFailureCase.groovy
  • test/src/test/groovy/org/zstack/test/integration/storage/primary/addon/zbs/ZbsVhostVolumeCase.groovy
  • utils/src/main/java/org/zstack/utils/clouderrorcode/CloudOperationsErrorCode.java
✅ Files skipped from review due to trivial changes (1)
  • utils/src/main/java/org/zstack/utils/clouderrorcode/CloudOperationsErrorCode.java
🚧 Files skipped from review as they are similar to previous changes (4)
  • test/src/test/groovy/org/zstack/test/integration/storage/primary/addon/zbs/ZbsVhostVolumeCase.groovy
  • storage/src/main/java/org/zstack/storage/addon/primary/ExternalPrimaryStorageGlobalConfig.java
  • test/src/test/groovy/org/zstack/test/integration/storage/primary/addon/zbs/ZbsVhostAttachDeployFailureCase.groovy
  • plugin/zbs/src/main/java/org/zstack/storage/zbs/ZbsStorageController.java

When attaching an external primary storage to a cluster,
ZbsStorageController.deployClient swallowed vhost target deploy and
prepare-env failures (only logger.warn then trigger.next), so a host was
marked Connected even though vhost was not actually deployed (e.g. free
hugepage: 0, docker not installed). The attach then reported success
while the storage was unusable, and VM creation failed later.

Make prepare-vhost-target-env and deploy-vhost-target failures fail
deployClient. In ExternalPrimaryStorage.attachHook, fail the attach only
when the ratio of failed hosts reaches the configurable threshold
externalPrimaryStorage.attach.hostDeployFailureRatioThreshold
(default 0.3); below the threshold the attach succeeds and the failed
hosts are marked Disconnected and recovered by the periodic ping
self-heal.

Resolves: ZSTAC-86222

Change-Id: I2aee53c1f997d5a1e0c546e1329b83c1d81c9c48
@MatheMatrix MatheMatrix force-pushed the sync/jin.ma/fix/ZSTAC-86222-attach-deploy-threshold@@2 branch from d060442 to 6ef6d94 Compare July 9, 2026 01:34

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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
`@storage/src/main/java/org/zstack/storage/addon/primary/ExternalPrimaryStorageFactory.java`:
- Around line 178-190: The validation for
ExternalPrimaryStorageGlobalConfig.ATTACH_HOST_DEPLOY_FAILURE_RATIO_THRESHOLD
currently allows threshold == 1, which conflicts with the intended open interval
(0, 1). Update the installValidateExtension logic in
ExternalPrimaryStorageFactory so the bound check rejects 1 as well (use v >= 1),
and adjust the GlobalConfigException message to say the value must be greater
than 0 and less than 1.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 9793b2e7-15fc-4070-965c-80e40d727e69

📥 Commits

Reviewing files that changed from the base of the PR and between d060442 and 6ef6d94.

📒 Files selected for processing (7)
  • plugin/zbs/src/main/java/org/zstack/storage/zbs/ZbsStorageController.java
  • storage/src/main/java/org/zstack/storage/addon/primary/ExternalPrimaryStorage.java
  • storage/src/main/java/org/zstack/storage/addon/primary/ExternalPrimaryStorageFactory.java
  • storage/src/main/java/org/zstack/storage/addon/primary/ExternalPrimaryStorageGlobalConfig.java
  • test/src/test/groovy/org/zstack/test/integration/storage/primary/addon/zbs/ZbsVhostAttachDeployFailureCase.groovy
  • test/src/test/groovy/org/zstack/test/integration/storage/primary/addon/zbs/ZbsVhostVolumeCase.groovy
  • utils/src/main/java/org/zstack/utils/clouderrorcode/CloudOperationsErrorCode.java
🚧 Files skipped from review as they are similar to previous changes (6)
  • test/src/test/groovy/org/zstack/test/integration/storage/primary/addon/zbs/ZbsVhostVolumeCase.groovy
  • utils/src/main/java/org/zstack/utils/clouderrorcode/CloudOperationsErrorCode.java
  • storage/src/main/java/org/zstack/storage/addon/primary/ExternalPrimaryStorageGlobalConfig.java
  • test/src/test/groovy/org/zstack/test/integration/storage/primary/addon/zbs/ZbsVhostAttachDeployFailureCase.groovy
  • storage/src/main/java/org/zstack/storage/addon/primary/ExternalPrimaryStorage.java
  • plugin/zbs/src/main/java/org/zstack/storage/zbs/ZbsStorageController.java

Comment on lines +178 to +190
ExternalPrimaryStorageGlobalConfig.ATTACH_HOST_DEPLOY_FAILURE_RATIO_THRESHOLD.installValidateExtension((category, name, oldValue, newValue) -> {
double v;
try {
v = Double.parseDouble(newValue);
} catch (NumberFormatException e) {
throw new GlobalConfigException(String.format(
"the value[%s] of %s.%s is not a valid number", newValue, category, name), e);
}
if (v <= 0 || v > 1) {
throw new GlobalConfigException(String.format(
"the value[%s] of %s.%s must be greater than 0 and not greater than 1", newValue, category, name));
}
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

校验边界与设计意图不一致,threshold=1 会使阈值保护完全失效。

PR 目标中明确指出该配置应仅接受开区间 (0, 1) 的取值(即 0 和 1 都应被拒绝),但当前代码 v <= 0 || v > 1 允许 v == 1 通过校验。

结合 ExternalPrimaryStorage.attachHook 中的判断逻辑 if ((double) failedCount.incrementAndGet() / totalHosts > threshold):当 threshold == 1 时,failedCount / totalHosts 永远不会大于 1(因为失败数不会超过总数),导致即使所有主机部署失败,attach 也不会因阈值判断而失败——阈值保护机制被静默禁用。

建议将上边界改为 v >= 1,与设计意图(开区间)保持一致。

🐛 建议修复
-            if (v <= 0 || v > 1) {
+            if (v <= 0 || v >= 1) {
                 throw new GlobalConfigException(String.format(
                         "the value[%s] of %s.%s must be greater than 0 and not greater than 1", newValue, category, name));
             }

(同时应同步更新错误提示文案,例如改为 "must be greater than 0 and less than 1"。)

📝 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
ExternalPrimaryStorageGlobalConfig.ATTACH_HOST_DEPLOY_FAILURE_RATIO_THRESHOLD.installValidateExtension((category, name, oldValue, newValue) -> {
double v;
try {
v = Double.parseDouble(newValue);
} catch (NumberFormatException e) {
throw new GlobalConfigException(String.format(
"the value[%s] of %s.%s is not a valid number", newValue, category, name), e);
}
if (v <= 0 || v > 1) {
throw new GlobalConfigException(String.format(
"the value[%s] of %s.%s must be greater than 0 and not greater than 1", newValue, category, name));
}
});
ExternalPrimaryStorageGlobalConfig.ATTACH_HOST_DEPLOY_FAILURE_RATIO_THRESHOLD.installValidateExtension((category, name, oldValue, newValue) -> {
double v;
try {
v = Double.parseDouble(newValue);
} catch (NumberFormatException e) {
throw new GlobalConfigException(String.format(
"the value[%s] of %s.%s is not a valid number", newValue, category, name), e);
}
if (v <= 0 || v >= 1) {
throw new GlobalConfigException(String.format(
"the value[%s] of %s.%s must be greater than 0 and not greater than 1", newValue, category, name));
}
});
🤖 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
`@storage/src/main/java/org/zstack/storage/addon/primary/ExternalPrimaryStorageFactory.java`
around lines 178 - 190, The validation for
ExternalPrimaryStorageGlobalConfig.ATTACH_HOST_DEPLOY_FAILURE_RATIO_THRESHOLD
currently allows threshold == 1, which conflicts with the intended open interval
(0, 1). Update the installValidateExtension logic in
ExternalPrimaryStorageFactory so the bound check rejects 1 as well (use v >= 1),
and adjust the GlobalConfigException message to say the value must be greater
than 0 and less than 1.

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.

3 participants