Skip to content

<fix>[compute]: fix VM clone quota check fail#3624

Open
zstack-robot-1 wants to merge 1 commit into5.5.12from
sync/tian.huang/huangtian-ZSTAC-83646@@2
Open

<fix>[compute]: fix VM clone quota check fail#3624
zstack-robot-1 wants to merge 1 commit into5.5.12from
sync/tian.huang/huangtian-ZSTAC-83646@@2

Conversation

@zstack-robot-1
Copy link
Copy Markdown
Collaborator

1.Clone a tenant VM using admin account, quota check
didn't base on current role.
2.quota error is overwritten during exception handling.
http://jira.zstack.io/browse/ZSTAC-83646

Resolves: ZSTAC-83646

Change-Id: I776c6b7a786d79746f616b716f736f646e686868

sync from gitlab !9480

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 27, 2026

Walkthrough

本次变更在主机分配流程中引入了基于会话账户 UUID 的管理员权限短路路径,并将配额检查从直接抛异常改为返回 ErrorCode 的可组合检查,新增消息/规范字段以在分配链路中携带账户信息。

Changes

Cohort / File(s) Summary
配额分配主流程
compute/src/main/java/org/zstack/compute/allocator/QuotaAllocatorFlow.java
allocate() 增加读取 spec.getAccountUuid() 的管理员判定,管理员直接跳过配额检查并调用 next(candidates);非管理员调用改为使用返回 ErrorCode 的检查方法,若返回非空则抛出 OperationFailureException
配额检查逻辑与工具
compute/src/main/java/org/zstack/compute/vm/VmQuotaOperator.java, identity/src/main/java/org/zstack/identity/QuotaUtil.java
新增 *WithResult 系列方法,返回 ErrorCode 代替直接抛异常;保留原有 void 方法并在内部委托给 WithResult 版本以在非空返回时抛出相应异常;新增 checkQuotaAndReturn(...),并由原 CheckQuota(...) 使用该方法实现抛错行为。
分配消息与规范扩展
header/src/main/java/org/zstack/header/allocator/AllocateHostMsg.java, header/src/main/java/org/zstack/header/allocator/HostAllocatorSpec.java
AllocateHostMsgHostAllocatorSpec 中新增 accountUuid 字段及其 getter/setter;HostAllocatorSpec.fromAllocationMsg(...) 传递并设置该字段,使会话账户 UUID 在分配流程中流转。

Sequence Diagram

sequenceDiagram
    participant QAF as QuotaAllocatorFlow
    participant VMQ as VmQuotaOperator
    participant QU as QuotaUtil

    QAF->>QAF: throwExceptionIfIAmTheFirstFlow()
    QAF->>QAF: spec.getAccountUuid() 并判断 isAdminPermission
    alt 管理员
        QAF->>QAF: next(candidates)
    else 非管理员
        QAF->>VMQ: checkVmCupAndMemoryCapacityWithResult(...)
        VMQ->>QU: checkQuotaAndReturn(cpu/memory quota)
        QU-->>VMQ: ErrorCode|null
        VMQ-->>QAF: ErrorCode|null
        alt 返回 ErrorCode
            QAF->>QAF: throw OperationFailureException(error)
        else 无错误
            QAF->>VMQ: checkVmInstanceQuotaWithResult(...)
            VMQ->>QU: checkQuotaAndReturn(instance quotas)
            QU-->>VMQ: ErrorCode|null
            VMQ-->>QAF: ErrorCode|null
            alt 返回 ErrorCode
                QAF->>QAF: throw OperationFailureException(error)
            else 无错误
                QAF->>QAF: next(candidates)
            end
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰✨ 我是小兔写代码,跳入配额田,
管理员一跳过,流程轻又闲,
错误变成信号,悄悄传回来,
新字段一路随,从消息到规范,
分配小径更通达,兔耳欢快摇摆。

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title follows the required format '[scope]: ' with 45 characters, meeting the 72-character limit.
Description check ✅ Passed The description clearly relates to the changeset, detailing two specific issues being fixed: admin quota bypass and quota error preservation during exception handling.

✏️ 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/tian.huang/huangtian-ZSTAC-83646@@2

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

Caution

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

⚠️ Outside diff range comments (1)
identity/src/main/java/org/zstack/identity/QuotaUtil.java (1)

35-35: ⚠️ Potential issue | 🟡 Minor

导入语句格式异常。

这一行的格式看起来有问题,/** 紧跟在分号后面,可能是合并冲突或编辑错误导致的。

🐛 建议修复
-import static org.zstack.utils.clouderrorcode.CloudOperationsErrorCode.*;/**
+import static org.zstack.utils.clouderrorcode.CloudOperationsErrorCode.*;
+
+/**
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@identity/src/main/java/org/zstack/identity/QuotaUtil.java` at line 35, The
import line in QuotaUtil.java is malformed because a stray "/**" is appended to
the end of the import statement; edit the import statement that references
org.zstack.utils.clouderrorcode.CloudOperationsErrorCode (the static import
line) to remove the trailing "/**" and ensure the line ends with a semicolon and
nothing else so the file compiles and imports correctly.
🧹 Nitpick comments (2)
header/src/main/java/org/zstack/header/allocator/AllocateHostMsg.java (1)

34-34: 新增字段及其访问器的实现是正确的。

该字段用于在主机分配流程中传递会话账户 UUID,以便在配额检查时区分操作者身份。

一个小建议:getter 和 setter 的格式(单行书写)与文件中其他方法风格不一致,建议调整为多行格式以保持一致性。

♻️ 可选的格式调整
-    public String getSessionAccountUuid() { return sessionAccountUuid; }
-
-    public void setSessionAccountUuid(String sessionAccountUuid) { this.sessionAccountUuid = sessionAccountUuid; }
+    public String getSessionAccountUuid() {
+        return sessionAccountUuid;
+    }
+
+    public void setSessionAccountUuid(String sessionAccountUuid) {
+        this.sessionAccountUuid = sessionAccountUuid;
+    }

Also applies to: 215-218

🤖 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/allocator/AllocateHostMsg.java` at
line 34, 新增的字段 sessionAccountUuid 与其访问器 getSessionAccountUuid 和
setSessionAccountUuid 已实现正确,但访问器采用单行写法与文件其余方法风格不一致;请将 getSessionAccountUuid 和
setSessionAccountUuid 改为标准多行 getter/setter 格式(每个方法各占多行,带花括号和
return/this.assignment 行),并同时修正文件中另一处相同问题的访问器(提到的 215-218 位置的同名或类似方法)以保持风格一致性。
header/src/main/java/org/zstack/header/allocator/HostAllocatorSpec.java (1)

165-171: getter/setter 的位置破坏了相关方法的分组。

getSessionAccountUuid()setSessionAccountUuid() 被放置在 getL3NetworkUuids()setL3NetworkUuids() 之间,导致 L3NetworkUuids 的 getter/setter 被分隔开。建议将 sessionAccountUuid 的访问器移至字段声明附近的其他 getter/setter 区域,以保持代码组织的一致性。

🤖 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/allocator/HostAllocatorSpec.java`
around lines 165 - 171, The two accessors getSessionAccountUuid() and
setSessionAccountUuid() are misplaced between getL3NetworkUuids() and
setL3NetworkUuids(), splitting that pair; move getSessionAccountUuid() and
setSessionAccountUuid() out of the middle and place them adjacent to the other
sessionAccountUuid accessors (near the field declaration or the other related
getters/setters) so that getL3NetworkUuids() and setL3NetworkUuids() remain
contiguous in class HostAllocatorSpec.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@compute/src/main/java/org/zstack/compute/allocator/QuotaAllocatorFlow.java`:
- Around line 56-61: The admin-check currently in QuotaAllocatorFlow uses
AccountConstant.isAdminPermission(sessionAccountUuid) to skip quota checks;
change it to use QuotaUtil's account-type check so it matches other quota logic:
replace the AccountConstant.isAdminPermission(...) checks around
spec.getSessionAccountUuid() in QuotaAllocatorFlow with a call to new
QuotaUtil().isAdminAccount(...) (or the equivalent QuotaUtil.isAdminAccount
usage) so the flow treats all SystemAdmin accounts as admins consistent with
QuotaChecker and VmQuotaOperator.

---

Outside diff comments:
In `@identity/src/main/java/org/zstack/identity/QuotaUtil.java`:
- Line 35: The import line in QuotaUtil.java is malformed because a stray "/**"
is appended to the end of the import statement; edit the import statement that
references org.zstack.utils.clouderrorcode.CloudOperationsErrorCode (the static
import line) to remove the trailing "/**" and ensure the line ends with a
semicolon and nothing else so the file compiles and imports correctly.

---

Nitpick comments:
In `@header/src/main/java/org/zstack/header/allocator/AllocateHostMsg.java`:
- Line 34: 新增的字段 sessionAccountUuid 与其访问器 getSessionAccountUuid 和
setSessionAccountUuid 已实现正确,但访问器采用单行写法与文件其余方法风格不一致;请将 getSessionAccountUuid 和
setSessionAccountUuid 改为标准多行 getter/setter 格式(每个方法各占多行,带花括号和
return/this.assignment 行),并同时修正文件中另一处相同问题的访问器(提到的 215-218 位置的同名或类似方法)以保持风格一致性。

In `@header/src/main/java/org/zstack/header/allocator/HostAllocatorSpec.java`:
- Around line 165-171: The two accessors getSessionAccountUuid() and
setSessionAccountUuid() are misplaced between getL3NetworkUuids() and
setL3NetworkUuids(), splitting that pair; move getSessionAccountUuid() and
setSessionAccountUuid() out of the middle and place them adjacent to the other
sessionAccountUuid accessors (near the field declaration or the other related
getters/setters) so that getL3NetworkUuids() and setL3NetworkUuids() remain
contiguous in class HostAllocatorSpec.
🪄 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: 488d71f8-befd-414f-99bc-e337a0fb6044

📥 Commits

Reviewing files that changed from the base of the PR and between c4c23a6 and c0bceed.

📒 Files selected for processing (5)
  • compute/src/main/java/org/zstack/compute/allocator/QuotaAllocatorFlow.java
  • compute/src/main/java/org/zstack/compute/vm/VmQuotaOperator.java
  • header/src/main/java/org/zstack/header/allocator/AllocateHostMsg.java
  • header/src/main/java/org/zstack/header/allocator/HostAllocatorSpec.java
  • identity/src/main/java/org/zstack/identity/QuotaUtil.java

@ZStack-Robot
Copy link
Copy Markdown
Collaborator

Comment from yaohua.wu:

Review: MR !9480 — ZSTAC-83646 fix VM clone quota check fail

关联 MR: premium !13346
Jira: ZSTAC-83646 — admin clone 租户 VM 时 quota 校验基于租户而非操作者 + quota 报错被覆盖为 unhandled exception

变更概述

本 MR 修复两个问题:

  1. admin bypass:在 AllocateHostMsg / HostAllocatorSpec 中新增 sessionAccountUuid 字段透传操作者身份,QuotaAllocatorFlow 据此跳过 admin 的 quota 校验。
  2. 错误传播QuotaUtil.CheckQuota() 抛出的 ApiMessageInterceptionException 在 host allocator chain 中被吞掉覆盖为 "unhandled exception"。新增 checkQuotaAndReturn() 返回 ErrorCode,由 QuotaAllocatorFlow 包装为 OperationFailureException(框架正确处理),保留原始 quota 错误信息。

架构上设计合理:WithResult 变体返回 ErrorCode,旧方法保留为 wrapper 抛 ApiMessageInterceptionException,向后兼容无回归风险。

Warning

  1. 🟡 [QuotaAllocatorFlow.java] 遗忘路径风险 — sessionAccountUuid 依赖调用方逐一设置

    当前 sessionAccountUuid 仅在 MevocoVmInstanceBase 的 clone dryRun 路径中设置。但 DesignatedAllocateHostMsg 在代码库中还有 8+ 处构造(VmInstanceBase.getStartingCandidateHosts L919、getMigrationTargetHosts L2002、VmAllocateHostFlowVmAllocateHostForStoppedVmFlow 等),均未设置此字段。

    虽然这些路径的 admin 操作通常在 API interceptor 层已通过 AccountType.SystemAdmin 跳过了 quota 检查,但 QuotaAllocatorFlow 仍会对 admin 操作租户 VM 执行额外的 quota 校验。clone 之所以触发 bug 是因为 dryRun 路径绕过了 API interceptor。

    风险:未来新增 dryRun 路径时,如果忘记设置 sessionAccountUuid,同样的问题会复现。当前修复依赖"每个调用点都记得传 sessionAccountUuid"的隐式约定。

    建议:考虑在 QuotaAllocatorFlow.allocate() 内部直接从操作上下文获取 session 信息(如通过 ThreadContextspec 中已有的其他字段),而非依赖调用方显式传递。如果当前方案作为 hotfix 可接受,建议在代码中添加注释说明设计决策和 TODO。

  2. 🟡 [HostAllocatorSpec.java:165-169] getter/setter 打断现有代码对getSessionAccountUuid() / setSessionAccountUuid() 插入在 getL3NetworkUuids()setL3NetworkUuids() 之间,打断了 L3NetworkUuids 的 getter-setter 配对。建议移到文件末尾或其他 getter-setter 对之间。

  3. 🟡 [AllocateHostMsg.java:215-217] 格式不一致 — 新增的 getter/setter 使用单行格式 { return sessionAccountUuid; },文件中其他所有方法均使用多行格式。建议统一为多行格式。

Suggestion

  1. 🟢 [VmQuotaOperator.java] 方法命名 typocheckVmCupAndMemoryCapacityWithResult 延续了已有的 "Cup" 拼写错误(应为 "Cpu")。新增方法是修正命名的好时机。

  2. 🟢 [VmQuotaOperator.java] wrapper 方法丢失 @Transactional 注解 — 重构后的 wrapper checkVmInstanceQuota()checkVmCupAndMemoryCapacity() 不再有 @Transactional(readOnly = true) 注解(原方法有)。虽然 @Configurable + AspectJ weaving 下内部方法调用的 @Transactional 仍生效,但建议在 wrapper 上也保留注解以明确事务语义。

  3. 🟢 [QuotaAllocatorFlow.java:56] 注释改进// skip checkquota if the operator is admin 中 "checkquota" 建议改为 "quota check" 以提升可读性。

Verdict: ✅ APPROVED

修复正确解决了 Jira 中描述的两个问题:admin clone 租户 VM 的 quota 绕过 + 错误信息保留。WithResult 模式的重构向后兼容,OperationFailureException 替代 ApiMessageInterceptionException 在 allocator flow 中是正确选择。遗忘路径风险作为 hotfix 可接受,建议后续优化集中 admin bypass 逻辑。


🤖 Robot Reviewer

1.Clone a tenant VM using admin account, quota check
didn't base on current role.
2.quota error is overwritten during exception handling.
http://jira.zstack.io/browse/ZSTAC-83646

Resolves: ZSTAC-83646

Change-Id: I776c6b7a786d79746f616b716f736f646e686868
@MatheMatrix MatheMatrix force-pushed the sync/tian.huang/huangtian-ZSTAC-83646@@2 branch from c0bceed to ee4b6be Compare March 31, 2026 06:45
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.

♻️ Duplicate comments (1)
compute/src/main/java/org/zstack/compute/allocator/QuotaAllocatorFlow.java (1)

56-61: ⚠️ Potential issue | 🟠 Major

管理员绕过条件仍然过窄,而且对漏传账号没有兜底。

这里仍然只看 AccountConstant.isAdminPermission(...),它只覆盖 INITIAL_SYSTEM_ADMIN_UUID,不能覆盖其他 SystemAdmin;同时这段逻辑完全依赖 spec.getAccountUuid() 被上游显式透传,而 HostAllocatorSpec.fromAllocationMsg(...) 只是被动复制该字段,漏传时会静默退化回租户配额校验。建议和 QuotaUtil.checkQuota(...) 保持一致,按管理员账户类型判断,并补一层对操作账号缺失的兜底/校验。还需要顺手核查所有 AllocateHostMsg / DesignatedAllocateHostMsg 构造路径是否都会设置这个字段。

🤖 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/allocator/QuotaAllocatorFlow.java`
around lines 56 - 61, The admin-bypass check in QuotaAllocatorFlow currently
only calls AccountConstant.isAdminPermission(spec.getAccountUuid()) and silently
skips when spec.getAccountUuid() is missing; change it to mirror
QuotaUtil.checkQuota(...) by resolving the operator account type (e.g., check
AccountType/SystemAdmin semantics rather than only INITIAL_SYSTEM_ADMIN_UUID)
and treat null spec.getAccountUuid() as a missing operator—try to derive the
account UUID from the original allocation message pathway (use
HostAllocatorSpec.fromAllocationMsg(...) input or the
AllocateHostMsg/DesignatedAllocateHostMsg constructors) and if still absent fail
fast or log+reject instead of defaulting to tenant checks; update the logic in
QuotaAllocatorFlow (and add unit checks) and verify all AllocateHostMsg /
DesignatedAllocateHostMsg construction paths set the account UUID.
🧹 Nitpick comments (1)
compute/src/main/java/org/zstack/compute/vm/VmQuotaOperator.java (1)

237-270: checkVmCupAndMemoryCapacity* 更正为 Cpu

这两个是这次新增/扩展的公开入口,Cup 是拼写错误;等调用点扩散后再改名,兼容成本会更高。

As per coding guidelines, “命名应尽量用完整的单词组合表达意图,并在名称中体现数据类型或用途”。

🤖 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/VmQuotaOperator.java` around
lines 237 - 270, The two public methods checkVmCupAndMemoryCapacityWithResult
and checkVmCupAndMemoryCapacity contain a spelling mistake ("Cup")—rename them
to checkVmCpuAndMemoryCapacityWithResult and checkVmCpuAndMemoryCapacity
respectively, update all internal references/call sites to the new names, and
adjust any related comments/Javadoc to use "Cpu" consistently; ensure
compilation by refactoring callers in the same module and run tests to catch
missed references.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@compute/src/main/java/org/zstack/compute/allocator/QuotaAllocatorFlow.java`:
- Around line 56-61: The admin-bypass check in QuotaAllocatorFlow currently only
calls AccountConstant.isAdminPermission(spec.getAccountUuid()) and silently
skips when spec.getAccountUuid() is missing; change it to mirror
QuotaUtil.checkQuota(...) by resolving the operator account type (e.g., check
AccountType/SystemAdmin semantics rather than only INITIAL_SYSTEM_ADMIN_UUID)
and treat null spec.getAccountUuid() as a missing operator—try to derive the
account UUID from the original allocation message pathway (use
HostAllocatorSpec.fromAllocationMsg(...) input or the
AllocateHostMsg/DesignatedAllocateHostMsg constructors) and if still absent fail
fast or log+reject instead of defaulting to tenant checks; update the logic in
QuotaAllocatorFlow (and add unit checks) and verify all AllocateHostMsg /
DesignatedAllocateHostMsg construction paths set the account UUID.

---

Nitpick comments:
In `@compute/src/main/java/org/zstack/compute/vm/VmQuotaOperator.java`:
- Around line 237-270: The two public methods
checkVmCupAndMemoryCapacityWithResult and checkVmCupAndMemoryCapacity contain a
spelling mistake ("Cup")—rename them to checkVmCpuAndMemoryCapacityWithResult
and checkVmCpuAndMemoryCapacity respectively, update all internal
references/call sites to the new names, and adjust any related comments/Javadoc
to use "Cpu" consistently; ensure compilation by refactoring callers in the same
module and run tests to catch missed references.

ℹ️ 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: cdbb1e58-8db8-4c58-b154-aba681aa2921

📥 Commits

Reviewing files that changed from the base of the PR and between c0bceed and ee4b6be.

📒 Files selected for processing (5)
  • compute/src/main/java/org/zstack/compute/allocator/QuotaAllocatorFlow.java
  • compute/src/main/java/org/zstack/compute/vm/VmQuotaOperator.java
  • header/src/main/java/org/zstack/header/allocator/AllocateHostMsg.java
  • header/src/main/java/org/zstack/header/allocator/HostAllocatorSpec.java
  • identity/src/main/java/org/zstack/identity/QuotaUtil.java
🚧 Files skipped from review as they are similar to previous changes (2)
  • header/src/main/java/org/zstack/header/allocator/AllocateHostMsg.java
  • header/src/main/java/org/zstack/header/allocator/HostAllocatorSpec.java

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