[zstack] cordon production trigger — U-δ buffer helper + plan + STATUS R3 close#3906
[zstack] cordon production trigger — U-δ buffer helper + plan + STATUS R3 close#3906ZStack-Robot wants to merge 3 commits intofeature-unifi-hostfrom
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
Walkthrough本 PR 全面引入了统一硬件管理架构,包括物理服务器与服务器池主模型、主容量唯一真表、角色生命周期管理与 SPI、API 全补全、全路径分配聚合、主机与 VM 适配器、扩展点、数据库迁移和大规模单元/性能测试、自动化脚本和完整项目文档。 Changes统一硬件模型与容量重构全链路实现
Sequence Diagram(s)sequenceDiagram
participant API
participant Manager as PhysicalServerManagerImpl
participant Updater as PhysicalServerCapacityUpdater
participant DB
API->>Manager: 调用API请求(如物理服务器/角色变更)
Manager->>Updater: recalculate(serverUuid)
Updater->>DB: 聚合悲观锁/写available
Updater-->>Manager: 返回聚合结果
Manager-->>API: 返回API事件
Estimated code review effort🎯 5 (Critical) | ⏱️ ~150+ minutes Poem
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
|
00e0964 to
dec29a7
Compare
There was a problem hiding this comment.
Actionable comments posted: 14
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
compute/src/main/java/org/zstack/compute/allocator/HostCpuOverProvisioningManagerImpl.java (2)
72-97:⚠️ Potential issue | 🟠 Major | ⚡ Quick win全局比率更新只排除了内存态覆盖,重启后会覆写持久化的单机覆盖。
ratios只存在内存里,但单机覆盖已经落到了PhysicalServerCapacityVO.cpuOverprovisioningRatio。服务重启后ratios会变空,这里的 bulk update 会把所有KVM_HOST的totalCpu都改成全局值,连已经持久化的单机覆盖也一起改掉;随后getRatio()又会从 PSC 读回旧覆盖值,结果totalCpu和容量换算使用的 ratio 就会互相打架。这里需要按 PSC 中“是否存在单机覆盖”做排除,而不是只看内存 map。🤖 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 `@compute/src/main/java/org/zstack/compute/allocator/HostCpuOverProvisioningManagerImpl.java` around lines 72 - 97, The bulk updater in updateHostsCpuCapacity currently excludes hosts only by the in-memory ratios map and will overwrite per-server persisted overrides in PhysicalServerCapacityVO.cpuOverprovisioningRatio after a restart; change the SQL in updateHostsCpuCapacity to also exclude any PhysicalServerCapacityVO rows that have a persisted cpuOverprovisioningRatio (e.g. add "and cap.cpuOverprovisioningRatio is null" or equivalent null-check to both the empty- and non-empty-ratios queries), so the update skips servers with a stored per-server override (retain the existing PhysicalServerRoleVO KVM_HOST role filter and the roleUuid exclusion when ratios is non-empty); leave getRatio() and the ratios map logic unchanged.
130-133:⚠️ Potential issue | 🟠 Major | ⚡ Quick win删除单机覆盖时不应把当前全局值固化成新的 PSC 覆盖。
deleteRatio()先从ratios里删掉覆盖,但紧接着又通过updateHostCpuCapacityByUuid(hostUuid, getGlobalRatio())把当前全局值写回了cpuOverprovisioningRatio。这样该 Host 之后会一直命中 PSC 覆盖,未来全局比率变化也不会再跟随。删除覆盖时应该清空/重置 PSC 里的覆盖标记,再按当前全局值重算totalCpu。🤖 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 `@compute/src/main/java/org/zstack/compute/allocator/HostCpuOverProvisioningManagerImpl.java` around lines 130 - 133, deleteRatio currently removes the host-specific entry from ratios but immediately calls updateHostCpuCapacityByUuid(hostUuid, getGlobalRatio()), which writes the current global ratio back into the host's persistent cpuOverprovisioningRatio and effectively re-applies a PSC override; instead, after removing ratios.remove(hostUuid) clear/reset the host's PSC override field (cpuOverprovisioningRatio or the override flag stored in the host/PSC record) so it no longer contains a hard-coded ratio, then call recalculateHostCapacityByUuid(hostUuid) (and only call updateHostCpuCapacityByUuid if you need to persist an explicit null/removed state) so the host will use the global ratio dynamically going forward; update functions to target updateHostCpuCapacityByUuid, ratios, and the PSC field accordingly.
🧹 Nitpick comments (2)
compute/src/main/java/org/zstack/compute/host/HostManagerImpl.java (1)
432-434: ⚡ Quick win为扩展点调用增加失败兜底,避免 add-host 流程在异常时失去可控失败路径
Line [432]-Line [434] 这里直接调用扩展点实现;若某个实现抛出运行时异常,
doAddHost()可能在chain.start()前退出,调用方拿不到统一的ErrorCode失败结果。建议在循环内捕获异常并走completion.fail(...)后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 `@compute/src/main/java/org/zstack/compute/host/HostManagerImpl.java` around lines 432 - 434, The loop calling pluginRgty.getExtensionList(PhysicalServerPathTwoExtensionPoint.class) and invoking ext.contributeAddHostFlows(chain, msg, vo, cluster) can throw runtime exceptions and currently can escape doAddHost() before chain.start(), leaving callers without a unified ErrorCode; wrap each ext.contributeAddHostFlows(...) call in a try/catch, on exception call completion.fail(...) with a SystemError (or appropriate ErrorCode) that includes the exception message/stacktrace, and return immediately to stop further processing so the add-host flow reports a controlled, observable failure instead of crashing.compute/src/main/java/org/zstack/compute/allocator/HostCapacityOverProvisioningManagerImpl.java (1)
78-85: 💤 Low value建议使用参数化查询替代 String.format 拼接 SQL。
当前代码将
ratio直接嵌入 SQL 字符串:String sql = String.format( "update PhysicalServerCapacityVO cap" + " set cap.memoryOverprovisioningRatio = %s" + " where cap.uuid = :suuid", ratio);虽然
ratio是double类型不存在注入风险,但为保持一致性和可维护性,建议统一使用参数绑定。♻️ 建议修改
- String sql = String.format( - "update PhysicalServerCapacityVO cap" - + " set cap.memoryOverprovisioningRatio = %s" - + " where cap.uuid = :suuid", - ratio); - Query q = dbf.getEntityManager().createQuery(sql); - q.setParameter("suuid", serverUuid); + String sql = "update PhysicalServerCapacityVO cap" + + " set cap.memoryOverprovisioningRatio = :ratio" + + " where cap.uuid = :suuid"; + Query q = dbf.getEntityManager().createQuery(sql); + q.setParameter("ratio", ratio); + q.setParameter("suuid", serverUuid);🤖 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 `@compute/src/main/java/org/zstack/compute/allocator/HostCapacityOverProvisioningManagerImpl.java` around lines 78 - 85, The update SQL currently injects the double variable via String.format (see HostCapacityOverProvisioningManagerImpl, PhysicalServerCapacityVO.memoryOverprovisioningRatio, variables ratio and serverUuid); change it to a parameterized JPQL update using the EntityManager from dbf (e.g., "update PhysicalServerCapacityVO cap set cap.memoryOverprovisioningRatio = :ratio where cap.uuid = :suuid"), then call q.setParameter("ratio", ratio) and q.setParameter("suuid", serverUuid) before q.executeUpdate() to avoid string concatenation and keep parameter binding consistent.
🤖 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
`@compute/src/main/java/org/zstack/compute/allocator/HostAllocatorManagerImpl.java`:
- Around line 157-184: 在 handle(RecalculateHostCapacityMsg msg) 中当前对
cluster/zone 分支逐个遍历 HostVO 后直接调用
HostCapacityUpdater.resolveServerUuidOrThrow(...),导致只要遇到一个未映射到
PhysicalServerRoleVO 的 Host 就会抛出并中断整批重算;请改为只对已能解析到 serverUuid 的主机执行
psCapacityUpdater.recalculate(serverUuid):要么在查询阶段直接 select 可映射的 serverUuid(替换对
HostVO_.uuid 的查询)并循环调用 psCapacityUpdater.recalculate(...),要么将对
HostCapacityUpdater.resolveServerUuidOrThrow(...) 的调用改为安全版本(捕获异常或使用返回 null 的
resolveServerUuid 方法)对缺失映射的 Host 做 processLogger.warn(...) 并跳过,而不是 fail-fast,保留对
RecalculateHostCapacityMsg 的最后 bus.reply(msg, new MessageReply()) 行为不变。
In
`@compute/src/main/java/org/zstack/compute/allocator/PhysicalServerCapacityUpdater.java`:
- Line 82: Replace the Chinese inline comment in PhysicalServerCapacityUpdater
(the comment currently reading "铁律 15: lazy getter pattern — never
field-initialize from pluginRgty.") with an English version following the file's
existing style; for example change it to "Rule 15: lazy getter pattern — never
initialize fields directly from pluginRgty." Ensure spelling and punctuation are
correct and keep the comment format consistent with surrounding English
comments.
In
`@compute/src/main/java/org/zstack/compute/cluster/ClusterExtensionPointEmitter.java`:
- Around line 29-35: The afterCreate method in ClusterExtensionPointEmitter
calls extp.afterCreateCluster(cluster) without isolating exceptions, which can
bubble up after the ClusterVO has been persisted and cause inconsistent "created
but reported failed" states; wrap each call to
ClusterCreateExtensionPoint.afterCreateCluster in a try/catch inside the
CollectionUtils.safeForEach loop (iterate createExts), catch Throwable (or
Exception), log a warning/error with the extension identity and full exception
details via the class's logger (include cluster id/name and
extp.getClass().getName()), and continue to the next extension so one failing
extension cannot abort the creation flow (optionally emit a metric or marker for
later compensation/alerting).
In `@conf/db/upgrade/V5.5.18__schema.sql`:
- Around line 46-49: The schema-check uses DATABASE() causing silent mischecks;
replace TABLE_SCHEMA = DATABASE() with TABLE_SCHEMA = 'zstack' in all
occurrences in this file (the SELECT that sets `@has_native` and the two other
analogous SELECTs mentioned) so the guard explicitly targets the 'zstack'
schema; locate the SELECT ... FROM information_schema.TABLES statements (e.g.,
the one assigning `@has_native`) and update their TABLE_SCHEMA predicate to
'zstack'.
- Around line 68-82: Replace all DEFAULT '0000-00-00 00:00:00' createDate
sentinel values in this migration with a non-zero sentinel (e.g., '2000-01-01
00:00:00') for ServerPoolVO and the other seven new tables that currently use
the zero-timestamp; update the CREATE TABLE statements (createDate column
definitions) to use that value so future manual inserts follow the repo
convention. For the deliberate FK removal on
BareMetal2ProvisionNetworkClusterRefVO, add an inline SQL comment documenting
the intentional drop per ADR-013 and either make networkUuid nullable or add an
explicit data-validation/commented reminder to add application-level
checks/cleanup (or a later trigger/constraint) so orphaned networkUuid rows are
handled in Phase 2.
In `@docs/api-flow-qa-testplan.md`:
- Around line 11-487: The markdown file has many fenced code blocks without
language tags (triggering MD040); update every unlabeled ``` block in the
document to include an appropriate language hint (e.g. change the ASCII
topology/diagram blocks like the one starting with "Zone" to ```text, JSON API
payload examples (e.g. the POST /clusters and POST /physical-servers payloads)
to ```json, shell/HTTP examples to ```bash or ```http, and migration/SQL
snippets (e.g. the Flyway/ALTER/CREATE statements) to ```sql; scan for blocks
containing "步骤" sections, API requests (POST/GET/PUT), and the Flyway migration
block and add the matching language tag to each fenced block.
In `@docs/architecture-allocator-2026-03-18.md`:
- Around line 1136-1177: The provider API used here is outdated:
PhysicalServerRoleProvider#getCapacityConsumption must accept (serverUuid,
roleUuid) and recalculateCapacity should call that two-arg method per role and
aggregate capacity by taking the union (not summing) across roles; update the
loop in recalculateCapacity to iterate the server's role UUIDs, call
provider.getCapacityConsumption(serverUuid, roleUuid), compute per-resource max
(cpu/memory/disk) across roles (rather than adding into
totalUsedCpu/Memory/Disk), and update the PhysicalServerRoleProvider interface
and all callers/tests to the new signature.
- Around line 46-85: Add a language tag to each fenced code block that currently
starts with "ServerAllocatorChain Flow 执行顺序(7 个通用 Flow):" (and the other listed
blocks at 419-438, 778-823, 831-843, 1115-1132, 1307-1340) so Markdownlint MD040
stops firing; choose an appropriate language token such as text for plain
descriptions or sql/java where snippets show queries or code, e.g., change ```
to ```text or ```sql/```java as appropriate for each block.
In `@docs/architecture-bm1-adapter-2026-03-18.md`:
- Around line 346-366: The fenced code blocks showing call sequences for
createChassis, deleteChassis and updateChassis are missing language labels and
trigger markdownlint MD040; update each fenced block (including the ones at the
other ranges noted) to include an appropriate language tag such as text or sql
so the linter stops complaining. Locate the blocks that contain createChassis(),
deleteChassis(), updateChassis(), the
pluginRgty.getExtensionList(BaremetalChassisLifecycleExtensionPoint.class)
invocations and dbf.persist/remove/update lines and add a language identifier
immediately after the opening ``` for each fenced block.
- Around line 72-115: The current getCapacityConsumption implementation uses the
old single-String SPI (getCapacityConsumption(String serverUuid)) which
mismatches the current PhysicalServerRoleProvider interface; update the method
to the new SPI signature used by PhysicalServerRoleProvider (e.g.,
getCapacityConsumption(PhysicalServerRoleInventory roleInv) or the exact
interface method on PhysicalServerRoleProvider), then inside the new method use
the provided PhysicalServerRoleInventory (or provided parameters) to obtain
roleUuid/serverUuid, query BaremetalChassisVO via
dbf.findByUuid(roleInv.getRoleUuid(), BaremetalChassisVO.class), check chassis
status against BaremetalChassisStatus.Allocated, and populate CapacityUsage from
PhysicalServerCapacityVO (queried by serverUuid) as before; ensure the method
name and parameter types exactly match the PhysicalServerRoleProvider interface
so the adapter compiles and the provider is properly wired.
In `@docs/architecture-container-adapter-2026-03-18.md`:
- Around line 1138-1140: CD-3 conflicts with body sections 3.1/3.2 about
EXTERNAL_READONLY: update the appendix so "available" semantics match the main
text by stating that EXTERNAL_READONLY capacities are counted toward consumption
(i.e., available should reflect ZStack controllable roles including
EXTERNAL_READONLY), and ensure CD-4's behavior
(decreaseCapacity/increaseCapacity affect available) is preserved; reference
CD-3, CD-4, the available field and EXTERNAL_READONLY in the edit.
- Around line 128-135: The current logic in getCapacityConsumption() uses
Q.New(PodVO.class)...select(PodVO_.cpuNum, PodVO_.memorySize).findTuple() which
returns a single Pod row rather than aggregated totals; replace the select call
to use SUM aggregation (e.g., select(SUM(PodVO_.cpuNum),
SUM(PodVO_.memorySize))) and wrap with coalesce to default null sums to 0 so
usedCpu/usedMemory get total host requests (ensure the returned tuple values are
read as Long and keep the same get(0, Long.class)/get(1, Long.class) usage).
- Around line 637-646: The two aggregation queries that set totalPodCpu and
totalPodMemory currently only filter by hostUuid and therefore include
terminated/non-running pods; update both SQL.New calls (the queries that select
sum(p.cpuNum) and sum(p.memorySize) over PodVO p) to include a state filter
(e.g., "and p.state = :state") and pass a corresponding .param("state",
"Running") (or the enum/constant used elsewhere for Running) so only running
pods are counted.
In `@docs/STATUS.md`:
- Around line 118-130: The table row for "U18-U20 ProvisionProvider SPI"
currently has four Markdown columns causing misalignment; edit the U18-U20 row
so it matches the three-column header (区段 / 范围 / 状态) by merging the extra fourth
cell content ("stage-based + ping monitoring GREEN,phase tracked...
production-validated...") into the single "状态" cell for U18-U20, ensuring the
row contains exactly three pipe-separated columns like the header.
---
Outside diff comments:
In
`@compute/src/main/java/org/zstack/compute/allocator/HostCpuOverProvisioningManagerImpl.java`:
- Around line 72-97: The bulk updater in updateHostsCpuCapacity currently
excludes hosts only by the in-memory ratios map and will overwrite per-server
persisted overrides in PhysicalServerCapacityVO.cpuOverprovisioningRatio after a
restart; change the SQL in updateHostsCpuCapacity to also exclude any
PhysicalServerCapacityVO rows that have a persisted cpuOverprovisioningRatio
(e.g. add "and cap.cpuOverprovisioningRatio is null" or equivalent null-check to
both the empty- and non-empty-ratios queries), so the update skips servers with
a stored per-server override (retain the existing PhysicalServerRoleVO KVM_HOST
role filter and the roleUuid exclusion when ratios is non-empty); leave
getRatio() and the ratios map logic unchanged.
- Around line 130-133: deleteRatio currently removes the host-specific entry
from ratios but immediately calls updateHostCpuCapacityByUuid(hostUuid,
getGlobalRatio()), which writes the current global ratio back into the host's
persistent cpuOverprovisioningRatio and effectively re-applies a PSC override;
instead, after removing ratios.remove(hostUuid) clear/reset the host's PSC
override field (cpuOverprovisioningRatio or the override flag stored in the
host/PSC record) so it no longer contains a hard-coded ratio, then call
recalculateHostCapacityByUuid(hostUuid) (and only call
updateHostCpuCapacityByUuid if you need to persist an explicit null/removed
state) so the host will use the global ratio dynamically going forward; update
functions to target updateHostCpuCapacityByUuid, ratios, and the PSC field
accordingly.
---
Nitpick comments:
In
`@compute/src/main/java/org/zstack/compute/allocator/HostCapacityOverProvisioningManagerImpl.java`:
- Around line 78-85: The update SQL currently injects the double variable via
String.format (see HostCapacityOverProvisioningManagerImpl,
PhysicalServerCapacityVO.memoryOverprovisioningRatio, variables ratio and
serverUuid); change it to a parameterized JPQL update using the EntityManager
from dbf (e.g., "update PhysicalServerCapacityVO cap set
cap.memoryOverprovisioningRatio = :ratio where cap.uuid = :suuid"), then call
q.setParameter("ratio", ratio) and q.setParameter("suuid", serverUuid) before
q.executeUpdate() to avoid string concatenation and keep parameter binding
consistent.
In `@compute/src/main/java/org/zstack/compute/host/HostManagerImpl.java`:
- Around line 432-434: The loop calling
pluginRgty.getExtensionList(PhysicalServerPathTwoExtensionPoint.class) and
invoking ext.contributeAddHostFlows(chain, msg, vo, cluster) can throw runtime
exceptions and currently can escape doAddHost() before chain.start(), leaving
callers without a unified ErrorCode; wrap each ext.contributeAddHostFlows(...)
call in a try/catch, on exception call completion.fail(...) with a SystemError
(or appropriate ErrorCode) that includes the exception message/stacktrace, and
return immediately to stop further processing so the add-host flow reports a
controlled, observable failure instead of crashing.
🪄 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: 5195a7b9-645a-4a3d-9245-ca2a01f67a00
⛔ Files ignored due to path filters (15)
compute/pom.xmlis excluded by!**/*.xmlcompute/src/test/resources/searchConfig/indexConfig.xmlis excluded by!**/*.xmlconf/globalConfig/hostAllocator.xmlis excluded by!**/*.xmlconf/globalConfig/physicalServer.xmlis excluded by!**/*.xmlconf/persistence.xmlis excluded by!**/*.xmlconf/serviceConfig/physicalServer.xmlis excluded by!**/*.xmlconf/serviceConfig/provisionNetwork.xmlis excluded by!**/*.xmlconf/serviceConfig/serverPool.xmlis excluded by!**/*.xmlconf/springConfigXml/HostAllocatorManager.xmlis excluded by!**/*.xmlconf/springConfigXml/Kvm.xmlis excluded by!**/*.xmlconf/springConfigXml/PhysicalServerManager.xmlis excluded by!**/*.xmlconf/zstack.xmlis excluded by!**/*.xmlplugin/kvm/pom.xmlis excluded by!**/*.xmlplugin/kvm/src/test/resources/searchConfig/indexConfig.xmlis excluded by!**/*.xmlplugin/physicalServer/pom.xmlis excluded by!**/*.xml
📒 Files selected for processing (285)
.gitignoreHardwareUnifiedManagement.mdcompute/src/main/java/org/zstack/compute/allocator/HostAllocatorGlobalConfig.javacompute/src/main/java/org/zstack/compute/allocator/HostAllocatorManagerImpl.javacompute/src/main/java/org/zstack/compute/allocator/HostCapacityOverProvisioningManagerImpl.javacompute/src/main/java/org/zstack/compute/allocator/HostCapacityUpdater.javacompute/src/main/java/org/zstack/compute/allocator/HostCpuOverProvisioningManagerImpl.javacompute/src/main/java/org/zstack/compute/allocator/PhysicalServerCapacityBuffers.javacompute/src/main/java/org/zstack/compute/allocator/PhysicalServerCapacityUpdater.javacompute/src/main/java/org/zstack/compute/cluster/ClusterExtensionPointEmitter.javacompute/src/main/java/org/zstack/compute/cluster/ClusterManagerImpl.javacompute/src/main/java/org/zstack/compute/host/HostManagerImpl.javacompute/src/main/java/org/zstack/compute/vm/StaticIpOperator.javacompute/src/main/java/org/zstack/compute/vm/VmCreateOnHypervisorFlow.javacompute/src/main/java/org/zstack/compute/vm/VmInstantiateResourceForChangeImageFlow.javacompute/src/main/java/org/zstack/compute/vm/VmInstantiateResourcePostFlow.javacompute/src/main/java/org/zstack/compute/vm/VmInstantiateResourcePreFlow.javacompute/src/main/java/org/zstack/compute/vm/VmReleaseResourceFlow.javacompute/src/main/java/org/zstack/compute/vm/VmStartOnHypervisorFlow.javacompute/src/main/java/org/zstack/compute/zone/ZoneExtensionPointEmitter.javacompute/src/main/java/org/zstack/compute/zone/ZoneManagerImpl.javacompute/src/test/java/org/zstack/compute/allocator/HostCpuOverProvisioningManagerImplTest.javacompute/src/test/java/org/zstack/compute/allocator/PhysicalServerCapacityUpdaterOrchestrationOverheadTest.javacompute/src/test/java/org/zstack/compute/allocator/PhysicalServerCapacityUpdaterTest.javacompute/src/test/resources/zstack.propertiesconf/db/upgrade/V5.5.18__schema.sqlcore/src/main/java/org/zstack/core/aspect/EncryptColumnAspect.ajcore/src/main/java/org/zstack/core/db/GLock.javadocs/STATUS.mddocs/api-flow-qa-testplan.mddocs/architecture-allocator-2026-03-18.mddocs/architecture-bm1-adapter-2026-03-18.mddocs/architecture-bm2-adapter-2026-03-18.mddocs/architecture-container-adapter-2026-03-18.mddocs/architecture-kvm-adapter-2026-03-18.mddocs/architecture-review-final-2026-03-18.mddocs/architecture-unified-hardware-2026-03-18.mddocs/archive/2026-03-31-milestone-20days.mddocs/archive/2026-03-31-unified-hardware-management.mddocs/archive/2026-04-02-unified-hardware.mddocs/archive/2026-04-08-task1-5-patch.mddocs/archive/hardware-unified-management/ANALYSIS_baremetal2_detailed.mddocs/archive/hardware-unified-management/ANALYSIS_baremetal2_module.mddocs/archive/hardware-unified-management/ANALYSIS_baremetal_module.mddocs/archive/hardware-unified-management/ANALYSIS_compute_allocator.mddocs/archive/hardware-unified-management/ANALYSIS_container_module.mddocs/archive/hardware-unified-management/ANALYSIS_kvm_host_module.mddocs/archive/hardware-unified-management/ARCHITECT_DECISION.mddocs/archive/hardware-unified-management/CH1_Architecture_Overview.mddocs/archive/hardware-unified-management/CH2_Physical_Layer.mddocs/archive/hardware-unified-management/CH3_Role_and_Logic_Layer.mddocs/archive/hardware-unified-management/CH4_Allocation_and_Capacity.mddocs/archive/hardware-unified-management/CH5_Monitoring_and_Lifecycle.mddocs/archive/hardware-unified-management/CH6_API_and_Migration.mddocs/archive/hardware-unified-management/PHASE1_Detailed_Design.mddocs/archive/hardware-unified-management/PLAN_header.mddocs/archive/hardware-unified-management/REVIEW_baremetal2.mddocs/archive/hardware-unified-management/REVIEW_baremetal2_round2.mddocs/archive/hardware-unified-management/REVIEW_baremetal_v1.mddocs/archive/hardware-unified-management/REVIEW_compute_allocator.mddocs/archive/hardware-unified-management/REVIEW_compute_allocator_R2.mddocs/archive/hardware-unified-management/REVIEW_container.mddocs/archive/hardware-unified-management/REVIEW_container_round2.mddocs/archive/hardware-unified-management/REVIEW_kvm_host_expert.mddocs/archive/hardware-unified-management/REVIEW_kvm_host_expert_round2.mddocs/archive/hardware-unified-management/REVIEW_summary.mddocs/archive/hardware-unified-management/REVIEW_summary_round2.mddocs/archive/hardware-unified-management/REVISED_Architecture_Assessment.mddocs/archive/hardware-unified-management/analysis_before/ANALYSIS_baremetal.mddocs/archive/hardware-unified-management/analysis_before/ANALYSIS_baremetal2.mddocs/archive/hardware-unified-management/analysis_before/ANALYSIS_baremetal2_detailed.mddocs/archive/hardware-unified-management/analysis_before/ANALYSIS_baremetal_detailed.mddocs/archive/hardware-unified-management/analysis_before/ANALYSIS_container.mddocs/archive/hardware-unified-management/analysis_before/ANALYSIS_container_detailed.mddocs/archive/hardware-unified-management/analysis_before/ANALYSIS_kvm.mddocs/archive/hardware-unified-management/analysis_before/ANALYSIS_kvm_detailed.mddocs/archive/hardware-unified-management/analysis_before/PLAN_header.mddocs/archive/prd-unified-hardware-2026-03-18.bak.mddocs/archive/product-brief-unified-hardware-2026-03-06.bak.mddocs/audits/2026-04-27-phase2-prd-audit.mddocs/blockers/2026-04-27-wave1-failed-cases.mddocs/blockers/2026-04-29-p11-forked-vm-crash.mddocs/brainstorms/2026-04-15-phase2-full-requirements.mddocs/brainstorms/2026-04-16-provision-provider-spi-design.mddocs/brainstorms/2026-04-16-role-provider-spi-design.mddocs/brainstorms/2026-04-26-001-phase-2d-autonomous-run-report.mddocs/brainstorms/_template-next-session.mddocs/brainstorms/next-session.mddocs/decisions/ADR-001-hostcapacity-updater-static-resolve.mddocs/decisions/ADR-002-hostcapacity-updater-uuid-semantics.mddocs/decisions/ADR-003-hami-3field-flush.mddocs/decisions/ADR-004-psc-no-fk-vcenter.mddocs/decisions/ADR-005-hcv-view-algorithm-merge.mddocs/decisions/ADR-006-pspn-inplace-rename.mddocs/decisions/ADR-007-no-backup-tables.mddocs/decisions/ADR-008-fk-rename-follows-parent.mddocs/decisions/ADR-009-vcenter-option-c.mddocs/decisions/ADR-010-bm1-out-of-scope.mddocs/decisions/ADR-011-md5-salt-uuid-derivation.mddocs/decisions/ADR-012-roleprovider-pre-generated-role-uuid.mddocs/decisions/ADR-013-bm2-clusterref-table-not-view.mddocs/decisions/ADR-014-incremental-rebuild-antipattern.mddocs/decisions/README.mddocs/design-verdict-2026-04-14.mddocs/plans/2026-04-22-001-feat-v5518-unified-hardware-phase2-plan.mddocs/plans/2026-04-23-001-u28-flyway-data-migration.mddocs/plans/2026-04-27-001-feat-v5518-phase2-prd-audit-plan.mddocs/plans/2026-04-27-002-feat-phase3-wave1-exec-plan.mddocs/plans/2026-04-28-001-fix-phase2-prd-gaps-plan.mddocs/plans/2026-04-29-001-phase3-review-followups.mddocs/plans/2026-05-01-physical-server-first-provision-plan.mddocs/plans/2026-05-02-bm2-gateway-data-plane-wiring-plan.mddocs/plans/2026-05-03-os-install-completion-monitoring-plan.mddocs/plans/2026-05-08-001-psc-writer-collapse-plan.mddocs/plans/2026-05-09-001-cordon-production-trigger-plan.mddocs/prd-unified-hardware-2026-03-18.mddocs/product-brief-unified-hardware-2026-03-06.mddocs/qa/v5.5.18-qa-handoff.mddocs/review-allocator-deep-2026-03-19.mddocs/review-bm1-deep-2026-03-19.mddocs/review-bm2-deep-2026-03-19.mddocs/review-container-deep-2026-03-19.mddocs/review-kvm-deep-2026-03-19.mddocs/runbooks/aspectj-method-local-class-pitfall.mddocs/runbooks/physical-server-pxe-real-env-validation.mddocs/runbooks/testing-envs.mddocs/runbooks/v5518-recalculate-perf.mddocs/runbooks/v5518-sql-ddl-pitfalls.mddocs/runbooks/v5518-unified-hardware-rollback.mddocs/superpowers/plans/2026-04-07-unified-hardware-full.mdheader/src/main/java/org/zstack/header/allocator/HostCapacityStruct.javaheader/src/main/java/org/zstack/header/allocator/HostCapacityVO.javaheader/src/main/java/org/zstack/header/allocator/ReportHostCapacityExtensionPoint.javaheader/src/main/java/org/zstack/header/allocator/ServerReservedCapacityExtensionPoint.javaheader/src/main/java/org/zstack/header/cluster/ClusterAO.javaheader/src/main/java/org/zstack/header/cluster/ClusterAO_.javaheader/src/main/java/org/zstack/header/cluster/ClusterCreateExtensionPoint.javaheader/src/main/java/org/zstack/header/cluster/ClusterInventory.javaheader/src/main/java/org/zstack/header/host/AddHostMessage.javaheader/src/main/java/org/zstack/header/server/APIAttachPhysicalServerRoleEvent.javaheader/src/main/java/org/zstack/header/server/APIAttachPhysicalServerRoleMsg.javaheader/src/main/java/org/zstack/header/server/APIAttachProvisionNetworkToClusterEvent.javaheader/src/main/java/org/zstack/header/server/APIAttachProvisionNetworkToClusterMsg.javaheader/src/main/java/org/zstack/header/server/APIAttachProvisionNetworkToPoolEvent.javaheader/src/main/java/org/zstack/header/server/APIAttachProvisionNetworkToPoolMsg.javaheader/src/main/java/org/zstack/header/server/APIChangeClusterServerPoolEvent.javaheader/src/main/java/org/zstack/header/server/APIChangeClusterServerPoolMsg.javaheader/src/main/java/org/zstack/header/server/APIChangePhysicalServerStateEvent.javaheader/src/main/java/org/zstack/header/server/APIChangePhysicalServerStateMsg.javaheader/src/main/java/org/zstack/header/server/APICreatePhysicalServerEvent.javaheader/src/main/java/org/zstack/header/server/APICreatePhysicalServerMsg.javaheader/src/main/java/org/zstack/header/server/APICreateProvisionNetworkEvent.javaheader/src/main/java/org/zstack/header/server/APICreateProvisionNetworkMsg.javaheader/src/main/java/org/zstack/header/server/APICreateServerPoolEvent.javaheader/src/main/java/org/zstack/header/server/APICreateServerPoolMsg.javaheader/src/main/java/org/zstack/header/server/APIDeletePhysicalServerEvent.javaheader/src/main/java/org/zstack/header/server/APIDeletePhysicalServerMsg.javaheader/src/main/java/org/zstack/header/server/APIDeleteProvisionNetworkEvent.javaheader/src/main/java/org/zstack/header/server/APIDeleteProvisionNetworkMsg.javaheader/src/main/java/org/zstack/header/server/APIDeleteServerPoolEvent.javaheader/src/main/java/org/zstack/header/server/APIDeleteServerPoolMsg.javaheader/src/main/java/org/zstack/header/server/APIDetachPhysicalServerRoleEvent.javaheader/src/main/java/org/zstack/header/server/APIDetachPhysicalServerRoleMsg.javaheader/src/main/java/org/zstack/header/server/APIDetachProvisionNetworkFromClusterEvent.javaheader/src/main/java/org/zstack/header/server/APIDetachProvisionNetworkFromClusterMsg.javaheader/src/main/java/org/zstack/header/server/APIDetachProvisionNetworkFromPoolEvent.javaheader/src/main/java/org/zstack/header/server/APIDetachProvisionNetworkFromPoolMsg.javaheader/src/main/java/org/zstack/header/server/APIDiscoverPhysicalServerHardwareEvent.javaheader/src/main/java/org/zstack/header/server/APIDiscoverPhysicalServerHardwareMsg.javaheader/src/main/java/org/zstack/header/server/APIPowerOffPhysicalServerEvent.javaheader/src/main/java/org/zstack/header/server/APIPowerOffPhysicalServerMsg.javaheader/src/main/java/org/zstack/header/server/APIPowerOnPhysicalServerEvent.javaheader/src/main/java/org/zstack/header/server/APIPowerOnPhysicalServerMsg.javaheader/src/main/java/org/zstack/header/server/APIPowerResetPhysicalServerEvent.javaheader/src/main/java/org/zstack/header/server/APIPowerResetPhysicalServerMsg.javaheader/src/main/java/org/zstack/header/server/APIProvisionPhysicalServerEvent.javaheader/src/main/java/org/zstack/header/server/APIProvisionPhysicalServerMsg.javaheader/src/main/java/org/zstack/header/server/APIQueryPhysicalServerMsg.javaheader/src/main/java/org/zstack/header/server/APIQueryPhysicalServerReply.javaheader/src/main/java/org/zstack/header/server/APIQueryPhysicalServerRoleMsg.javaheader/src/main/java/org/zstack/header/server/APIQueryPhysicalServerRoleReply.javaheader/src/main/java/org/zstack/header/server/APIQueryProvisionNetworkMsg.javaheader/src/main/java/org/zstack/header/server/APIQueryProvisionNetworkReply.javaheader/src/main/java/org/zstack/header/server/APIQueryServerPoolMsg.javaheader/src/main/java/org/zstack/header/server/APIQueryServerPoolReply.javaheader/src/main/java/org/zstack/header/server/APIScanPhysicalServersEvent.javaheader/src/main/java/org/zstack/header/server/APIScanPhysicalServersMsg.javaheader/src/main/java/org/zstack/header/server/APIUpdatePhysicalServerEvent.javaheader/src/main/java/org/zstack/header/server/APIUpdatePhysicalServerMsg.javaheader/src/main/java/org/zstack/header/server/APIUpdateProvisionNetworkEvent.javaheader/src/main/java/org/zstack/header/server/APIUpdateProvisionNetworkMsg.javaheader/src/main/java/org/zstack/header/server/APIUpdateServerPoolEvent.javaheader/src/main/java/org/zstack/header/server/APIUpdateServerPoolMsg.javaheader/src/main/java/org/zstack/header/server/CapacityUsage.javaheader/src/main/java/org/zstack/header/server/CreateRoleEntityContext.javaheader/src/main/java/org/zstack/header/server/PhysicalServerAO.javaheader/src/main/java/org/zstack/header/server/PhysicalServerAO_.javaheader/src/main/java/org/zstack/header/server/PhysicalServerCapacityState.javaheader/src/main/java/org/zstack/header/server/PhysicalServerCapacityVO.javaheader/src/main/java/org/zstack/header/server/PhysicalServerCapacityVO_.javaheader/src/main/java/org/zstack/header/server/PhysicalServerConstant.javaheader/src/main/java/org/zstack/header/server/PhysicalServerEnqueueDiscoveryHook.javaheader/src/main/java/org/zstack/header/server/PhysicalServerHardwareDetailVO.javaheader/src/main/java/org/zstack/header/server/PhysicalServerHardwareDetailVO_.javaheader/src/main/java/org/zstack/header/server/PhysicalServerHardwareDiscoveryExtensionPoint.javaheader/src/main/java/org/zstack/header/server/PhysicalServerHardwareInfoVO.javaheader/src/main/java/org/zstack/header/server/PhysicalServerHardwareInfoVO_.javaheader/src/main/java/org/zstack/header/server/PhysicalServerInventory.javaheader/src/main/java/org/zstack/header/server/PhysicalServerManager.javaheader/src/main/java/org/zstack/header/server/PhysicalServerPathTwoExtensionPoint.javaheader/src/main/java/org/zstack/header/server/PhysicalServerPowerStatus.javaheader/src/main/java/org/zstack/header/server/PhysicalServerProvisionDataPlane.javaheader/src/main/java/org/zstack/header/server/PhysicalServerProvisionNetworkClusterRefVO.javaheader/src/main/java/org/zstack/header/server/PhysicalServerProvisionNetworkClusterRefVO_.javaheader/src/main/java/org/zstack/header/server/PhysicalServerProvisionNetworkInventory.javaheader/src/main/java/org/zstack/header/server/PhysicalServerProvisionNetworkPoolRefVO.javaheader/src/main/java/org/zstack/header/server/PhysicalServerProvisionNetworkPoolRefVO_.javaheader/src/main/java/org/zstack/header/server/PhysicalServerProvisionNetworkVO.javaheader/src/main/java/org/zstack/header/server/PhysicalServerProvisionNetworkVO_.javaheader/src/main/java/org/zstack/header/server/PhysicalServerProvisionTarget.javaheader/src/main/java/org/zstack/header/server/PhysicalServerRoleInventory.javaheader/src/main/java/org/zstack/header/server/PhysicalServerRoleProvider.javaheader/src/main/java/org/zstack/header/server/PhysicalServerRoleVO.javaheader/src/main/java/org/zstack/header/server/PhysicalServerRoleVO_.javaheader/src/main/java/org/zstack/header/server/PhysicalServerState.javaheader/src/main/java/org/zstack/header/server/PhysicalServerStateEvent.javaheader/src/main/java/org/zstack/header/server/PhysicalServerVO.javaheader/src/main/java/org/zstack/header/server/ProvisionNetworkState.javaheader/src/main/java/org/zstack/header/server/ProvisionNetworkType.javaheader/src/main/java/org/zstack/header/server/ProvisionPhase.javaheader/src/main/java/org/zstack/header/server/ProvisionProvider.javaheader/src/main/java/org/zstack/header/server/ProvisionRequest.javaheader/src/main/java/org/zstack/header/server/ProvisionResult.javaheader/src/main/java/org/zstack/header/server/RoleMatchContext.javaheader/src/main/java/org/zstack/header/server/RoleWorkloadStatus.javaheader/src/main/java/org/zstack/header/server/SchedulingMode.javaheader/src/main/java/org/zstack/header/server/ServerPoolInventory.javaheader/src/main/java/org/zstack/header/server/ServerPoolState.javaheader/src/main/java/org/zstack/header/server/ServerPoolVO.javaheader/src/main/java/org/zstack/header/server/ServerPoolVO_.javaheader/src/main/java/org/zstack/header/server/ServerRoleType.javaheader/src/main/java/org/zstack/header/server/WorkloadRef.javaheader/src/main/java/org/zstack/header/server/flow/PathTwoFlowDataKey.javaheader/src/main/java/org/zstack/header/zone/ZoneCreateExtensionPoint.javaidentity/src/main/java/org/zstack/identity/QuotaAPIRequestChecker.javaidentity/src/main/java/org/zstack/identity/QuotaUtil.javalongjob/src/main/java/org/zstack/longjob/LongJobManagerImpl.javaplugin/kvm/src/main/java/org/zstack/kvm/APIAddKVMHostMsg.javaplugin/kvm/src/main/java/org/zstack/kvm/AddKVMHostMsg.javaplugin/kvm/src/main/java/org/zstack/kvm/KVMHost.javaplugin/kvm/src/main/java/org/zstack/kvm/KvmPhysicalServerRoleSoftDeleteExtension.javaplugin/kvm/src/main/java/org/zstack/kvm/KvmRoleProvider.javaplugin/kvm/src/main/java/org/zstack/kvm/server/KvmHardwareDiscoveryAdapter.javaplugin/kvm/src/test/java/org/zstack/core/Platform.javaplugin/kvm/src/test/java/org/zstack/kvm/KvmRoleProviderTest.javaplugin/kvm/src/test/resources/zstack.propertiesplugin/physicalServer/src/main/java/org/zstack/server/DefaultServerPoolClusterCreateExtension.javaplugin/physicalServer/src/main/java/org/zstack/server/DefaultServerPoolCreationPolicy.javaplugin/physicalServer/src/main/java/org/zstack/server/DefaultServerPoolFactory.javaplugin/physicalServer/src/main/java/org/zstack/server/DefaultServerPoolZoneCreateExtension.javaplugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerApiInterceptor.javaplugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerAutoAssociator.javaplugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerCapacityCascadeExtension.javaplugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerCascadeExtension.javaplugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerGlobalConfig.javaplugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerHardwareDetailCascadeExtension.javaplugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerHardwareInfoCascadeExtension.javaplugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerIpmiPowerExecutor.javaplugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerManagerImpl.javaplugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerPathTwoContributor.javaplugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerPathTwoOrchestrator.javaplugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerProvisionNetworkCascadeExtension.javaplugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerProvisionService.javaplugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerRoleCascadeExtension.javaplugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerScanner.javaplugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerStandalonePxeProvisionProvider.javaplugin/physicalServer/src/main/java/org/zstack/server/ProvisionPhysicalServerLongJob.javaplugin/physicalServer/src/main/java/org/zstack/server/ServerPoolCascadeExtension.javaplugin/physicalServer/src/main/java/org/zstack/server/flow/AutoAssociateFlow.javaplugin/physicalServer/src/main/java/org/zstack/server/flow/CreatePhysicalServerRoleFlow.javaplugin/physicalServer/src/main/java/org/zstack/server/flow/InitPhysicalServerCapacityFlow.javaplugin/physicalServer/src/main/java/org/zstack/server/hardware/HardwareDiscoveryScheduler.javaplugin/physicalServer/src/main/java/org/zstack/server/hardware/PhysicalServerEnqueueDiscoveryHookImpl.javaplugin/physicalServer/src/main/java/org/zstack/server/hardware/PhysicalServerHardwareService.javaplugin/physicalServer/src/main/java/org/zstack/server/hardware/UnifiedHardwareInfo.java
| private void handle(RecalculateHostCapacityMsg msg) { | ||
| final List<String> hostUuids = new ArrayList<>(); | ||
| // U-B: delegate to PhysicalServerCapacityUpdater.recalculate — the single Layer 2 writer. | ||
| // All upstreams (HostBase connect flow / ratio change / KvmHostReserveExtension) remain | ||
| // unchanged; only this handler body is replaced (backward-compat: msg fields, reply fields, | ||
| // and service id are all unchanged). | ||
| if (msg.getHostUuid() != null) { | ||
| hostUuids.add(msg.getHostUuid()); | ||
| String serverUuid = HostCapacityUpdater.resolveServerUuidOrThrow(msg.getHostUuid()); | ||
| psCapacityUpdater.recalculate(serverUuid); | ||
| } else if (msg.getClusterUuid() != null) { | ||
| hostUuids.addAll(Q.New(HostVO.class).select(HostVO_.uuid) | ||
| List<String> hostUuids = Q.New(HostVO.class).select(HostVO_.uuid) | ||
| .eq(HostVO_.clusterUuid, msg.getClusterUuid()) | ||
| .listValues()); | ||
| .listValues(); | ||
| for (String huuid : hostUuids) { | ||
| String serverUuid = HostCapacityUpdater.resolveServerUuidOrThrow(huuid); | ||
| psCapacityUpdater.recalculate(serverUuid); | ||
| } | ||
| } else { | ||
| SimpleQuery<HostVO> q = dbf.createQuery(HostVO.class); | ||
| q.select(HostVO_.uuid); | ||
| q.add(HostVO_.zoneUuid, Op.EQ, msg.getZoneUuid()); | ||
| hostUuids.addAll(q.listValue()); | ||
| } | ||
|
|
||
| if (hostUuids.isEmpty()) { | ||
| return; | ||
| } | ||
|
|
||
| class HostUsedCpuMem { | ||
| String hostUuid; | ||
| Long usedMemory; | ||
| Long usedCpu; | ||
| } | ||
|
|
||
| List<HostUsedCpuMem> hostUsedCpuMemList = new Callable<List<HostUsedCpuMem>>() { | ||
| @Override | ||
| @Transactional(readOnly = true) | ||
| public List<HostUsedCpuMem> call() { | ||
| String sql = "select sum(vm.memorySize), vm.hostUuid, sum(vm.cpuNum)" + | ||
| " from VmInstanceVO vm" + | ||
| " where vm.hostUuid in (:hostUuids)" + | ||
| " and vm.state not in (:vmStates)"; | ||
|
|
||
| if (!unsupportedVmTypeForCapacityCalculation.isEmpty()) { | ||
| sql += " and vm.type not in (:vmTypes)"; | ||
| } | ||
|
|
||
| sql += " group by vm.hostUuid"; | ||
| TypedQuery<Tuple> q = dbf.getEntityManager().createQuery(sql, Tuple.class); | ||
| q.setParameter("hostUuids", hostUuids); | ||
| q.setParameter("vmStates", list( | ||
| VmInstanceState.Destroyed, | ||
| VmInstanceState.Created, | ||
| VmInstanceState.Destroying, | ||
| VmInstanceState.Stopped)); | ||
|
|
||
| if (!unsupportedVmTypeForCapacityCalculation.isEmpty()) { | ||
| q.setParameter("vmTypes", unsupportedVmTypeForCapacityCalculation); | ||
| } | ||
|
|
||
| List<Tuple> ts = q.getResultList(); | ||
|
|
||
| List<HostUsedCpuMem> ret = new ArrayList<>(); | ||
| for (Tuple t : ts) { | ||
| HostUsedCpuMem s = new HostUsedCpuMem(); | ||
| s.hostUuid = t.get(1, String.class); | ||
|
|
||
| if (t.get(0, Long.class) == null) { | ||
| continue; | ||
| } | ||
|
|
||
| s.usedMemory = ratioMgr.calculateMemoryByRatio(s.hostUuid, t.get(0, Long.class)); | ||
| long usedMemBySysCom = 0L; | ||
| final List<SysComponentMemUsageExtensionPoint> extps = | ||
| pluginRgty.getExtensionList(SysComponentMemUsageExtensionPoint.class); | ||
| for (SysComponentMemUsageExtensionPoint extp : extps) { | ||
| long hugePageMemUsage = Math.max(0L, extp.getHugePageMemoryUsage(s.hostUuid)); | ||
| long normalMemUsage = Math.max(0L, extp.getNormalMemoryUsage(s.hostUuid)); | ||
| usedMemBySysCom += hugePageMemUsage + normalMemUsage; | ||
| } | ||
| s.usedMemory = usedMemBySysCom + ratioMgr.calculateMemoryByRatio(s.hostUuid, t.get(0, Long.class)); | ||
| s.usedCpu = t.get(2, Long.class); | ||
| ret.add(s); | ||
| } | ||
| return ret; | ||
| } | ||
| }.call(); | ||
|
|
||
| List<String> hostHasVms = CollectionUtils.transformToList(hostUsedCpuMemList, new Function<String, HostUsedCpuMem>() { | ||
| @Override | ||
| public String call(HostUsedCpuMem arg) { | ||
| return arg.hostUuid; | ||
| List<String> hostUuids = q.listValue(); | ||
| for (String huuid : hostUuids) { | ||
| String serverUuid = HostCapacityUpdater.resolveServerUuidOrThrow(huuid); | ||
| psCapacityUpdater.recalculate(serverUuid); | ||
| } | ||
| }); | ||
|
|
||
| hostUuids.stream().filter(huuid -> !hostHasVms.contains(huuid)).forEach(huuid -> { | ||
| HostUsedCpuMem s = new HostUsedCpuMem(); | ||
| s.hostUuid = huuid; | ||
| hostUsedCpuMemList.add(s); | ||
| }); | ||
|
|
||
| for (final HostUsedCpuMem s : hostUsedCpuMemList) { | ||
| new HostCapacityUpdater(s.hostUuid).run(new HostCapacityUpdaterRunnable() { | ||
| @Override | ||
| public HostCapacityVO call(HostCapacityVO cap) { | ||
| long before = cap.getAvailableMemory(); | ||
| long avail = s.usedMemory == null ? cap.getTotalMemory() : cap.getTotalMemory() - s.usedMemory; | ||
| cap.setAvailableMemory(avail); | ||
|
|
||
| long totalCpu = cpuRatioMgr.calculateHostCpuByRatio(s.hostUuid, cap.getCpuNum()); | ||
| long totalCpuBefore = cap.getTotalCpu(); | ||
| cap.setTotalCpu(totalCpu); | ||
|
|
||
| long beforeCpu = cap.getAvailableCpu(); | ||
| long availCpu = s.usedCpu == null ? cap.getTotalCpu() : cap.getTotalCpu() - s.usedCpu; | ||
| cap.setAvailableCpu(availCpu); | ||
|
|
||
| logger.debug(String.format("re-calculated available capacity on the host[uuid:%s]:" + | ||
| "\n[available memory] before: %s, now: %s" + | ||
| "\n[total cpu] before: %s, now: %s" + | ||
| "\n[available cpu] before: %s, now :%s", | ||
| s.hostUuid, | ||
| before, avail, | ||
| totalCpuBefore, totalCpu, | ||
| beforeCpu, availCpu)); | ||
| return cap; | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| bus.reply(msg, new MessageReply()); |
There was a problem hiding this comment.
批量重算会被首个未映射 Host 整体打断。
这里在 cluster/zone 分支先枚举所有 HostVO,再对每个 huuid 调 resolveServerUuidOrThrow()。只要其中一个 Host 还没有 PhysicalServerRoleVO 映射,整个 RecalculateHostCapacityMsg 就会直接抛出,后面的已映射 Host 也不会重算。建议这里直接查询可映射的 serverUuid,或者对缺失映射的 Host 做 warn+skip,而不是 fail-fast。
🤖 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
`@compute/src/main/java/org/zstack/compute/allocator/HostAllocatorManagerImpl.java`
around lines 157 - 184, 在 handle(RecalculateHostCapacityMsg msg) 中当前对
cluster/zone 分支逐个遍历 HostVO 后直接调用
HostCapacityUpdater.resolveServerUuidOrThrow(...),导致只要遇到一个未映射到
PhysicalServerRoleVO 的 Host 就会抛出并中断整批重算;请改为只对已能解析到 serverUuid 的主机执行
psCapacityUpdater.recalculate(serverUuid):要么在查询阶段直接 select 可映射的 serverUuid(替换对
HostVO_.uuid 的查询)并循环调用 psCapacityUpdater.recalculate(...),要么将对
HostCapacityUpdater.resolveServerUuidOrThrow(...) 的调用改为安全版本(捕获异常或使用返回 null 的
resolveServerUuid 方法)对缺失映射的 Host 做 processLogger.warn(...) 并跳过,而不是 fail-fast,保留对
RecalculateHostCapacityMsg 的最后 bus.reply(msg, new MessageReply()) 行为不变。
There was a problem hiding this comment.
Comment from jin.ma:
Skip — fail-fast is intentional. On feature-unifi-host every HostVO must have a corresponding PhysicalServerRoleVO mapping (created atomically by the role-provider chain on host add). An unmapped Host means data-integrity drift, not a transitional state — silently skipping it would mask the bug. Per CLAUDE.md NB-30 single-lock invariant, recalculate must abort and surface the unmapped Host so ops can investigate.
There was a problem hiding this comment.
@zstack-robot-2 Got it. Acknowledged — I’m withdrawing the skip-on-error suggestion. Keeping handle(RecalculateHostCapacityMsg) fail-fast on unmapped Host is intentional on feature-unifi-host to surface integrity drift per NB‑30; no change requested.
If helpful, I can add a brief Javadoc/comment on this handler (and/or ensure the thrown error includes the offending hostUuid and original scope) to make the invariant explicit for future readers. Otherwise, I’ll consider this resolved.
✏️ Learnings added
Learnt from: zstack-robot-2
Repo: MatheMatrix/zstack PR: 3906
File: compute/src/main/java/org/zstack/compute/allocator/HostAllocatorManagerImpl.java:157-184
Timestamp: 2026-05-09T02:01:57.419Z
Learning: On branch feature-unifi-host, HostAllocatorManagerImpl.handle(RecalculateHostCapacityMsg) is intentionally fail-fast: HostCapacityUpdater.resolveServerUuidOrThrow(hostUuid) must throw when a HostVO lacks a PhysicalServerRoleVO mapping. An unmapped Host indicates data-integrity drift (not a transitional state), per NB-30 single-lock invariant. Do not change this path to warn-and-skip during batch recalculation.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
🧠 Learnings used
Learnt from: ZStack-Robot
Repo: MatheMatrix/zstack PR: 3826
File: compute/src/main/java/org/zstack/compute/allocator/HostAllocatorChain.java:261-265
Timestamp: 2026-04-22T04:38:00.864Z
Learning: 在 ZStack 的 `Platform.err()` / `Platform.toRawString()` 调用中,传入用于格式化的 `%s` 参数若实现了 `org.zstack.header.core.I18nMessage`,框架会自动通过 `getDetails()` 取得原始文本、并通过 `getI18nDetails()` 取得国际化文本。因此,在代码审查中不应将“直接把实现了 `I18nMessage` 的对象作为 `%s` 参数传入而可能打印出对象地址/不安全”当作问题;此用法是正确且应当被视为安全的(例如将 `HostCandidate.RejectedCandidate` 这类实现了 `I18nMessage` 的对象作为 `%s` 参数传入)。
Learnt from: ZStack-Robot
Repo: MatheMatrix/zstack PR: 3837
File: compute/src/main/java/org/zstack/compute/vm/VmInstanceBase.java:8789-8791
Timestamp: 2026-04-22T14:27:42.894Z
Learning: In ZStack compute code, calls to VmInstanceResourceMetadataManager.deleteVmResourceMetadata(...) should generally be treated as best-effort: do not fail/block the primary VM operation when deletion fails (for example, during detach/swap flows such as VmDetachNicFlow, VolumeManagerImpl, KVMHostFactory, and VmInstanceBase when swapping the default CD-ROM). Prefer logging the returned ErrorCode (and relevant context) rather than enforcing strict failure, unless maintainers explicitly request strict enforcement for that specific flow.
| Tuple podUsage = Q.New(PodVO.class) | ||
| .eq(PodVO_.hostUuid, hostUuid) | ||
| .select(PodVO_.cpuNum, PodVO_.memorySize) | ||
| .findTuple(); | ||
|
|
||
| long usedCpu = podUsage != null ? podUsage.get(0, Long.class) : 0; | ||
| long usedMemory = podUsage != null ? podUsage.get(1, Long.class) : 0; | ||
|
|
There was a problem hiding this comment.
getCapacityConsumption() 示例没有做聚合,实际只会拿到单条 Pod 数据
这里 select(PodVO_.cpuNum, PodVO_.memorySize).findTuple() 不是求和语义,会导致容量消耗被低估。应使用 SUM(...) 聚合(并配合 coalesce)来计算该 Host 全量 Pod request。
🤖 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 `@docs/architecture-container-adapter-2026-03-18.md` around lines 128 - 135,
The current logic in getCapacityConsumption() uses
Q.New(PodVO.class)...select(PodVO_.cpuNum, PodVO_.memorySize).findTuple() which
returns a single Pod row rather than aggregated totals; replace the select call
to use SUM aggregation (e.g., select(SUM(PodVO_.cpuNum),
SUM(PodVO_.memorySize))) and wrap with coalesce to default null sums to 0 so
usedCpu/usedMemory get total host requests (ensure the returned tuple values are
read as Long and keep the same get(0, Long.class)/get(1, Long.class) usage).
| Long totalPodCpu = SQL.New( | ||
| "select coalesce(sum(p.cpuNum), 0) from PodVO p where p.hostUuid = :hostUuid", | ||
| Long.class) | ||
| .param("hostUuid", host.getUuid()) | ||
| .find(); | ||
| Long totalPodMemory = SQL.New( | ||
| "select coalesce(sum(p.memorySize), 0) from PodVO p where p.hostUuid = :hostUuid", | ||
| Long.class) | ||
| .param("hostUuid", host.getUuid()) | ||
| .find(); |
There was a problem hiding this comment.
Pod 汇总 SQL 缺少状态过滤,可能把已结束 Pod 也计入消耗
sum(p.cpuNum)/sum(p.memorySize) 当前只按 hostUuid 过滤,未限制运行态,会放大消耗并误触发容量不足/cordon。建议至少限定运行中状态(与其他文档里 “state=Running” 口径一致)。
🤖 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 `@docs/architecture-container-adapter-2026-03-18.md` around lines 637 - 646,
The two aggregation queries that set totalPodCpu and totalPodMemory currently
only filter by hostUuid and therefore include terminated/non-running pods;
update both SQL.New calls (the queries that select sum(p.cpuNum) and
sum(p.memorySize) over PodVO p) to include a state filter (e.g., "and p.state =
:state") and pass a corresponding .param("state", "Running") (or the
enum/constant used elsewhere for Running) so only running pods are counted.
| | 区段 | 范围 | 状态 | | ||
| |---|---|---| | ||
| | **U1-U7** capacity ledger + W1-W9 + @Immutable | U1 PSC entity / U2 Hardware skeleton + Scheduler + GlobalConfig / U3 PoolRef + Attach/Detach API / U4 W1-W3 / U5 W4-W6 / U6 W9 vcenter / U7 @Immutable | 全 ✅ | | ||
| | **U8-U10** RoleProvider wire-up | U8 KVM / U9 BM2 / U10 Container | ✅ path 1+2 全通;U10 Container 容量管道 Layer 1 (`syncNodesFromCluster` 写 PSC.total*) + Layer 2 (`PhysicalServerCapacityUpdater.recalculate` 派生 available*) production-validated 2026-05-09 | | ||
| | **U11-U13** FlowChain tail | U11 KVM / U12 BM2 / U13 Container per-node @Transactional | ❌ 全部未起步 — Phase 3 Wave 1 U1 | | ||
| | **U14-U17** Hardware discovery | U14 K8s NodeInventory 字段 / U15 3 private discover / U16 Scheduler retry / U17 handler | U16 ✅;U14/U15/U17 ❌ | | ||
| | **U18-U20** ProvisionProvider SPI | U18 SPI / U19 PhysicalServer-first PXE provider / U20 LongJob | ✅ DONE | stage-based + ping monitoring GREEN,phase tracked in LongJobVO.jobData (no schema change);2026-05-05 production-validated on 172.26.201.160 (CreatePhysicalServer + AttachPhysicalServerRole(KVM_HOST) → RoleVO + HostVO/KVMHostVO + HostCapacityVO + PhysicalServerCapacityVO 全建) | | ||
| | **U21-U22** Container Cordon | U21 ContainerNodeCordonService / U22 recalculate Cordon 集成 | ⚠️ U21 GREEN;capacity reserved extension exists,broader mixed-deployment still separate | | ||
| | **U23-U24** BM2 ProvisionNetwork pool-only 重写 | U23 BM2 manager redirect / U24 cascade removal | N/A — ADR-013 反向,推 v1.1+ | | ||
| | **U25-U26** SDK + DSL 清理 | U25 testlib DSL / U26 删 deprecated VO + 4 SDK Action | ⚠️ 待审;apihelper changeClusterServerPool blocker (next-session §3 #2) 同源 | | ||
| | **U27-U29** Schema + rollback | U27 V5.5.18.1 schema / U28 V5.5.18.2 data migration / U29 rollback runbook | U27 ✅(已合并到 V5.5.18__schema.sql)/ U28 ⚠️(data migration 部分 ✅,BM2 VIEW-ization 撤回 per ADR-013,仍按 ADR-013 落地)/ U29 ✅ | | ||
| | **U30-U31** admin-only + power stubs | U30 24 API admin-only / U31 power operr stubs | U30 ✅;U31 ✅:OOB-first power handler + no-OOB operr | | ||
|
|
There was a problem hiding this comment.
修复 U18-U20 行的表格列数不一致
这段表头是 3 列(区段/范围/状态),但 U18-U20 这一行实际写成了 4 列,Markdown 渲染会错位。建议把“stage-based + ping monitoring GREEN …”合并回“状态”列。
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 124-124: Table column count
Expected: 3; Actual: 4; Too many cells, extra data will be missing
(MD056, table-column-count)
🤖 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 `@docs/STATUS.md` around lines 118 - 130, The table row for "U18-U20
ProvisionProvider SPI" currently has four Markdown columns causing misalignment;
edit the U18-U20 row so it matches the three-column header (区段 / 范围 / 状态) by
merging the extra fourth cell content ("stage-based + ping monitoring
GREEN,phase tracked... production-validated...") into the single "状态" cell for
U18-U20, ensuring the row contains exactly three pipe-separated columns like the
header.
dec29a7 to
4a47543
Compare
There was a problem hiding this comment.
Actionable comments posted: 11
🧹 Nitpick comments (2)
compute/src/main/java/org/zstack/compute/zone/ZoneManagerImpl.java (1)
54-55: ⚡ Quick win建议避免缩写字段名,保持命名可读性一致。
Line [55] 的
extpEmitter属于不必要缩写,建议改为zoneExtensionPointEmitter,并同步 Line [163] 调用,降低维护歧义。As per coding guidelines, “不允许使用不必要的缩写,应使用完整单词提升可读性”。
🤖 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 `@compute/src/main/java/org/zstack/compute/zone/ZoneManagerImpl.java` around lines 54 - 55, Rename the field extpEmitter to zoneExtensionPointEmitter in ZoneManagerImpl and update all references (e.g., usages where extpEmitter is called, including the call around the previously noted usage at line 163) to the new name to eliminate the unnecessary abbreviation; update the `@Autowired` declaration and any method/constructor references inside ZoneManagerImpl to use zoneExtensionPointEmitter and run a compile to ensure all references are updated.docs/archive/hardware-unified-management/ANALYSIS_compute_allocator.md (1)
26-26: ⚡ Quick win建议为代码围栏添加语言标识符
根据 Markdown 最佳实践,建议为所有代码围栏块指定语言标识符。这里的 ASCII 图表可以标记为
text或plaintext,以提高 Markdown 规范合规性并改善编辑器的显示效果。✨ 建议的改进
Line 26 (分配流程图):
-``` +```text +-------------------+ | AllocateHostMsg | (from VM creation, migration, etc.)Line 463 (抽象架构图):
-``` +```text +------------------------+ | ComputeCapacityVO | (New abstract interface or base class)Also applies to: 463-463
🤖 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 `@docs/archive/hardware-unified-management/ANALYSIS_compute_allocator.md` at line 26, The Markdown code fences containing ASCII diagrams (e.g., the AllocateHostMsg allocation flow diagram and the ComputeCapacityVO abstract architecture diagram) lack a language identifier; update those code fences to include a language tag such as "text" or "plaintext" (for example change ``` to ```text) so editors and linters treat the blocks correctly and rendering/syntax highlighting improves for the diagrams containing AllocateHostMsg and ComputeCapacityVO.
🤖 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
`@compute/src/main/java/org/zstack/compute/allocator/HostAllocatorManagerImpl.java`:
- Around line 203-234: PhysicalServerCapacityVO's totalPhysicalCpu is never set
or compared; update both the creation path (where vo is new) and the update path
(inside the needUpdateCapacity branch) to assign
vo.setTotalPhysicalCpu(msg.getTotalPhysicalCpu()) (or the appropriate source
field) and add a comparison for getTotalPhysicalCpu() in needUpdateCapacity so
changes to the host's physical CPU count trigger updates; target the
PhysicalServerCapacityVO assignments and the needUpdateCapacity(...) method for
the changes.
In `@compute/src/main/java/org/zstack/compute/zone/ZoneManagerImpl.java`:
- Around line 162-164: The extpEmitter.afterCreate(inventory) call in
ZoneManagerImpl currently runs after persistence without protection, so any
exception can make the API return failure while the Zone is already created;
wrap the extpEmitter.afterCreate(ZoneInventory) invocation in a try/catch inside
the create flow (method that converts finalVO to ZoneInventory), catch Throwable
(or Exception), log the full error with context (including the zone id/name from
ZoneInventory) using the class logger, and suppress the exception so it does not
propagate to the caller; optionally record a metric or emit a non-fatal event
for monitoring instead of failing the API, and keep the persisted Zone unchanged
to avoid changing previous behavior.
In `@docs/architecture-allocator-2026-03-18.md`:
- Around line 969-983: The code currently sets candidateHostUuids on
AllocateHostMsg inside injectCandidateHostUuidsAndAllocate and then calls
doHandleAllocateHost, but the allocator chain expects the candidate list on
HostAllocatorSpec; change the injection to populate
HostAllocatorSpec.candidateHostUuids (not msg) — either by invoking the
HostAllocatorPreStartExtensionPoint during spec construction or by adding a
helper that attaches the candidateHostUuids to the spec before calling
doHandleAllocateHost; ensure DesignatedHostAllocatorFlow reads
spec.getCandidateHostUuids and remove/stop relying on msg.setCandidateHostUuids
in injectCandidateHostUuidsAndAllocate so the pre-filtered set is visible to the
HostAllocatorChain.
In `@docs/architecture-bm2-adapter-2026-03-18.md`:
- Around line 262-268: The fenced code block showing the flow with
APIAddBareMetal2IpmiChassisMsg →
BareMetal2ChassisFactory.createBareMetal2Chassis() → BareMetal2IpmiChassisVO
etc. is missing a language tag; update the opening backticks to include an
appropriate language (e.g., ```text or ```java) so the block is labeled and
markdownlint MD040 is avoided, and apply the same fix to the other unlabeled
fenced blocks mentioned (those containing similar flow or code snippets) by
adding consistent language tags.
In `@docs/architecture-kvm-adapter-2026-03-18.md`:
- Around line 521-527: The code sets ServerPoolVO.state using the wrong enum
(PhysicalServerState.Enabled); update the assignment in the ServerPoolVO
creation (the call to pool.setState(...)) to use the ServerPoolState enum value
(e.g., ServerPoolState.Enabled or the correct ServerPoolState constant) instead
of PhysicalServerState so the types align and the code compiles.
In `@docs/architecture-unified-hardware-2026-03-18.md`:
- Around line 2431-2444: The docs define two incompatible SPI signatures for
HardwareDiscoverable (discoverHardware(String, Completion) vs discover(String,
ReturnValueCompletion<HardwareDiscoveryResult>)); pick one final SPI and make
all docs and references consistent: decide whether the SPI should be
discoverHardware/Completion or
discover/ReturnValueCompletion<HardwareDiscoveryResult>, update the
HardwareDiscoverable definition and every mention of discoverHardware/discover,
Completion/ReturnValueCompletion, and
HardwareDiscoveryResult/PhysicalServerHardwareInfoVO so modules implement the
chosen method name and completion type, and ensure the section describing
return/merging behavior references the selected types (e.g.,
HardwareDiscoveryResult) and OOB merge policy consistently across the document
and any example code.
- Around line 1230-1232: PhysicalServerRoleVO.roleStatus is defined as String
but the loop compares role.getRoleStatus() to the enum RoleStatus.Active; make
these types consistent by changing the VO to use the RoleStatus enum (update
PhysicalServerRoleVO.roleStatus and its getter/setter) or by comparing the
String to the enum's string form (e.g., compare role.getRoleStatus() to
RoleStatus.Active.name() or RoleStatus.Active.toString()); update any code that
constructs or persists PhysicalServerRoleVO and any call sites that set
roleStatus to use the chosen representation so comparisons like the one in the
loop (role.getRoleStatus() != RoleStatus.Active) compile and behave correctly.
- Around line 2021-2068: The docs conflict: you introduced
ClusterServerPoolRefVO as the Cluster↔ServerPool association but other docs
(docs/STATUS.md and docs/api-flow-qa-testplan.md) still describe
ClusterVO.serverPoolUuid; pick the association-table model and update the other
docs to match. Replace any mentions of ClusterVO.serverPoolUuid, cascades,
default-pool behavior and API assumptions with the new model's semantics
(ClusterServerPoolRefVO, poolUuid, UNIQUE(clusterUuid), CASCADE refs to
ServerPoolVO), update the API/test plan descriptions to reference the
association table for lookups/creation/deletion and adjust expected
cascade/default behavior accordingly, and ensure examples and QA steps reflect
using ClusterServerPoolRefVO rather than a field on ClusterVO.
In `@docs/archive/2026-03-31-unified-hardware-management.md`:
- Around line 470-493: The PhysicalServerRoleVO class definition is missing the
clusterUuid field used elsewhere; add a new member named clusterUuid to the
PhysicalServerRoleVO class (with `@Column` annotation and appropriate type String)
so the entity matches the queries that filter by clusterUuid; update
getters/setters (getClusterUuid/setClusterUuid) for caller code compatibility
and ensure annotations (e.g., `@Column`) mirror other fields like serverUuid to
avoid mapping/compilation errors.
- Around line 350-353: The entity field ClusterServerPoolRefVO.id is defined as
a plain long but the SQL migration uses AUTO_INCREMENT, causing a mismatch;
update the ClusterServerPoolRefVO class to mark id as an auto-generated primary
key (e.g., add the appropriate JPA generation annotation such as `@GeneratedValue`
with GenerationType.IDENTITY and keep `@Id/`@Column) so the entity auto-increments
on persist and matches the SQL migration; ensure the change is applied to the
ClusterServerPoolRefVO class where id is declared and to any similar VO classes
noted in the comment.
In `@docs/archive/hardware-unified-management/ANALYSIS_compute_allocator.md`:
- Around line 496-509: Replace the Windows absolute paths in the file reference
table with repository-relative paths so the docs are cross-platform and
portable; for each listed entry (e.g., HostAllocatorManagerImpl,
HostAllocatorChain, HostSortorChain, HostCapacityVO, HostCapacityUpdater,
HostCapacityReserveManagerImpl, HostCpuOverProvisioningManagerImpl,
HostCapacityOverProvisioningManagerImpl, HostCapacityAllocatorFlow,
VmAllocateHostForStoppedVmFlow, HostAllocatorStrategy, HostAllocatorSpec,
HostAllocatorConstant, HostAllocatorGlobalConfig) change values like
"H:\ZStack\zstack\..." to repo-relative locations such as
"compute/src/main/java/org/zstack/compute/allocator/HostAllocatorManagerImpl.java"
(or the appropriate module-root-relative path), update all table rows
consistently, and verify links/rendering in the docs render correctly on
Linux/Mac.
---
Nitpick comments:
In `@compute/src/main/java/org/zstack/compute/zone/ZoneManagerImpl.java`:
- Around line 54-55: Rename the field extpEmitter to zoneExtensionPointEmitter
in ZoneManagerImpl and update all references (e.g., usages where extpEmitter is
called, including the call around the previously noted usage at line 163) to the
new name to eliminate the unnecessary abbreviation; update the `@Autowired`
declaration and any method/constructor references inside ZoneManagerImpl to use
zoneExtensionPointEmitter and run a compile to ensure all references are
updated.
In `@docs/archive/hardware-unified-management/ANALYSIS_compute_allocator.md`:
- Line 26: The Markdown code fences containing ASCII diagrams (e.g., the
AllocateHostMsg allocation flow diagram and the ComputeCapacityVO abstract
architecture diagram) lack a language identifier; update those code fences to
include a language tag such as "text" or "plaintext" (for example change ``` to
```text) so editors and linters treat the blocks correctly and rendering/syntax
highlighting improves for the diagrams containing AllocateHostMsg and
ComputeCapacityVO.
🪄 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: bb37c46f-4162-463e-8aca-f3033aa0ae6f
⛔ Files ignored due to path filters (15)
compute/pom.xmlis excluded by!**/*.xmlcompute/src/test/resources/searchConfig/indexConfig.xmlis excluded by!**/*.xmlconf/globalConfig/hostAllocator.xmlis excluded by!**/*.xmlconf/globalConfig/physicalServer.xmlis excluded by!**/*.xmlconf/persistence.xmlis excluded by!**/*.xmlconf/serviceConfig/physicalServer.xmlis excluded by!**/*.xmlconf/serviceConfig/provisionNetwork.xmlis excluded by!**/*.xmlconf/serviceConfig/serverPool.xmlis excluded by!**/*.xmlconf/springConfigXml/HostAllocatorManager.xmlis excluded by!**/*.xmlconf/springConfigXml/Kvm.xmlis excluded by!**/*.xmlconf/springConfigXml/PhysicalServerManager.xmlis excluded by!**/*.xmlconf/zstack.xmlis excluded by!**/*.xmlplugin/kvm/pom.xmlis excluded by!**/*.xmlplugin/kvm/src/test/resources/searchConfig/indexConfig.xmlis excluded by!**/*.xmlplugin/physicalServer/pom.xmlis excluded by!**/*.xml
📒 Files selected for processing (267)
.gitignoreHardwareUnifiedManagement.mdcompute/src/main/java/org/zstack/compute/allocator/HostAllocatorGlobalConfig.javacompute/src/main/java/org/zstack/compute/allocator/HostAllocatorManagerImpl.javacompute/src/main/java/org/zstack/compute/allocator/HostCapacityOverProvisioningManagerImpl.javacompute/src/main/java/org/zstack/compute/allocator/HostCapacityUpdater.javacompute/src/main/java/org/zstack/compute/allocator/HostCpuOverProvisioningManagerImpl.javacompute/src/main/java/org/zstack/compute/allocator/PhysicalServerCapacityBuffers.javacompute/src/main/java/org/zstack/compute/allocator/PhysicalServerCapacityUpdater.javacompute/src/main/java/org/zstack/compute/cluster/ClusterExtensionPointEmitter.javacompute/src/main/java/org/zstack/compute/cluster/ClusterManagerImpl.javacompute/src/main/java/org/zstack/compute/host/HostManagerImpl.javacompute/src/main/java/org/zstack/compute/vm/StaticIpOperator.javacompute/src/main/java/org/zstack/compute/vm/VmCreateOnHypervisorFlow.javacompute/src/main/java/org/zstack/compute/vm/VmInstantiateResourceForChangeImageFlow.javacompute/src/main/java/org/zstack/compute/vm/VmInstantiateResourcePostFlow.javacompute/src/main/java/org/zstack/compute/vm/VmInstantiateResourcePreFlow.javacompute/src/main/java/org/zstack/compute/vm/VmReleaseResourceFlow.javacompute/src/main/java/org/zstack/compute/vm/VmStartOnHypervisorFlow.javacompute/src/main/java/org/zstack/compute/zone/ZoneExtensionPointEmitter.javacompute/src/main/java/org/zstack/compute/zone/ZoneManagerImpl.javacompute/src/test/java/org/zstack/compute/allocator/HostCpuOverProvisioningManagerImplTest.javacompute/src/test/java/org/zstack/compute/allocator/PhysicalServerCapacityUpdaterOrchestrationOverheadTest.javacompute/src/test/java/org/zstack/compute/allocator/PhysicalServerCapacityUpdaterTest.javacompute/src/test/resources/zstack.propertiesconf/db/upgrade/V5.5.18__schema.sqlcore/src/main/java/org/zstack/core/aspect/EncryptColumnAspect.ajcore/src/main/java/org/zstack/core/db/GLock.javadocs/STATUS.mddocs/api-flow-qa-testplan.mddocs/architecture-allocator-2026-03-18.mddocs/architecture-bm1-adapter-2026-03-18.mddocs/architecture-bm2-adapter-2026-03-18.mddocs/architecture-container-adapter-2026-03-18.mddocs/architecture-kvm-adapter-2026-03-18.mddocs/architecture-review-final-2026-03-18.mddocs/architecture-unified-hardware-2026-03-18.mddocs/archive/2026-03-31-milestone-20days.mddocs/archive/2026-03-31-unified-hardware-management.mddocs/archive/2026-04-02-unified-hardware.mddocs/archive/2026-04-08-task1-5-patch.mddocs/archive/hardware-unified-management/ANALYSIS_baremetal2_detailed.mddocs/archive/hardware-unified-management/ANALYSIS_baremetal2_module.mddocs/archive/hardware-unified-management/ANALYSIS_baremetal_module.mddocs/archive/hardware-unified-management/ANALYSIS_compute_allocator.mddocs/archive/hardware-unified-management/ANALYSIS_container_module.mddocs/archive/hardware-unified-management/ANALYSIS_kvm_host_module.mddocs/archive/hardware-unified-management/ARCHITECT_DECISION.mddocs/archive/hardware-unified-management/CH1_Architecture_Overview.mddocs/archive/hardware-unified-management/CH2_Physical_Layer.mddocs/archive/hardware-unified-management/CH3_Role_and_Logic_Layer.mddocs/archive/hardware-unified-management/CH4_Allocation_and_Capacity.mddocs/archive/hardware-unified-management/CH5_Monitoring_and_Lifecycle.mddocs/archive/hardware-unified-management/CH6_API_and_Migration.mddocs/archive/hardware-unified-management/PHASE1_Detailed_Design.mddocs/archive/hardware-unified-management/PLAN_header.mddocs/archive/hardware-unified-management/REVIEW_baremetal2.mddocs/archive/hardware-unified-management/REVIEW_baremetal2_round2.mddocs/archive/hardware-unified-management/REVIEW_baremetal_v1.mddocs/archive/hardware-unified-management/REVIEW_compute_allocator.mddocs/archive/hardware-unified-management/REVIEW_compute_allocator_R2.mddocs/archive/hardware-unified-management/REVIEW_container.mddocs/archive/hardware-unified-management/REVIEW_container_round2.mddocs/archive/hardware-unified-management/REVIEW_kvm_host_expert.mddocs/archive/hardware-unified-management/REVIEW_kvm_host_expert_round2.mddocs/archive/hardware-unified-management/REVIEW_summary.mddocs/archive/hardware-unified-management/REVIEW_summary_round2.mddocs/archive/hardware-unified-management/REVISED_Architecture_Assessment.mddocs/archive/hardware-unified-management/analysis_before/ANALYSIS_baremetal.mddocs/archive/hardware-unified-management/analysis_before/ANALYSIS_baremetal2.mddocs/archive/hardware-unified-management/analysis_before/ANALYSIS_baremetal2_detailed.mddocs/archive/hardware-unified-management/analysis_before/ANALYSIS_baremetal_detailed.mddocs/archive/hardware-unified-management/analysis_before/ANALYSIS_container.mddocs/archive/hardware-unified-management/analysis_before/ANALYSIS_container_detailed.mddocs/archive/hardware-unified-management/analysis_before/ANALYSIS_kvm.mddocs/archive/hardware-unified-management/analysis_before/ANALYSIS_kvm_detailed.mddocs/archive/hardware-unified-management/analysis_before/PLAN_header.mddocs/archive/prd-unified-hardware-2026-03-18.bak.mddocs/archive/product-brief-unified-hardware-2026-03-06.bak.mddocs/audits/2026-04-27-phase2-prd-audit.mddocs/blockers/2026-04-27-wave1-failed-cases.mddocs/blockers/2026-04-29-p11-forked-vm-crash.mddocs/brainstorms/2026-04-15-phase2-full-requirements.mddocs/brainstorms/2026-04-16-provision-provider-spi-design.mddocs/brainstorms/2026-04-16-role-provider-spi-design.mddocs/brainstorms/2026-04-26-001-phase-2d-autonomous-run-report.mddocs/brainstorms/_template-next-session.mddocs/brainstorms/next-session.mddocs/decisions/ADR-001-hostcapacity-updater-static-resolve.mddocs/decisions/ADR-002-hostcapacity-updater-uuid-semantics.mddocs/decisions/ADR-003-hami-3field-flush.mddocs/decisions/ADR-004-psc-no-fk-vcenter.mddocs/decisions/ADR-005-hcv-view-algorithm-merge.mddocs/decisions/ADR-006-pspn-inplace-rename.mddocs/decisions/ADR-007-no-backup-tables.mddocs/decisions/ADR-008-fk-rename-follows-parent.mddocs/decisions/ADR-009-vcenter-option-c.mddocs/decisions/ADR-010-bm1-out-of-scope.mddocs/decisions/ADR-011-md5-salt-uuid-derivation.mddocs/decisions/ADR-012-roleprovider-pre-generated-role-uuid.mddocs/decisions/ADR-013-bm2-clusterref-table-not-view.mddocs/decisions/ADR-014-incremental-rebuild-antipattern.mddocs/decisions/README.mddocs/design-verdict-2026-04-14.mddocs/plans/2026-04-22-001-feat-v5518-unified-hardware-phase2-plan.mddocs/plans/2026-04-23-001-u28-flyway-data-migration.mddocs/plans/2026-04-27-001-feat-v5518-phase2-prd-audit-plan.mddocs/plans/2026-04-27-002-feat-phase3-wave1-exec-plan.mddocs/plans/2026-04-28-001-fix-phase2-prd-gaps-plan.mddocs/plans/2026-04-29-001-phase3-review-followups.mddocs/plans/2026-05-01-physical-server-first-provision-plan.mddocs/plans/2026-05-02-bm2-gateway-data-plane-wiring-plan.mddocs/plans/2026-05-03-os-install-completion-monitoring-plan.mddocs/plans/2026-05-08-001-psc-writer-collapse-plan.mddocs/plans/2026-05-09-001-cordon-production-trigger-plan.mddocs/prd-unified-hardware-2026-03-18.mddocs/product-brief-unified-hardware-2026-03-06.mddocs/qa/v5.5.18-qa-handoff.mddocs/review-allocator-deep-2026-03-19.mddocs/review-bm1-deep-2026-03-19.mddocs/review-bm2-deep-2026-03-19.mddocs/review-container-deep-2026-03-19.mddocs/review-kvm-deep-2026-03-19.mddocs/runbooks/aspectj-method-local-class-pitfall.mddocs/runbooks/physical-server-pxe-real-env-validation.mddocs/runbooks/testing-envs.mddocs/runbooks/v5518-recalculate-perf.mddocs/runbooks/v5518-sql-ddl-pitfalls.mddocs/runbooks/v5518-unified-hardware-rollback.mddocs/superpowers/plans/2026-04-07-unified-hardware-full.mdheader/src/main/java/org/zstack/header/allocator/HostCapacityStruct.javaheader/src/main/java/org/zstack/header/allocator/HostCapacityVO.javaheader/src/main/java/org/zstack/header/allocator/ReportHostCapacityExtensionPoint.javaheader/src/main/java/org/zstack/header/allocator/ServerReservedCapacityExtensionPoint.javaheader/src/main/java/org/zstack/header/cluster/ClusterAO.javaheader/src/main/java/org/zstack/header/cluster/ClusterAO_.javaheader/src/main/java/org/zstack/header/cluster/ClusterCreateExtensionPoint.javaheader/src/main/java/org/zstack/header/cluster/ClusterInventory.javaheader/src/main/java/org/zstack/header/host/AddHostMessage.javaheader/src/main/java/org/zstack/header/server/APIAttachPhysicalServerRoleEvent.javaheader/src/main/java/org/zstack/header/server/APIAttachPhysicalServerRoleMsg.javaheader/src/main/java/org/zstack/header/server/APIAttachProvisionNetworkToClusterEvent.javaheader/src/main/java/org/zstack/header/server/APIAttachProvisionNetworkToClusterMsg.javaheader/src/main/java/org/zstack/header/server/APIAttachProvisionNetworkToPoolEvent.javaheader/src/main/java/org/zstack/header/server/APIAttachProvisionNetworkToPoolMsg.javaheader/src/main/java/org/zstack/header/server/APIChangeClusterServerPoolEvent.javaheader/src/main/java/org/zstack/header/server/APIChangeClusterServerPoolMsg.javaheader/src/main/java/org/zstack/header/server/APIChangePhysicalServerStateEvent.javaheader/src/main/java/org/zstack/header/server/APIChangePhysicalServerStateMsg.javaheader/src/main/java/org/zstack/header/server/APICreatePhysicalServerEvent.javaheader/src/main/java/org/zstack/header/server/APICreatePhysicalServerMsg.javaheader/src/main/java/org/zstack/header/server/APICreateProvisionNetworkEvent.javaheader/src/main/java/org/zstack/header/server/APICreateProvisionNetworkMsg.javaheader/src/main/java/org/zstack/header/server/APICreateServerPoolEvent.javaheader/src/main/java/org/zstack/header/server/APICreateServerPoolMsg.javaheader/src/main/java/org/zstack/header/server/APIDeletePhysicalServerEvent.javaheader/src/main/java/org/zstack/header/server/APIDeletePhysicalServerMsg.javaheader/src/main/java/org/zstack/header/server/APIDeleteProvisionNetworkEvent.javaheader/src/main/java/org/zstack/header/server/APIDeleteProvisionNetworkMsg.javaheader/src/main/java/org/zstack/header/server/APIDeleteServerPoolEvent.javaheader/src/main/java/org/zstack/header/server/APIDeleteServerPoolMsg.javaheader/src/main/java/org/zstack/header/server/APIDetachPhysicalServerRoleEvent.javaheader/src/main/java/org/zstack/header/server/APIDetachPhysicalServerRoleMsg.javaheader/src/main/java/org/zstack/header/server/APIDetachProvisionNetworkFromClusterEvent.javaheader/src/main/java/org/zstack/header/server/APIDetachProvisionNetworkFromClusterMsg.javaheader/src/main/java/org/zstack/header/server/APIDetachProvisionNetworkFromPoolEvent.javaheader/src/main/java/org/zstack/header/server/APIDetachProvisionNetworkFromPoolMsg.javaheader/src/main/java/org/zstack/header/server/APIDiscoverPhysicalServerHardwareEvent.javaheader/src/main/java/org/zstack/header/server/APIDiscoverPhysicalServerHardwareMsg.javaheader/src/main/java/org/zstack/header/server/APIPowerOffPhysicalServerEvent.javaheader/src/main/java/org/zstack/header/server/APIPowerOffPhysicalServerMsg.javaheader/src/main/java/org/zstack/header/server/APIPowerOnPhysicalServerEvent.javaheader/src/main/java/org/zstack/header/server/APIPowerOnPhysicalServerMsg.javaheader/src/main/java/org/zstack/header/server/APIPowerResetPhysicalServerEvent.javaheader/src/main/java/org/zstack/header/server/APIPowerResetPhysicalServerMsg.javaheader/src/main/java/org/zstack/header/server/APIProvisionPhysicalServerEvent.javaheader/src/main/java/org/zstack/header/server/APIProvisionPhysicalServerMsg.javaheader/src/main/java/org/zstack/header/server/APIQueryPhysicalServerMsg.javaheader/src/main/java/org/zstack/header/server/APIQueryPhysicalServerReply.javaheader/src/main/java/org/zstack/header/server/APIQueryPhysicalServerRoleMsg.javaheader/src/main/java/org/zstack/header/server/APIQueryPhysicalServerRoleReply.javaheader/src/main/java/org/zstack/header/server/APIQueryProvisionNetworkMsg.javaheader/src/main/java/org/zstack/header/server/APIQueryProvisionNetworkReply.javaheader/src/main/java/org/zstack/header/server/APIQueryServerPoolMsg.javaheader/src/main/java/org/zstack/header/server/APIQueryServerPoolReply.javaheader/src/main/java/org/zstack/header/server/APIScanPhysicalServersEvent.javaheader/src/main/java/org/zstack/header/server/APIScanPhysicalServersMsg.javaheader/src/main/java/org/zstack/header/server/APIUpdatePhysicalServerEvent.javaheader/src/main/java/org/zstack/header/server/APIUpdatePhysicalServerMsg.javaheader/src/main/java/org/zstack/header/server/APIUpdateProvisionNetworkEvent.javaheader/src/main/java/org/zstack/header/server/APIUpdateProvisionNetworkMsg.javaheader/src/main/java/org/zstack/header/server/APIUpdateServerPoolEvent.javaheader/src/main/java/org/zstack/header/server/APIUpdateServerPoolMsg.javaheader/src/main/java/org/zstack/header/server/CapacityUsage.javaheader/src/main/java/org/zstack/header/server/CreateRoleEntityContext.javaheader/src/main/java/org/zstack/header/server/PhysicalServerAO.javaheader/src/main/java/org/zstack/header/server/PhysicalServerAO_.javaheader/src/main/java/org/zstack/header/server/PhysicalServerCapacityState.javaheader/src/main/java/org/zstack/header/server/PhysicalServerCapacityVO.javaheader/src/main/java/org/zstack/header/server/PhysicalServerCapacityVO_.javaheader/src/main/java/org/zstack/header/server/PhysicalServerConstant.javaheader/src/main/java/org/zstack/header/server/PhysicalServerEnqueueDiscoveryHook.javaheader/src/main/java/org/zstack/header/server/PhysicalServerHardwareDetailVO.javaheader/src/main/java/org/zstack/header/server/PhysicalServerHardwareDetailVO_.javaheader/src/main/java/org/zstack/header/server/PhysicalServerHardwareDiscoveryExtensionPoint.javaheader/src/main/java/org/zstack/header/server/PhysicalServerHardwareInfoVO.javaheader/src/main/java/org/zstack/header/server/PhysicalServerHardwareInfoVO_.javaheader/src/main/java/org/zstack/header/server/PhysicalServerInventory.javaheader/src/main/java/org/zstack/header/server/PhysicalServerManager.javaheader/src/main/java/org/zstack/header/server/PhysicalServerPathTwoExtensionPoint.javaheader/src/main/java/org/zstack/header/server/PhysicalServerPowerStatus.javaheader/src/main/java/org/zstack/header/server/PhysicalServerProvisionDataPlane.javaheader/src/main/java/org/zstack/header/server/PhysicalServerProvisionNetworkClusterRefVO.javaheader/src/main/java/org/zstack/header/server/PhysicalServerProvisionNetworkClusterRefVO_.javaheader/src/main/java/org/zstack/header/server/PhysicalServerProvisionNetworkInventory.javaheader/src/main/java/org/zstack/header/server/PhysicalServerProvisionNetworkPoolRefVO.javaheader/src/main/java/org/zstack/header/server/PhysicalServerProvisionNetworkPoolRefVO_.javaheader/src/main/java/org/zstack/header/server/PhysicalServerProvisionNetworkVO.javaheader/src/main/java/org/zstack/header/server/PhysicalServerProvisionNetworkVO_.javaheader/src/main/java/org/zstack/header/server/PhysicalServerProvisionTarget.javaheader/src/main/java/org/zstack/header/server/PhysicalServerRoleInventory.javaheader/src/main/java/org/zstack/header/server/PhysicalServerRoleProvider.javaheader/src/main/java/org/zstack/header/server/PhysicalServerRoleVO.javaheader/src/main/java/org/zstack/header/server/PhysicalServerRoleVO_.javaheader/src/main/java/org/zstack/header/server/PhysicalServerState.javaheader/src/main/java/org/zstack/header/server/PhysicalServerStateEvent.javaheader/src/main/java/org/zstack/header/server/PhysicalServerVO.javaheader/src/main/java/org/zstack/header/server/ProvisionNetworkState.javaheader/src/main/java/org/zstack/header/server/ProvisionNetworkType.javaheader/src/main/java/org/zstack/header/server/ProvisionPhase.javaheader/src/main/java/org/zstack/header/server/ProvisionProvider.javaheader/src/main/java/org/zstack/header/server/ProvisionRequest.javaheader/src/main/java/org/zstack/header/server/ProvisionResult.javaheader/src/main/java/org/zstack/header/server/RoleMatchContext.javaheader/src/main/java/org/zstack/header/server/RoleWorkloadStatus.javaheader/src/main/java/org/zstack/header/server/SchedulingMode.javaheader/src/main/java/org/zstack/header/server/ServerPoolInventory.javaheader/src/main/java/org/zstack/header/server/ServerPoolState.javaheader/src/main/java/org/zstack/header/server/ServerPoolVO.javaheader/src/main/java/org/zstack/header/server/ServerPoolVO_.javaheader/src/main/java/org/zstack/header/server/ServerRoleType.javaheader/src/main/java/org/zstack/header/server/WorkloadRef.javaheader/src/main/java/org/zstack/header/server/flow/PathTwoFlowDataKey.javaheader/src/main/java/org/zstack/header/zone/ZoneCreateExtensionPoint.javaidentity/src/main/java/org/zstack/identity/QuotaAPIRequestChecker.javaidentity/src/main/java/org/zstack/identity/QuotaUtil.javalongjob/src/main/java/org/zstack/longjob/LongJobManagerImpl.javaplugin/kvm/src/main/java/org/zstack/kvm/APIAddKVMHostMsg.javaplugin/kvm/src/main/java/org/zstack/kvm/AddKVMHostMsg.javaplugin/kvm/src/main/java/org/zstack/kvm/KVMHost.javaplugin/kvm/src/main/java/org/zstack/kvm/KvmPhysicalServerRoleSoftDeleteExtension.javaplugin/kvm/src/main/java/org/zstack/kvm/KvmRoleProvider.javaplugin/kvm/src/main/java/org/zstack/kvm/server/KvmHardwareDiscoveryAdapter.javaplugin/kvm/src/test/java/org/zstack/core/Platform.javaplugin/kvm/src/test/java/org/zstack/kvm/KvmRoleProviderTest.javaplugin/kvm/src/test/resources/zstack.propertiesplugin/physicalServer/src/main/java/org/zstack/server/DefaultServerPoolClusterCreateExtension.javaplugin/physicalServer/src/main/java/org/zstack/server/DefaultServerPoolCreationPolicy.javaplugin/physicalServer/src/main/java/org/zstack/server/DefaultServerPoolFactory.javaplugin/physicalServer/src/main/java/org/zstack/server/DefaultServerPoolZoneCreateExtension.javaplugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerApiInterceptor.javaplugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerAutoAssociator.javaplugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerCapacityCascadeExtension.javaplugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerCascadeExtension.javaplugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerGlobalConfig.javaplugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerHardwareDetailCascadeExtension.javaplugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerHardwareInfoCascadeExtension.java
✅ Files skipped from review due to trivial changes (5)
- compute/src/test/resources/zstack.properties
- .gitignore
- docs/archive/hardware-unified-management/ANALYSIS_baremetal_module.md
- docs/archive/2026-03-31-milestone-20days.md
- docs/archive/2026-04-08-task1-5-patch.md
🚧 Files skipped from review as they are similar to previous changes (24)
- compute/src/main/java/org/zstack/compute/vm/VmCreateOnHypervisorFlow.java
- compute/src/main/java/org/zstack/compute/allocator/HostCapacityOverProvisioningManagerImpl.java
- compute/src/main/java/org/zstack/compute/vm/VmInstantiateResourcePreFlow.java
- core/src/main/java/org/zstack/core/db/GLock.java
- compute/src/main/java/org/zstack/compute/allocator/PhysicalServerCapacityBuffers.java
- core/src/main/java/org/zstack/core/aspect/EncryptColumnAspect.aj
- compute/src/main/java/org/zstack/compute/vm/VmReleaseResourceFlow.java
- compute/src/main/java/org/zstack/compute/vm/VmInstantiateResourceForChangeImageFlow.java
- compute/src/main/java/org/zstack/compute/cluster/ClusterManagerImpl.java
- compute/src/main/java/org/zstack/compute/host/HostManagerImpl.java
- compute/src/main/java/org/zstack/compute/allocator/HostAllocatorGlobalConfig.java
- compute/src/main/java/org/zstack/compute/cluster/ClusterExtensionPointEmitter.java
- compute/src/main/java/org/zstack/compute/zone/ZoneExtensionPointEmitter.java
- compute/src/main/java/org/zstack/compute/allocator/HostCpuOverProvisioningManagerImpl.java
- compute/src/main/java/org/zstack/compute/vm/StaticIpOperator.java
- compute/src/test/java/org/zstack/compute/allocator/HostCpuOverProvisioningManagerImplTest.java
- compute/src/main/java/org/zstack/compute/allocator/PhysicalServerCapacityUpdater.java
- compute/src/main/java/org/zstack/compute/allocator/HostCapacityUpdater.java
- compute/src/test/java/org/zstack/compute/allocator/PhysicalServerCapacityUpdaterTest.java
- compute/src/main/java/org/zstack/compute/vm/VmInstantiateResourcePostFlow.java
- compute/src/main/java/org/zstack/compute/vm/VmStartOnHypervisorFlow.java
- compute/src/test/java/org/zstack/compute/allocator/PhysicalServerCapacityUpdaterOrchestrationOverheadTest.java
- HardwareUnifiedManagement.md
- conf/db/upgrade/V5.5.18__schema.sql
| vo = new PhysicalServerCapacityVO(); | ||
| vo.setUuid(serverUuid); | ||
| vo.setTotalCpu(totalCpu); | ||
| vo.setAvailableCpu(availCpu); | ||
| vo.setTotalMemory(msg.getTotalMemory()); | ||
| vo.setAvailableMemory(availMem); | ||
| vo.setTotalPhysicalMemory(msg.getTotalMemory()); | ||
| vo.setAvailablePhysicalMemory(availMem); | ||
| vo.setAvailablePhysicalMemory(availPhysMem); | ||
| vo.setCpuNum(msg.getCpuNum()); | ||
| vo.setCpuSockets(msg.getCpuSockets()); | ||
| vo.setCpuCoreNum(msg.getCpuCoreNum()); | ||
|
|
||
| HostCapacityStruct s = new HostCapacityStruct(); | ||
| s.setCpuSockets(vo.getCpuSockets()); | ||
| s.setCapacityVO(vo); | ||
| s.setCpuNum(msg.getCpuNum()); | ||
| s.setTotalCpu(totalCpu); | ||
| s.setTotalMemory(msg.getTotalMemory()); | ||
| s.setUsedCpu(msg.getUsedCpu()); | ||
| s.setUsedMemory(msg.getUsedMemory()); | ||
| s.setInit(true); | ||
| for (ReportHostCapacityExtensionPoint ext : pluginRgty.getExtensionList(ReportHostCapacityExtensionPoint.class)) { | ||
| vo = ext.reportHostCapacity(s); | ||
| } | ||
| dbf.persist(vo); | ||
| } else if (needUpdateCapacity(vo, msg, totalCpu, availCpu, availMem)) { | ||
| dbf.getEntityManager().persist(vo); | ||
| } else if (needUpdateCapacity(vo, msg, totalCpu, availPhysMem)) { | ||
| vo.setCpuNum(msg.getCpuNum()); | ||
| vo.setTotalCpu(totalCpu); | ||
| vo.setAvailableCpu(availCpu); | ||
| vo.setTotalPhysicalMemory(msg.getTotalMemory()); | ||
| vo.setAvailablePhysicalMemory(availMem); | ||
| vo.setAvailablePhysicalMemory(availPhysMem); | ||
| vo.setTotalMemory(msg.getTotalMemory()); | ||
| vo.setCpuSockets(msg.getCpuSockets()); | ||
| vo.setCpuCoreNum(msg.getCpuCoreNum()); | ||
|
|
||
| HostCapacityStruct s = new HostCapacityStruct(); | ||
| s.setCapacityVO(vo); | ||
| s.setCpuSockets(msg.getCpuSockets()); | ||
| s.setTotalCpu(totalCpu); | ||
| s.setTotalMemory(msg.getTotalMemory()); | ||
| s.setUsedCpu(msg.getUsedCpu()); | ||
| s.setUsedMemory(msg.getUsedMemory()); | ||
| s.setInit(false); | ||
| for (ReportHostCapacityExtensionPoint ext : pluginRgty.getExtensionList(ReportHostCapacityExtensionPoint.class)) { | ||
| vo = ext.reportHostCapacity(s); | ||
| } | ||
| dbf.update(vo); | ||
| dbf.getEntityManager().merge(vo); | ||
| } | ||
|
|
||
| // Layer 2: derive availableCpu / availableMemory via single-lock recalculate path. | ||
| psCapacityUpdater.recalculate(serverUuid); | ||
|
|
||
| bus.reply(msg, new MessageReply()); | ||
| } | ||
|
|
||
| private boolean needUpdateCapacity(HostCapacityVO vo, ReportHostCapacityMessage msg, long totalCpu, long avaliCpu, long availMem) { | ||
| private boolean needUpdateCapacity(PhysicalServerCapacityVO vo, ReportHostCapacityMessage msg, long totalCpu, long availPhysMem) { | ||
| return vo.getCpuNum() != msg.getCpuNum() || vo.getTotalCpu() != totalCpu | ||
| || vo.getAvailableCpu() != avaliCpu || vo.getTotalPhysicalMemory() != msg.getTotalMemory() | ||
| || vo.getAvailablePhysicalMemory() != availMem || vo.getTotalMemory() != msg.getTotalMemory() | ||
| || vo.getTotalPhysicalMemory() != msg.getTotalMemory() | ||
| || vo.getAvailablePhysicalMemory() != availPhysMem || vo.getTotalMemory() != msg.getTotalMemory() | ||
| || vo.getCpuSockets() != msg.getCpuSockets() || vo.getCpuCoreNum() != msg.getCpuCoreNum(); |
There was a problem hiding this comment.
totalPhysicalCpu 在这条写路径里从未被写入。
这里创建/更新 PhysicalServerCapacityVO 时只维护了 totalCpu,但没有初始化或刷新 totalPhysicalCpu。这样 KVM 主机的物理 CPU 总量会一直停在默认值,后面任何依赖物理 CPU 的展示、校验或 buffer 计算都会读到错误数据。
🤖 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
`@compute/src/main/java/org/zstack/compute/allocator/HostAllocatorManagerImpl.java`
around lines 203 - 234, PhysicalServerCapacityVO's totalPhysicalCpu is never set
or compared; update both the creation path (where vo is new) and the update path
(inside the needUpdateCapacity branch) to assign
vo.setTotalPhysicalCpu(msg.getTotalPhysicalCpu()) (or the appropriate source
field) and add a comparison for getTotalPhysicalCpu() in needUpdateCapacity so
changes to the host's physical CPU count trigger updates; target the
PhysicalServerCapacityVO assignments and the needUpdateCapacity(...) method for
the changes.
| * 将阶段1输出的 candidateHostUuids 注入到 HostAllocatorSpec, | ||
| * 然后调用现有 HostAllocatorChain 执行阶段2。 | ||
| * | ||
| * 注入方式:在 HostAllocatorSpec 中新增 candidateHostUuids 字段, | ||
| * DesignatedHostAllocatorFlow 识别此字段后,只加载这些 Host 作为初始候选集。 | ||
| */ | ||
| private void injectCandidateHostUuidsAndAllocate( | ||
| AllocateHostMsg msg, | ||
| List<String> candidateHostUuids, | ||
| ReturnValueCompletion<List<HostInventory>> completion) { | ||
| // 在 msg 中设置预筛选的候选集(通过新增的 candidateHostUuids 字段) | ||
| msg.setCandidateHostUuids(candidateHostUuids); | ||
| // 调用现有 HostAllocatorChain(doHandleAllocateHost) | ||
| // DesignatedHostAllocatorFlow 会识别 candidateHostUuids 做预过滤 | ||
| doHandleAllocateHost(msg, completion); |
There was a problem hiding this comment.
阶段2候选集注入目标写错:应注入 HostAllocatorSpec,不是 AllocateHostMsg
Line [979]-[983] 里直接 msg.setCandidateHostUuids(...),但本章其他位置(Line [998]-[1021])定义的扩展点是在 HostAllocatorSpec 生效。按当前流程,阶段2很可能读不到预筛选候选集。
建议修正思路
- msg.setCandidateHostUuids(candidateHostUuids);
- doHandleAllocateHost(msg, completion);
+ HostAllocatorSpec spec = buildHostAllocatorSpec(msg);
+ spec.setCandidateHostUuids(candidateHostUuids);
+ doHandleAllocateHost(spec, completion);// 如果当前代码路径只能接收 msg,建议通过 HostAllocatorPreStartExtensionPoint
// 在 spec 构建后注入 candidateHostUuids,而不是在 msg 层面承载。Also applies to: 998-1021
🤖 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 `@docs/architecture-allocator-2026-03-18.md` around lines 969 - 983, The code
currently sets candidateHostUuids on AllocateHostMsg inside
injectCandidateHostUuidsAndAllocate and then calls doHandleAllocateHost, but the
allocator chain expects the candidate list on HostAllocatorSpec; change the
injection to populate HostAllocatorSpec.candidateHostUuids (not msg) — either by
invoking the HostAllocatorPreStartExtensionPoint during spec construction or by
adding a helper that attaches the candidateHostUuids to the spec before calling
doHandleAllocateHost; ensure DesignatedHostAllocatorFlow reads
spec.getCandidateHostUuids and remove/stop relying on msg.setCandidateHostUuids
in injectCandidateHostUuidsAndAllocate so the pre-filtered set is visible to the
HostAllocatorChain.
| ``` | ||
| APIAddBareMetal2IpmiChassisMsg | ||
| → BareMetal2ChassisFactory.createBareMetal2Chassis() | ||
| → BareMetal2IpmiChassisVO 持久化(status: HardwareInfoUnknown) | ||
| → [可选] 触发 Inspect(PXE 重启物理机进行硬件发现) | ||
| → Chassis status 变为 Available | ||
| ``` |
There was a problem hiding this comment.
补齐未标注语言的 fenced code block,避免持续触发 MD040
当前多处代码块仍是裸 ```,会反复触发 markdownlint。建议统一补上 text/`java`/`sql` 等语言标签,减少文档 lint 噪音。
Also applies to: 336-343, 435-443, 598-607, 617-622, 820-826, 946-983
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 262-262: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 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 `@docs/architecture-bm2-adapter-2026-03-18.md` around lines 262 - 268, The
fenced code block showing the flow with APIAddBareMetal2IpmiChassisMsg →
BareMetal2ChassisFactory.createBareMetal2Chassis() → BareMetal2IpmiChassisVO
etc. is missing a language tag; update the opening backticks to include an
appropriate language (e.g., ```text or ```java) so the block is labeled and
markdownlint MD040 is avoided, and apply the same fix to the other unlabeled
fenced blocks mentioned (those containing similar flow or code snippets) by
adding consistent language tags.
| ServerPoolVO pool = new ServerPoolVO(); | ||
| pool.setUuid(Platform.getUuid()); | ||
| pool.setZoneUuid(zoneUuid); | ||
| pool.setName("default-pool"); | ||
| pool.setDescription("Auto-created default pool for zone"); | ||
| pool.setState(PhysicalServerState.Enabled); | ||
| pool.setCreateDate(new Timestamp(System.currentTimeMillis())); |
There was a problem hiding this comment.
ServerPoolVO.state 赋值使用了错误的枚举类型
Line [526] 把 ServerPoolVO.state 赋值为 PhysicalServerState.Enabled,类型应为 ServerPoolState。按文档实现会直接编译失败。
建议修正片段
- pool.setState(PhysicalServerState.Enabled);
+ pool.setState(ServerPoolState.Enabled);📝 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.
| ServerPoolVO pool = new ServerPoolVO(); | |
| pool.setUuid(Platform.getUuid()); | |
| pool.setZoneUuid(zoneUuid); | |
| pool.setName("default-pool"); | |
| pool.setDescription("Auto-created default pool for zone"); | |
| pool.setState(PhysicalServerState.Enabled); | |
| pool.setCreateDate(new Timestamp(System.currentTimeMillis())); | |
| ServerPoolVO pool = new ServerPoolVO(); | |
| pool.setUuid(Platform.getUuid()); | |
| pool.setZoneUuid(zoneUuid); | |
| pool.setName("default-pool"); | |
| pool.setDescription("Auto-created default pool for zone"); | |
| pool.setState(ServerPoolState.Enabled); | |
| pool.setCreateDate(new Timestamp(System.currentTimeMillis())); |
🤖 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 `@docs/architecture-kvm-adapter-2026-03-18.md` around lines 521 - 527, The code
sets ServerPoolVO.state using the wrong enum (PhysicalServerState.Enabled);
update the assignment in the ServerPoolVO creation (the call to
pool.setState(...)) to use the ServerPoolState enum value (e.g.,
ServerPoolState.Enabled or the correct ServerPoolState constant) instead of
PhysicalServerState so the types align and the code compiles.
| **设计决策**:使用关联表 `ClusterServerPoolRefVO`,而不是在 ClusterVO 上加 FK。 | ||
|
|
||
| 理由: | ||
| 1. **不改 ClusterVO**(Wrap, don't delete 原则,保护 git blame) | ||
| 2. 多个 Cluster 可引用同一 ServerPool(多对一),关联表可自然表达 | ||
| 3. 关联可选——未关联 ServerPool 的 Cluster 行为完全不变(向后兼容) | ||
| 4. 未来如果需要多对多(一个 Cluster 对应多个 Pool),关联表无需改 schema | ||
|
|
||
| ```java | ||
| package org.zstack.header.server; | ||
|
|
||
| import org.zstack.header.cluster.ClusterVO; | ||
| import org.zstack.header.vo.ForeignKey; | ||
| import org.zstack.header.vo.ForeignKey.ReferenceOption; | ||
|
|
||
| import javax.persistence.*; | ||
| import java.sql.Timestamp; | ||
|
|
||
| /** | ||
| * Cluster 与 ServerPool 的关联表。 | ||
| * | ||
| * 多个 Cluster 可引用同一个 ServerPool(多对一)。 | ||
| * UNIQUE(clusterUuid) 保证每个 Cluster 只关联一个 ServerPool。 | ||
| * 不使用 EO 软删除模式,直接硬删除。 | ||
| */ | ||
| @Entity | ||
| @Table(uniqueConstraints = { | ||
| @UniqueConstraint(name = "uk_cluster", columnNames = {"clusterUuid"}) | ||
| }) | ||
| public class ClusterServerPoolRefVO { | ||
|
|
||
| @Id | ||
| @Column | ||
| private String uuid; | ||
|
|
||
| @Column | ||
| @ForeignKey(parentEntityClass = ClusterVO.class, onDeleteAction = ReferenceOption.CASCADE) | ||
| private String clusterUuid; | ||
|
|
||
| @Column | ||
| @ForeignKey(parentEntityClass = ServerPoolVO.class, onDeleteAction = ReferenceOption.CASCADE) | ||
| private String poolUuid; | ||
|
|
||
| @Column | ||
| private Timestamp createDate; | ||
|
|
||
| // getter/setter 省略 | ||
| } |
There was a problem hiding this comment.
ClusterServerPoolRefVO 这套建模已经和同 PR 其他文档不一致了。
这里把 Cluster↔ServerPool 设计成独立关联表,但 docs/STATUS.md 和 docs/api-flow-qa-testplan.md 都已经按 ClusterVO.serverPoolUuid 直接建模,并基于那个前提描述了 API、级联和默认池行为。继续保留这段会把实现和测试引到两套不同 schema 上。
🤖 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 `@docs/architecture-unified-hardware-2026-03-18.md` around lines 2021 - 2068,
The docs conflict: you introduced ClusterServerPoolRefVO as the
Cluster↔ServerPool association but other docs (docs/STATUS.md and
docs/api-flow-qa-testplan.md) still describe ClusterVO.serverPoolUuid; pick the
association-table model and update the other docs to match. Replace any mentions
of ClusterVO.serverPoolUuid, cascades, default-pool behavior and API assumptions
with the new model's semantics (ClusterServerPoolRefVO, poolUuid,
UNIQUE(clusterUuid), CASCADE refs to ServerPoolVO), update the API/test plan
descriptions to reference the association table for lookups/creation/deletion
and adjust expected cascade/default behavior accordingly, and ensure examples
and QA steps reflect using ClusterServerPoolRefVO rather than a field on
ClusterVO.
| 原接口 `discoverHardware(String serverUuid, Completion completion)` 升级为带返回值的变体: | ||
|
|
||
| ```java | ||
| /** | ||
| * 主动触发硬件发现。统一层调用,RoleProvider 通过各自通道采集。 | ||
| * 返回标准化硬件信息,统一层合并后写入 HardwareInfoVO。 | ||
| * | ||
| * @param serverUuid PhysicalServerVO 的 UUID | ||
| * @param completion 完成回调,携带采集结果 | ||
| */ | ||
| void discover(String serverUuid, ReturnValueCompletion<HardwareDiscoveryResult> completion); | ||
| ``` | ||
|
|
||
| `HardwareDiscoveryResult` 字段与 PhysicalServerHardwareInfoVO 对齐,统一层按 OOB 优先策略合并各 Provider 结果后写入 VO。 |
There was a problem hiding this comment.
HardwareDiscoverable 在文档里出现了两套互不兼容的签名。
前面 3.3 节定义的是 discoverHardware(String, Completion),这里又改成 discover(String, ReturnValueCompletion<HardwareDiscoveryResult>)。如果不先统一哪一个是最终 SPI,各模块会按不同接口实现,最后无法对接。
🤖 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 `@docs/architecture-unified-hardware-2026-03-18.md` around lines 2431 - 2444,
The docs define two incompatible SPI signatures for HardwareDiscoverable
(discoverHardware(String, Completion) vs discover(String,
ReturnValueCompletion<HardwareDiscoveryResult>)); pick one final SPI and make
all docs and references consistent: decide whether the SPI should be
discoverHardware/Completion or
discover/ReturnValueCompletion<HardwareDiscoveryResult>, update the
HardwareDiscoverable definition and every mention of discoverHardware/discover,
Completion/ReturnValueCompletion, and
HardwareDiscoveryResult/PhysicalServerHardwareInfoVO so modules implement the
chosen method name and completion type, and ensure the section describing
return/merging behavior references the selected types (e.g.,
HardwareDiscoveryResult) and OOB merge policy consistently across the document
and any example code.
| public class ClusterServerPoolRefVO { | ||
| @Id | ||
| @Column | ||
| private long id; |
There was a problem hiding this comment.
ClusterServerPoolRefVO.id 的主键生成策略与 SQL 定义不一致
Line [350]-[353] 的 Java 片段把 id 定义为普通字段,但 Line [732]-[739] 的 SQL 是 AUTO_INCREMENT。按当前文档实现会出现持久化时主键来源不一致的问题。建议在实体示例里补上自增注解,和迁移脚本保持一致。
建议修正片段
`@Entity`
`@Table`(name = "ClusterServerPoolRefVO")
public class ClusterServerPoolRefVO {
`@Id`
`@Column`
+ `@GeneratedValue`(strategy = GenerationType.IDENTITY)
private long id;Also applies to: 732-739
🤖 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 `@docs/archive/2026-03-31-unified-hardware-management.md` around lines 350 -
353, The entity field ClusterServerPoolRefVO.id is defined as a plain long but
the SQL migration uses AUTO_INCREMENT, causing a mismatch; update the
ClusterServerPoolRefVO class to mark id as an auto-generated primary key (e.g.,
add the appropriate JPA generation annotation such as `@GeneratedValue` with
GenerationType.IDENTITY and keep `@Id/`@Column) so the entity auto-increments on
persist and matches the SQL migration; ensure the change is applied to the
ClusterServerPoolRefVO class where id is declared and to any similar VO classes
noted in the comment.
| ```java | ||
| @Entity | ||
| @Table(name = "PhysicalServerRoleVO", | ||
| uniqueConstraints = @UniqueConstraint(columnNames = {"serverUuid", "roleType"})) | ||
| public class PhysicalServerRoleVO extends ResourceVO { | ||
| @Column | ||
| private String serverUuid; // no DB FK, app-layer ref to PhysicalServerVO | ||
|
|
||
| @Column | ||
| @Enumerated(EnumType.STRING) | ||
| private ServerRoleType roleType; | ||
|
|
||
| @Column | ||
| private String roleUuid; // polymorphic ref, no DB FK | ||
|
|
||
| @Column | ||
| @Enumerated(EnumType.STRING) | ||
| private SchedulingMode schedulingMode; | ||
|
|
||
| @Column | ||
| private String roleStatus; // Active / Stale | ||
|
|
||
| // getters/setters | ||
| } |
There was a problem hiding this comment.
PhysicalServerRoleVO 字段定义缺少 clusterUuid,与后续查询示例冲突
Line [470]-[493] 的 PhysicalServerRoleVO 示例没有 clusterUuid,但 Line [1069]-[1071]、Line [1120]-[1122]、Line [1338]-[1344] 已将它作为一等过滤字段使用。这个不一致会让实现方按文档落地时直接编译/建模失败。
建议修正片段
public class PhysicalServerRoleVO extends ResourceVO {
`@Column`
private String serverUuid; // no DB FK, app-layer ref to PhysicalServerVO
+ `@Column`
+ `@ForeignKey`(parentEntityClass = ClusterEO.class, onDeleteAction = ReferenceOption.SET_NULL)
+ private String clusterUuid;
+
`@Column`
`@Enumerated`(EnumType.STRING)
private ServerRoleType roleType;Also applies to: 1053-1055, 1069-1071, 1120-1122, 1338-1344
🤖 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 `@docs/archive/2026-03-31-unified-hardware-management.md` around lines 470 -
493, The PhysicalServerRoleVO class definition is missing the clusterUuid field
used elsewhere; add a new member named clusterUuid to the PhysicalServerRoleVO
class (with `@Column` annotation and appropriate type String) so the entity
matches the queries that filter by clusterUuid; update getters/setters
(getClusterUuid/setClusterUuid) for caller code compatibility and ensure
annotations (e.g., `@Column`) mirror other fields like serverUuid to avoid
mapping/compilation errors.
| | Core Manager | `H:\ZStack\zstack\compute\src\main\java\org\zstack\compute\allocator\HostAllocatorManagerImpl.java` | | ||
| | Allocator Chain | `H:\ZStack\zstack\compute\src\main\java\org\zstack\compute\allocator\HostAllocatorChain.java` | | ||
| | Sortor Chain | `H:\ZStack\zstack\compute\src\main\java\org\zstack\compute\allocator\HostSortorChain.java` | | ||
| | Capacity VO | `H:\ZStack\zstack\header\src\main\java\org\zstack\header\allocator\HostCapacityVO.java` | | ||
| | Capacity Updater | `H:\ZStack\zstack\compute\src\main\java\org\zstack\compute\allocator\HostCapacityUpdater.java` | | ||
| | Reserve Manager | `H:\ZStack\zstack\compute\src\main\java\org\zstack\compute\allocator\HostCapacityReserveManagerImpl.java` | | ||
| | CPU Overprovisioning | `H:\ZStack\zstack\compute\src\main\java\org\zstack\compute\allocator\HostCpuOverProvisioningManagerImpl.java` | | ||
| | Memory Overprovisioning | `H:\ZStack\zstack\compute\src\main\java\org\zstack\compute\allocator\HostCapacityOverProvisioningManagerImpl.java` | | ||
| | Capacity Filter Flow | `H:\ZStack\zstack\compute\src\main\java\org\zstack\compute\allocator\HostCapacityAllocatorFlow.java` | | ||
| | VM Allocation Flow | `H:\ZStack\zstack\compute\src\main\java\org\zstack\compute\vm\VmAllocateHostForStoppedVmFlow.java` | | ||
| | Strategy Interface | `H:\ZStack\zstack\header\src\main\java\org\zstack\header\allocator\HostAllocatorStrategy.java` | | ||
| | Spec Class | `H:\ZStack\zstack\header\src\main\java\org\zstack\header\allocator\HostAllocatorSpec.java` | | ||
| | Constants | `H:\ZStack\zstack\header\src\main\java\org\zstack\header\allocator\HostAllocatorConstant.java` | | ||
| | Global Config | `H:\ZStack\zstack\compute\src\main\java\org\zstack\compute\allocator\HostAllocatorGlobalConfig.java` | |
There was a problem hiding this comment.
建议使用仓库相对路径替代 Windows 绝对路径
文件引用表中使用了 Windows 特定的绝对路径格式(H:\ZStack\zstack\...),这会导致以下问题:
- 跨平台兼容性差:Linux/Mac 系统无法识别 Windows 驱动器盘符
- 环境依赖性强:不同开发者的目录结构和盘符映射可能不同
- 文档可移植性差:这些路径在其他环境中无法直接使用
♻️ 建议改为仓库相对路径
| Category | File Path |
|----------|-----------|
-| Core Manager | `H:\ZStack\zstack\compute\src\main\java\org\zstack\compute\allocator\HostAllocatorManagerImpl.java` |
+| Core Manager | `compute/src/main/java/org/zstack/compute/allocator/HostAllocatorManagerImpl.java` |
-| Allocator Chain | `H:\ZStack\zstack\compute\src\main\java\org\zstack\compute\allocator\HostAllocatorChain.java` |
+| Allocator Chain | `compute/src/main/java/org/zstack/compute/allocator/HostAllocatorChain.java` |
-| Sortor Chain | `H:\ZStack\zstack\compute\src\main\java\org\zstack\compute\allocator\HostSortorChain.java` |
+| Sortor Chain | `compute/src/main/java/org/zstack/compute/allocator/HostSortorChain.java` |
-| Capacity VO | `H:\ZStack\zstack\header\src\main\java\org\zstack\header\allocator\HostCapacityVO.java` |
+| Capacity VO | `header/src/main/java/org/zstack/header/allocator/HostCapacityVO.java` |
-| Capacity Updater | `H:\ZStack\zstack\compute\src\main\java\org\zstack\compute\allocator\HostCapacityUpdater.java` |
+| Capacity Updater | `compute/src/main/java/org/zstack/compute/allocator/HostCapacityUpdater.java` |
-| Reserve Manager | `H:\ZStack\zstack\compute\src\main\java\org\zstack\compute\allocator\HostCapacityReserveManagerImpl.java` |
+| Reserve Manager | `compute/src/main/java/org/zstack/compute/allocator/HostCapacityReserveManagerImpl.java` |
-| CPU Overprovisioning | `H:\ZStack\zstack\compute\src\main\java\org\zstack\compute\allocator\HostCpuOverProvisioningManagerImpl.java` |
+| CPU Overprovisioning | `compute/src/main/java/org/zstack/compute/allocator/HostCpuOverProvisioningManagerImpl.java` |
-| Memory Overprovisioning | `H:\ZStack\zstack\compute\src\main\java\org\zstack\compute\allocator\HostCapacityOverProvisioningManagerImpl.java` |
+| Memory Overprovisioning | `compute/src/main/java/org/zstack/compute/allocator/HostCapacityOverProvisioningManagerImpl.java` |
-| Capacity Filter Flow | `H:\ZStack\zstack\compute\src\main\java\org\zstack\compute\allocator\HostCapacityAllocatorFlow.java` |
+| Capacity Filter Flow | `compute/src/main/java/org/zstack/compute/allocator/HostCapacityAllocatorFlow.java` |
-| VM Allocation Flow | `H:\ZStack\zstack\compute\src\main\java\org\zstack\compute\vm\VmAllocateHostForStoppedVmFlow.java` |
+| VM Allocation Flow | `compute/src/main/java/org/zstack/compute/vm/VmAllocateHostForStoppedVmFlow.java` |
-| Strategy Interface | `H:\ZStack\zstack\header\src\main\java\org\zstack\header\allocator\HostAllocatorStrategy.java` |
+| Strategy Interface | `header/src/main/java/org/zstack/header/allocator/HostAllocatorStrategy.java` |
-| Spec Class | `H:\ZStack\zstack\header\src\main\java\org\zstack\header\allocator\HostAllocatorSpec.java` |
+| Spec Class | `header/src/main/java/org/zstack/header/allocator/HostAllocatorSpec.java` |
-| Constants | `H:\ZStack\zstack\header\src\main\java\org\zstack\header\allocator\HostAllocatorConstant.java` |
+| Constants | `header/src/main/java/org/zstack/header/allocator/HostAllocatorConstant.java` |
-| Global Config | `H:\ZStack\zstack\compute\src\main\java\org\zstack\compute\allocator\HostAllocatorGlobalConfig.java` |
+| Global Config | `compute/src/main/java/org/zstack/compute/allocator/HostAllocatorGlobalConfig.java` |🤖 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 `@docs/archive/hardware-unified-management/ANALYSIS_compute_allocator.md`
around lines 496 - 509, Replace the Windows absolute paths in the file reference
table with repository-relative paths so the docs are cross-platform and
portable; for each listed entry (e.g., HostAllocatorManagerImpl,
HostAllocatorChain, HostSortorChain, HostCapacityVO, HostCapacityUpdater,
HostCapacityReserveManagerImpl, HostCpuOverProvisioningManagerImpl,
HostCapacityOverProvisioningManagerImpl, HostCapacityAllocatorFlow,
VmAllocateHostForStoppedVmFlow, HostAllocatorStrategy, HostAllocatorSpec,
HostAllocatorConstant, HostAllocatorGlobalConfig) change values like
"H:\ZStack\zstack\..." to repo-relative locations such as
"compute/src/main/java/org/zstack/compute/allocator/HostAllocatorManagerImpl.java"
(or the appropriate module-root-relative path), update all table rows
consistently, and verify links/rendering in the docs render correctly on
Linux/Mac.
There was a problem hiding this comment.
Actionable comments posted: 9
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
♻️ Duplicate comments (1)
compute/src/main/java/org/zstack/compute/zone/ZoneManagerImpl.java (1)
162-164:⚠️ Potential issue | 🟠 Major | ⚡ Quick win隔离
afterCreate扩展异常,避免“Zone 已创建但接口失败”Line [163] 在资源持久化后直接执行扩展点;一旦扩展抛异常,调用方可能收到失败,但 Zone 实际已创建。建议捕获并记录,避免把非关键扩展故障升级为主流程失败。
建议修复
ZoneInventory inventory = ZoneInventory.valueOf(finalVO); - extpEmitter.afterCreate(inventory); + try { + extpEmitter.afterCreate(inventory); + } catch (Exception e) { + logger.warn(String.format( + "zone afterCreate extension failed, continue create flow. zoneUuid:%s, error:%s", + inventory.getUuid(), e.getMessage() + )); + } return inventory;As per coding guidelines, “之前的代码产生的行为不要直接去改动……需要改动已有行为应做好回退准备”。
🤖 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 `@compute/src/main/java/org/zstack/compute/zone/ZoneManagerImpl.java` around lines 162 - 164, The call to extpEmitter.afterCreate(inventory) in ZoneManagerImpl is executed after persisting finalVO and if it throws, it will make callers think creation failed even though the Zone exists; wrap the extpEmitter.afterCreate(inventory) invocation in a try-catch that catches Throwable (or Exception), log the error with context (include inventory or finalVO id) via the class logger, and do not rethrow so the method still returns ZoneInventory.valueOf(finalVO); this isolates extension point failures from the main create flow while preserving existing behavior.
🟡 Minor comments (15)
docs/STATUS.md-86-92 (1)
86-92:⚠️ Potential issue | 🟡 Minor | ⚡ Quick win表格前后需补空行以通过 Markdown lint
Line 86 和 Line 164 的表格未满足“表格前后空行”规则(MD058)。建议在表格前后各补一行空行,避免 CI/lint 噪音。
Also applies to: 164-166
🤖 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 `@docs/STATUS.md` around lines 86 - 92, Add a blank line immediately before and after each Markdown table to satisfy MD058: locate the table that begins with the header string "| Doc | 状态 |" and insert one empty line above it and one empty line below it, and do the same for the second table referenced (the table around the snippet lines that include "|---|---|" and its surrounding rows) so both tables have a blank line before and after.docs/STATUS.md-56-56 (1)
56-56:⚠️ Potential issue | 🟡 Minor | ⚡ Quick win为围栏代码块补充语言标记
Line 56 的代码块未指定语言,触发 MD040。建议改为
text(或更贴切的语言),避免 lint 告警。建议修改
-``` +```text Phase 1 (v5.5.18 内部) — 骨架 [DONE — 2026-04 中] ├── Tasks 1-11: VO/CRUD/ServerPool/ProvisionNetwork/KvmRoleProvider stub/tests └── deliverable: PhysicalServer*VO 全家族 + RoleProvider SPI 接口 + 三家 stub implements @@ Phase 3+ (v1.1+) — Future [NOT PLANNED] ├── ServerAllocatorChain (R2 Group C) ├── Cross-role serialNumber 归一化 (AC-RS-13-P2) ├── HardwareDiscoveryStrategy SPI (现 3 private method) └── ProvisionAndAttachRole orchestrator API</details> <details> <summary>🤖 Prompt for AI Agents</summary>Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.In
@docs/STATUS.mdat line 56, The fenced code block around the "Phase 1...
Phase 3+" roadmap section is missing a language tag (causing MD040); update that
opening triple-backtick to include a language marker such as text (e.g., change
totext) so the block is explicitly marked and the linter warning is
resolved—look for the fenced block containing "Phase 1 (v5.5.18 内部) — 骨架" and
add the language identifier to its opening fence.</details> </blockquote></details> <details> <summary>header/src/main/java/org/zstack/header/server/APIDeleteProvisionNetworkMsg.java-20-20 (1)</summary><blockquote> `20-20`: _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_ **建议对 `uuid` 做 `trim` 标准化。** 当前直接赋值,若请求参数包含前后空白字符会导致后续资源匹配失败,排查成本高。 <details> <summary>建议修改</summary> ```diff - public void setUuid(String uuid) { this.uuid = uuid; } + public void setUuid(String uuid) { this.uuid = uuid == null ? null : uuid.trim(); } ``` </details> As per coding guidelines: “注意检查来自 Message 的参数是否做过 trim,用户可能在浏览器上复制粘贴的数据带有空格、换行符等”。 <details> <summary>🤖 Prompt for AI Agents</summary> ``` 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/server/APIDeleteProvisionNetworkMsg.java` at line 20, The setter setUuid in APIDeleteProvisionNetworkMsg assigns the incoming uuid directly; change it to normalize input by trimming whitespace and handling nulls (e.g., assign null if input is null, otherwise uuid.trim()) so downstream resource matching isn't broken by leading/trailing spaces; update the setUuid method to perform this normalization for the field uuid. ``` </details> </blockquote></details> <details> <summary>header/src/main/java/org/zstack/header/server/APIUpdateProvisionNetworkMsg.java-40-98 (1)</summary><blockquote> `40-98`: _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_ **建议在 setter 中统一 trim 字符串入参,避免空白字符引发脏数据或匹配失败。** 当前字符串字段均是直接赋值;API 入参若包含前后空格/换行,会把异常格式写入消息对象。建议在 setter 做统一归一化(至少 `uuid/name/description/dhcp*`)。 <details> <summary>可选修复示例</summary> ```diff public class APIUpdateProvisionNetworkMsg extends APIMessage { + private static String normalize(String value) { + return value == null ? null : value.trim(); + } + @@ public void setUuid(String uuid) { - this.uuid = uuid; + this.uuid = normalize(uuid); } @@ public void setName(String name) { - this.name = name; + this.name = normalize(name); } @@ public void setDescription(String description) { - this.description = description; + this.description = normalize(description); } @@ public void setDhcpInterface(String dhcpInterface) { - this.dhcpInterface = dhcpInterface; + this.dhcpInterface = normalize(dhcpInterface); } @@ public void setDhcpRangeStartIp(String dhcpRangeStartIp) { - this.dhcpRangeStartIp = dhcpRangeStartIp; + this.dhcpRangeStartIp = normalize(dhcpRangeStartIp); } @@ public void setDhcpRangeEndIp(String dhcpRangeEndIp) { - this.dhcpRangeEndIp = dhcpRangeEndIp; + this.dhcpRangeEndIp = normalize(dhcpRangeEndIp); } @@ public void setDhcpRangeNetmask(String dhcpRangeNetmask) { - this.dhcpRangeNetmask = dhcpRangeNetmask; + this.dhcpRangeNetmask = normalize(dhcpRangeNetmask); } @@ public void setDhcpRangeGateway(String dhcpRangeGateway) { - this.dhcpRangeGateway = dhcpRangeGateway; + this.dhcpRangeGateway = normalize(dhcpRangeGateway); } ``` </details> As per coding guidelines: 注意检查来自 Message 的参数是否做过 trim,用户可能在浏览器上复制粘贴的数据带有空格、换行符等。 <details> <summary>🤖 Prompt for AI Agents</summary> ``` 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/server/APIUpdateProvisionNetworkMsg.java` around lines 40 - 98, Trim incoming string parameters in the APIUpdateProvisionNetworkMsg setters to normalize values and avoid leading/trailing whitespace causing dirty data or matching failures; update setUuid, setName, setDescription and all dhcp-related setters (setDhcpInterface, setDhcpRangeStartIp, setDhcpRangeEndIp, setDhcpRangeNetmask, setDhcpRangeGateway) to null-check and call trim() on non-null inputs before assigning to the corresponding fields so stored values are normalized. ``` </details> </blockquote></details> <details> <summary>plugin/kvm/src/main/java/org/zstack/kvm/AddKVMHostMsg.java-51-52 (1)</summary><blockquote> `51-52`: _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_ **`serverUuid` 建议在 setter 中做 `trim()` 规范化。** Line 51-52 直接赋值会把前后空白带入后续匹配流程,容易触发“明明存在但关联失败”的边界问题。 <details> <summary>建议修改</summary> ```diff public void setServerUuid(String serverUuid) { - this.serverUuid = serverUuid; + this.serverUuid = serverUuid == null ? null : serverUuid.trim(); } ``` </details> As per coding guidelines, “注意检查来自 Message 的参数是否做过 trim,用户可能在浏览器上复制粘贴的数据带有空格、换行符等。” <details> <summary>🤖 Prompt for AI Agents</summary> ``` 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/kvm/src/main/java/org/zstack/kvm/AddKVMHostMsg.java` around lines 51 - 52, The setter AddKVMHostMsg.setServerUuid currently assigns the incoming serverUuid directly; change it to normalize input by trimming whitespace (and handling nulls) before storing to the serverUuid field so leading/trailing spaces/newlines won't break downstream matching; locate the setServerUuid method and replace the direct assignment with logic that does this.serverUuid = (serverUuid == null ? null : serverUuid.trim()). ``` </details> </blockquote></details> <details> <summary>plugin/kvm/src/main/java/org/zstack/kvm/APIAddKVMHostMsg.java-134-136 (1)</summary><blockquote> `134-136`: _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_ **建议在 `setServerUuid` 中做输入归一化(`trim`)。** 当前直接透传字符串,带前后空白时会导致后续按 UUID 查询/匹配异常,问题定位成本高。建议在 setter 内统一处理。 <details> <summary>建议修改</summary> ```diff public void setServerUuid(String serverUuid) { - this.serverUuid = serverUuid; + this.serverUuid = serverUuid == null ? null : serverUuid.trim(); } ``` </details> As per coding guidelines, “注意检查来自 Message 的参数是否做过 trim,用户可能在浏览器上复制粘贴的数据带有空格、换行符等”。 <details> <summary>🤖 Prompt for AI Agents</summary> ``` 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/kvm/src/main/java/org/zstack/kvm/APIAddKVMHostMsg.java` around lines 134 - 136, The setter setServerUuid should normalize input by trimming whitespace before assigning to the field to avoid lookup/matching failures; update setServerUuid to check for null and call trim() (or use a safe trim equivalent) and assign the trimmed value to this.serverUuid so inputs with leading/trailing spaces or newlines are normalized; ensure behavior preserves nulls (i.e., if incoming serverUuid is null, keep it null). ``` </details> </blockquote></details> <details> <summary>header/src/main/java/org/zstack/header/server/APIChangeClusterServerPoolMsg.java-11-11 (1)</summary><blockquote> `11-11`: _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_ **REST 路径资源名建议改为复数形式** 当前 `path = "/clusters/{clusterUuid}/server-pool/actions"` 使用了单数 `server-pool`,建议改为复数资源名以保持 API 一致性(例如 `server-pools`)。 As per coding guidelines "`@RestRequest` 的 `path`: 针对资源使用复数形式。" <details> <summary>🤖 Prompt for AI Agents</summary> ``` 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/server/APIChangeClusterServerPoolMsg.java` at line 11, The REST path in APIChangeClusterServerPoolMsg uses a singular resource segment ("server-pool") which violates the guideline to use plural resource names; update the `@RestRequest` annotation on class APIChangeClusterServerPoolMsg to change path = "/clusters/{clusterUuid}/server-pool/actions" to use the plural form "/clusters/{clusterUuid}/server-pools/actions" (adjust any related references/tests that construct or call this endpoint to the new path). ``` </details> </blockquote></details> <details> <summary>docs/runbooks/v5518-recalculate-perf.md-147-161 (1)</summary><blockquote> `147-161`: _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_ **为 fenced code block 补充语言标记** 当前代码块缺少语言标记,触发 MD040。 <details> <summary>建议修改</summary> ```diff -``` +```text ================================================================ PhysicalServerCapacityUpdater perf bench (AC-CM-PERF-01) ================================================================ Hosts: 1000 @@ batch wall: ~50 ms (target < 5000 ms) ================================================================ ``` ``` </details> <details> <summary>🤖 Prompt for AI Agents</summary>Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.In
@docs/runbooks/v5518-recalculate-perf.mdaround lines 147 - 161, Add a
language tag to the fenced code block containing the
"PhysicalServerCapacityUpdater perf bench (AC-CM-PERF-01)" output (the block
that begins with the line of === characters) by changing the opening ``` toleave the block content unchanged aside from the added "text" tag.header/src/main/java/org/zstack/header/server/PhysicalServerRoleProvider.java-15-17 (1)
15-17:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winJavadoc 中硬件发现 SPI 名称与当前代码不一致
这里写的是
HardwareDiscoveryStrategy,建议改为当前真实接口名,减少接入方误解。建议修改
- * Hardware discovery is <b>not</b> part of this SPI — see - * {`@code` HardwareDiscoveryStrategy} (role SPI PRD §2.5b) for the dedicated hardware-discovery SPI. + * Hardware discovery is <b>not</b> part of this SPI — see + * {`@link` PhysicalServerHardwareDiscoveryExtensionPoint} (role SPI PRD §2.5b) for the dedicated hardware-discovery SPI.🤖 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/server/PhysicalServerRoleProvider.java` around lines 15 - 17, Update the Javadoc in PhysicalServerRoleProvider to reference the actual hardware-discovery SPI interface name used in code rather than HardwareDiscoveryStrategy; find the Javadoc block in class PhysicalServerRoleProvider and replace the incorrect {`@code` HardwareDiscoveryStrategy} reference with the real interface/type name (the actual hardware discovery SPI class/interface used in the project) so consumers aren't misled.header/src/main/java/org/zstack/header/server/APIUpdateServerPoolMsg.java-31-65 (1)
31-65:⚠️ Potential issue | 🟡 Minor | ⚡ Quick win建议对可编辑字符串做
trim(),避免脏输入进入后续流程当前 setter 直接赋值,用户复制粘贴带空格/换行时会把噪音字符持久化。
建议修改
public void setUuid(String uuid) { - this.uuid = uuid; + this.uuid = uuid == null ? null : uuid.trim(); } @@ public void setName(String name) { - this.name = name; + this.name = name == null ? null : name.trim(); } @@ public void setDescription(String description) { - this.description = description; + this.description = description == null ? null : description.trim(); } @@ public void setPhysicalLocation(String physicalLocation) { - this.physicalLocation = physicalLocation; + this.physicalLocation = physicalLocation == null ? null : physicalLocation.trim(); } @@ public void setNetworkTopology(String networkTopology) { - this.networkTopology = networkTopology; + this.networkTopology = networkTopology == null ? null : networkTopology.trim(); }As per coding guidelines "注意检查来自 Message 的参数是否做过 trim,用户可能在浏览器上复制粘贴的数据带有空格、换行符等".
🤖 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/server/APIUpdateServerPoolMsg.java` around lines 31 - 65, Trim incoming editable string parameters in the setters to avoid persisting leading/trailing whitespace: update setName, setDescription, setPhysicalLocation and setNetworkTopology to assign the trimmed value (e.g., name = (name == null ? null : name.trim())) so nulls are preserved and only non-null inputs are trimmed; apply the same pattern if setUuid should also normalize input.docs/runbooks/v5518-recalculate-perf.md-135-137 (1)
135-137:⚠️ Potential issue | 🟡 Minor | ⚡ Quick win修复表格中未转义的管道符,避免列数错位
该行行内代码里的
|会被 Markdown 表格当成分隔符,触发列数不一致告警。建议修改
-| EXPLAIN: every hot-path query `type=const|ref|eq_ref` | yes | §U17 spec ("type=ref/eq_ref, rows=1, 索引命中") | +| EXPLAIN: every hot-path query `type=const\|ref\|eq_ref` | yes | §U17 spec ("type=ref/eq_ref, rows=1, 索引命中") |🤖 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 `@docs/runbooks/v5518-recalculate-perf.md` around lines 135 - 137, Table cells are breaking because unescaped pipe characters in inline code are treated as column separators; update the table rows containing "EXPLAIN: every hot-path query `type=const|ref|eq_ref`" (and any other inline code with `|`) to escape the pipe characters (e.g., change `type=const|ref|eq_ref` to `type=const\|ref\|eq_ref`) or replace the backtick code spans with HTML <code>…</code> so the `|` are not interpreted as column delimiters; ensure the row "EXPLAIN: no `Using filesort` / `Using temporary` / `type=ALL` on hot path" remains intact if it contains no pipes.plugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerIpmiPowerExecutor.java-70-81 (1)
70-81:⚠️ Potential issue | 🟡 Minor | ⚡ Quick win补充 OOB 端口范围校验
建议在校验阶段拒绝非法端口(<=0 或 >65535),避免执行阶段才失败。
建议修改
private void validate(PhysicalServerVO server) { if (!hasOobCredentials(server)) { throw new OperationFailureException(operr( "OOB credentials not configured for PhysicalServer[uuid:%s]", server == null ? null : server.getUuid())); } + if (server.getOobPort() != null && (server.getOobPort() <= 0 || server.getOobPort() > 65535)) { + throw new OperationFailureException(operr( + "invalid OOB port[%s] for PhysicalServer[uuid:%s]", + server.getOobPort(), server.getUuid())); + } if (server.getOobManagementType() != null && !"IPMI".equals(server.getOobManagementType())) { throw new OperationFailureException(operr( "unsupported OOB management type[%s] for PhysicalServer[uuid:%s]", server.getOobManagementType(), server.getUuid())); }🤖 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/physicalServer/src/main/java/org/zstack/server/PhysicalServerIpmiPowerExecutor.java` around lines 70 - 81, In validate(PhysicalServerVO server) add a guard that checks the server's OOB port (e.g., server.getOobPort()) and throws an OperationFailureException via operr when the port is <= 0 or > 65535; place this check in the validate method (alongside hasOobCredentials and getOobManagementType checks) so invalid ports are rejected early with a clear message referencing the invalid port and the server UUID.plugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerScanner.java-155-158 (1)
155-158:⚠️ Potential issue | 🟡 Minor | ⚡ Quick win
sshPort解析缺少错误处理,可能抛出 NumberFormatException。Line 158 的
Integer.parseInt(portStr)在portStr非数字时会抛出未捕获的NumberFormatException,导致用户看到原始异常而非友好的错误提示。🛡️ 建议添加解析异常处理
此问题位于
KvmRoleProvider.javaLine 158,但此处PhysicalServerScanner.java中无此问题。扫描器的oobPort和timeoutPerHost都是Integer类型,由框架处理。🤖 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/physicalServer/src/main/java/org/zstack/server/PhysicalServerScanner.java` around lines 155 - 158, The code calls Integer.parseInt(portStr) without handling NumberFormatException, so wrap the parse/validation of sshPort (the parseInt(portStr) call) in a try-catch that catches NumberFormatException, log a clear message and rethrow a user-friendly IllegalArgumentException (or BadRequest) that includes the invalid value (portStr) and expected numeric range (1-65535); also validate the parsed int is within 1-65535 and throw the same friendly error if out of range. Ensure you update the method containing parseInt(portStr) and reference the sshPort/portStr variable so callers get a readable error instead of a raw NumberFormatException.plugin/kvm/src/main/java/org/zstack/kvm/KvmRoleProvider.java-155-159 (1)
155-159:⚠️ Potential issue | 🟡 Minor | ⚡ Quick win
sshPort解析缺少错误处理,NumberFormatException可能逃逸。当
roleConfig.sshPort包含非数字字符(如"22a"或用户误粘贴的带空格值" 22 ")时,Integer.parseInt()会抛出NumberFormatException,用户将看到原始堆栈而非业务错误码。🛡️ 建议添加解析异常处理
int sshPort = 22; String portStr = cfg.get("sshPort"); if (portStr != null && !portStr.isEmpty()) { - sshPort = Integer.parseInt(portStr); + try { + sshPort = Integer.parseInt(portStr.trim()); + } catch (NumberFormatException e) { + throw new OperationFailureException( + operr(ORG_ZSTACK_KVM_10166, + "roleConfig 'sshPort' must be a valid integer, got: %s", portStr)); + } }As per coding guidelines: "注意检查来自 Message 的参数是否做过 trim,用户可能在浏览器上复制粘贴的数据带有空格、换行符等"
🤖 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/kvm/src/main/java/org/zstack/kvm/KvmRoleProvider.java` around lines 155 - 159, Trim the raw value from cfg.get("sshPort") and wrap the Integer.parseInt call in a try-catch to handle NumberFormatException in KvmRoleProvider: read String portStr = cfg.get("sshPort"); if (portStr != null) portStr = portStr.trim(); then try { sshPort = Integer.parseInt(portStr); } catch (NumberFormatException e) { log a clear, business-friendly error (including the invalid value) and either fall back to the default sshPort or throw a controlled runtime/validation exception (e.g., CloudRuntimeException or a BadRequest-style exception) instead of letting the raw stack escape; ensure the log/exception references the offending value and the config key ("sshPort").plugin/physicalServer/src/main/java/org/zstack/server/ProvisionPhysicalServerLongJob.java-56-65 (1)
56-65:⚠️ Potential issue | 🟡 Minor | ⚡ Quick win
parsePhase()方法缺少对无效 phase 值的错误处理。
ProvisionPhase.valueOf()在 phase 字符串无效时会抛出IllegalArgumentException,该异常会向上传播导致 LongJob 异常失败。建议添加防御性处理以优雅地处理无效数据。建议添加错误处理
private ProvisionPhase parsePhase(String jobData) { if (jobData == null || jobData.isEmpty()) { return ProvisionPhase.NotStarted; } - JsonObject obj = new JsonParser().parse(jobData).getAsJsonObject(); - if (obj.has("phase") && !obj.get("phase").isJsonNull()) { - return ProvisionPhase.valueOf(obj.get("phase").getAsString()); + try { + JsonObject obj = new JsonParser().parse(jobData).getAsJsonObject(); + if (obj.has("phase") && !obj.get("phase").isJsonNull()) { + return ProvisionPhase.valueOf(obj.get("phase").getAsString()); + } + } catch (IllegalArgumentException e) { + logger.warn(String.format("invalid phase value in jobData, falling back to NotStarted: %s", e.getMessage())); + } catch (Exception e) { + logger.warn(String.format("failed to parse phase from jobData, falling back to NotStarted: %s", e.getMessage())); } return ProvisionPhase.NotStarted; }另外,项目使用的 Gson 2.8.9 中,
new JsonParser()已被标记为 deprecated。建议在全局模块升级时使用JsonParser.parseString()静态方法替代。🤖 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/physicalServer/src/main/java/org/zstack/server/ProvisionPhysicalServerLongJob.java` around lines 56 - 65, parsePhase currently calls ProvisionPhase.valueOf(...) without handling invalid phase strings and uses deprecated new JsonParser(); update parsePhase to catch IllegalArgumentException (and NullPointerException if desired) around the valueOf call and return ProvisionPhase.NotStarted while logging or recording a warning when the input is invalid, and replace new JsonParser().parse(jobData) with the Gson 2.8.9 static API JsonParser.parseString(jobData) to avoid deprecation; reference the parsePhase method and the ProvisionPhase enum when making these changes.
🧹 Nitpick comments (18)
header/src/main/java/org/zstack/header/server/PhysicalServerProvisionNetworkClusterRefVO.java (1)
19-25: ⚡ Quick win建议为关联外键列显式声明非空约束
networkUuid和clusterUuid是关联主键字段,建议在 Line 19 和 Line 23 显式加nullable = false,避免空值在 ORM 层静默进入流程,把错误延后到数据库层才暴露。建议修改
- `@Column` + `@Column`(nullable = false) `@ForeignKey`(parentEntityClass = PhysicalServerProvisionNetworkVO.class, onDeleteAction = ReferenceOption.CASCADE) private String networkUuid; - `@Column` + `@Column`(nullable = false) `@ForeignKey`(parentEntityClass = ClusterEO.class, onDeleteAction = ReferenceOption.CASCADE) private String clusterUuid;🤖 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/server/PhysicalServerProvisionNetworkClusterRefVO.java` around lines 19 - 25, The `@Column` annotations on the entity PhysicalServerProvisionNetworkClusterRefVO for the fields networkUuid and clusterUuid should explicitly declare non-nullability to prevent nulls at the ORM level; update the `@Column` on the networkUuid field and the `@Column` on the clusterUuid field to include nullable = false (keeping the existing `@ForeignKey` annotations and ReferenceOption.CASCADE unchanged) so the ORM enforces not-null before hitting the database.header/src/main/java/org/zstack/header/zone/ZoneCreateExtensionPoint.java (1)
4-4: ⚡ Quick win
ZoneCreateExtensionPoint的方法缺少契约性 Javadoc。Line 4 是 SPI 回调入口,建议补充调用时机、参数语义和异常约束说明,避免实现方理解偏差。
建议修改
public interface ZoneCreateExtensionPoint { + /** + * Invoked after a zone is created and converted to inventory. + * + * `@param` inventory the created zone inventory + */ void afterCreateZone(ZoneInventory inventory); }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/zone/ZoneCreateExtensionPoint.java` at line 4, Add a Javadoc block to the SPI method afterCreateZone in ZoneCreateExtensionPoint that documents the callback timing (when a ZoneInventory is created), the parameter semantics (what fields in ZoneInventory callers can rely on), thread/context guarantees, and exception behavior (whether implementations may throw runtime exceptions or must handle errors and how they affect the caller flow); mention this is an SPI callback entry and avoid adding extra modifiers to the interface method; reference the method name afterCreateZone and the ZoneInventory parameter in the comment so implementers know exactly when and how to implement it.header/src/main/java/org/zstack/header/cluster/ClusterAO.java (1)
49-50: ⚡ Quick win建议为
serverPoolUuid增加索引,避免按池过滤退化。Line 49-50 新增字段看起来会作为池维度查询/批量更新条件,缺少索引时容易出现全表扫描。
建议修改
`@Column` + `@Index` private String serverPoolUuid;🤖 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/cluster/ClusterAO.java` around lines 49 - 50, ClusterAO adds a new field serverPoolUuid but lacks a DB index; add an index annotation to the serverPoolUuid field in class ClusterAO (e.g. apply the project's index/unique annotation used for other AOs) and create the corresponding schema migration to create the database index so queries/updates that filter by serverPoolUuid do not cause full table scans; locate the serverPoolUuid declaration in ClusterAO and mirror the indexing pattern used by other fields (or other *AO classes) to ensure consistency.header/src/main/java/org/zstack/header/cluster/ClusterCreateExtensionPoint.java (1)
4-4: ⚡ Quick win
ClusterCreateExtensionPoint回调方法建议补充 Javadoc。Line 4 作为扩展点契约,建议明确调用时机和参数约束,降低插件实现歧义。
建议修改
public interface ClusterCreateExtensionPoint { + /** + * Invoked after a cluster is created. + * + * `@param` cluster the created cluster entity + */ void afterCreateCluster(ClusterVO cluster); }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/cluster/ClusterCreateExtensionPoint.java` at line 4, Add a Javadoc to the ClusterCreateExtensionPoint#afterCreateCluster(ClusterVO cluster) method that clearly states when the callback is invoked (e.g., immediately after a ClusterVO is persisted/created), whether the cluster argument can be null or mutated, threading/transaction context (runs synchronously/asynchronously and whether it runs inside the creation DB transaction), and any expectations for implementors (side-effects allowed, exceptions handling). Keep the interface method signature free of extra modifiers and place the Javadoc directly above the void afterCreateCluster(ClusterVO cluster) declaration, referencing the ClusterVO parameter and any lifecycle/ordering guarantees.header/src/main/java/org/zstack/header/server/ProvisionProvider.java (1)
7-13: ⚡ Quick winSPI 接口方法缺少 Javadoc
ProvisionProvider的方法目前没有契约说明。建议为每个方法补充简要 Javadoc(参数语义、回调时机、失败传递约定),降低后续接入歧义。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/server/ProvisionProvider.java` around lines 7 - 13, Add Javadoc comments to the ProvisionProvider interface and each method (getType, prepareNetwork, destroyNetwork, startProvisioning) describing the contract: what the method does, parameter semantics (e.g., meaning of PhysicalServerProvisionNetworkInventory network, poolUuid, ProvisionRequest request), when the Completion or ReturnValueCompletion callbacks are invoked (success vs failure), what errors are propagated and how failures should be signaled via Completion.error or ReturnValueCompletion.fail, and any threading or reentrancy expectations; also ensure no extra modifiers are added to interface methods per coding guidelines.header/src/main/java/org/zstack/header/server/APIProvisionPhysicalServerMsg.java (1)
16-24: 💤 Low value建议添加 Javadoc 注释标注
@RestResponse。根据编码规范,API 消息类的返回类需要在注释中用
@RestResponse进行标注,以便生成 API 文档。♻️ 建议的修改
`@Action`(adminOnly = true, category = PhysicalServerConstant.ACTION_CATEGORY) `@RestRequest`( path = "/physical-servers/{serverUuid}/provision", method = HttpMethod.POST, parameterName = "params", responseClass = APIProvisionPhysicalServerEvent.class ) `@DefaultTimeout`(timeunit = TimeUnit.HOURS, value = 12) +/** + * `@RestResponse`(allTo = "inventory") + */ public class APIProvisionPhysicalServerMsg extends APICreateLongJobMessage {🤖 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/server/APIProvisionPhysicalServerMsg.java` around lines 16 - 24, 在 APIProvisionPhysicalServerMsg 类声明上方添加 Javadoc 注释并标注 `@RestResponse` 指向 APIProvisionPhysicalServerEvent,以满足编码规范并使 API 文档生成正确;即在 class APIProvisionPhysicalServerMsg(继承 APICreateLongJobMessage)声明上方增加类似的 Javadoc 注解并包含 `@RestResponse`(APIProvisionPhysicalServerEvent.class) 说明返回类型。header/src/main/java/org/zstack/header/server/APICreateProvisionNetworkMsg.java (1)
10-17: 💤 Low value建议添加 Javadoc 注释标注
@RestResponse。与
APIProvisionPhysicalServerMsg相同,建议添加@RestResponse注释以符合 API 文档生成规范。♻️ 建议的修改
`@Action`(adminOnly = true, category = PhysicalServerConstant.ACTION_CATEGORY) `@RestRequest`( path = "/provision-networks", method = HttpMethod.POST, parameterName = "params", responseClass = APICreateProvisionNetworkEvent.class ) +/** + * `@RestResponse`(allTo = "inventory") + */ public class APICreateProvisionNetworkMsg extends APICreateMessage {🤖 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/server/APICreateProvisionNetworkMsg.java` around lines 10 - 17, 在类 APICreateProvisionNetworkMsg 上添加与 APIProvisionPhysicalServerMsg 相同格式的 Javadoc 注释以标注 `@RestResponse`;具体地,在类定义上方加入一行 Javadoc 包含 `@RestResponse`(APICreateProvisionNetworkEvent.class) 来使 API 文档生成器识别响应类型(保持现有 `@RestRequest` 和 responseClass 对应),以确保接口文档规范一致。header/src/main/java/org/zstack/header/server/CreateRoleEntityContext.java (1)
57-59: ⚡ Quick win
setRoleConfig建议做 defensive copy,避免外部引用污染上下文当前直接持有调用方传入的
Map,外部后续修改会影响内部状态。建议修改
public CreateRoleEntityContext setRoleConfig(Map<String, String> roleConfig) { - this.roleConfig = roleConfig == null ? new HashMap<>() : roleConfig; + this.roleConfig = roleConfig == null ? new HashMap<>() : new HashMap<>(roleConfig); return this; }🤖 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/server/CreateRoleEntityContext.java` around lines 57 - 59, The setter setRoleConfig currently stores the caller's Map reference directly, allowing external mutation to affect the context; change it to make a defensive copy: in CreateRoleEntityContext.setRoleConfig(Map<String,String> roleConfig) assign this.roleConfig = roleConfig == null ? new HashMap<>() : new HashMap<>(roleConfig) (or Collections.unmodifiableMap(new HashMap<>(roleConfig)) if immutability is desired) and then return this, so the internal field roleConfig is not the original external map.plugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerApiInterceptor.java (3)
44-47: ⚡ Quick win建议在 IP 校验前做
trim(),提升用户体验。用户从浏览器复制粘贴 IP 时可能带有前导/尾随空格(如
" 192.168.1.100"),当前代码会直接报invalid managementIp,用户可能困惑为何格式正确的 IP 被拒绝。♻️ 建议的改进
private void validate(APICreatePhysicalServerMsg msg) { - if (!NetworkUtils.isIpv4Address(msg.getManagementIp())) { + String ip = msg.getManagementIp() != null ? msg.getManagementIp().trim() : null; + if (ip == null || !NetworkUtils.isIpv4Address(ip)) { throw new ApiMessageInterceptionException(argerr("invalid managementIp[%s]", msg.getManagementIp())); }As per coding guidelines: "注意检查来自 Message 的参数是否做过 trim,用户可能在浏览器上复制粘贴的数据带有空格、换行符等"
🤖 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/physicalServer/src/main/java/org/zstack/server/PhysicalServerApiInterceptor.java` around lines 44 - 47, In validate(...) inside PhysicalServerApiInterceptor (the APICreatePhysicalServerMsg validation), trim the management IP before validating: obtain a trimmed string from msg.getManagementIp(), use that trimmed value when calling NetworkUtils.isIpv4Address and in the error message, and update the message (e.g., setManagementIp) or otherwise ensure the trimmed value is used downstream so leading/trailing whitespace from the user is ignored.
67-80: 💤 Low valueProvision Network 的 DHCP 字段校验同样应考虑
trim()。
dhcpRangeStartIp、dhcpRangeEndIp、dhcpRangeNetmask、dhcpRangeGateway都是用户输入,建议统一处理前后空格。🤖 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/physicalServer/src/main/java/org/zstack/server/PhysicalServerApiInterceptor.java` around lines 67 - 80, The validate method in PhysicalServerApiInterceptor doesn't trim user input for DHCP fields before IPv4 validation; update validate(APICreateProvisionNetworkMsg msg) to trim each of msg.getDhcpRangeStartIp(), getDhcpRangeEndIp(), getDhcpRangeNetmask(), and getDhcpRangeGateway() (e.g., read, check for null, call .trim()) and perform NetworkUtils.isIpv4Address against the trimmed values, and also set the trimmed strings back on the APICreateProvisionNetworkMsg so the message is normalized for further processing.
61-65: ⚡ Quick win同上,
UpdatePhysicalServerMsg的managementIp校验也应先trim()。♻️ 建议的改进
private void validate(APIUpdatePhysicalServerMsg msg) { - if (msg.getManagementIp() != null && !NetworkUtils.isIpv4Address(msg.getManagementIp())) { + if (msg.getManagementIp() != null && !NetworkUtils.isIpv4Address(msg.getManagementIp().trim())) { throw new ApiMessageInterceptionException(argerr("invalid managementIp[%s]", msg.getManagementIp())); } }🤖 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/physicalServer/src/main/java/org/zstack/server/PhysicalServerApiInterceptor.java` around lines 61 - 65, 在 validate(APIUpdatePhysicalServerMsg msg) 中,对 msg.getManagementIp() 进行非空检查前先调用 trim(),即使用一个局部字符串如 managementIp = msg.getManagementIp() != null ? msg.getManagementIp().trim() : null,然后用 NetworkUtils.isIpv4Address(managementIp) 做校验并在校验失败时继续抛出 ApiMessageInterceptionException(argerr(...)),以确保空格不会导致合法 IP 被误判;请更新该方法中的相关引用(getManagementIp() 调用处)为该局部变量。plugin/kvm/src/test/java/org/zstack/kvm/KvmRoleProviderTest.java (2)
7-7: 💤 Low value移除未使用的导入
HttpEntity。
org.springframework.http.HttpEntity在测试类中未被使用。🤖 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/kvm/src/test/java/org/zstack/kvm/KvmRoleProviderTest.java` at line 7, The import org.springframework.http.HttpEntity in KvmRoleProviderTest is unused; remove this unused import declaration from the top of the file (delete the import line for HttpEntity) so the test class compiles without unused-import warnings.
459-464: 💤 Low value移除未使用的方法
noopRvComp()。该方法已定义但在整个测试类中未被调用,属于死代码。
🤖 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/kvm/src/test/java/org/zstack/kvm/KvmRoleProviderTest.java` around lines 459 - 464, Remove the unused dead method noopRvComp() from the KvmRoleProviderTest class: locate the private static ReturnValueCompletion<String> noopRvComp() declaration and delete the entire method body and signature to eliminate dead code and keep the test class clean.plugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerProvisionService.java (1)
208-219: ⚡ Quick win
providers()方法在每次调用时重建 Map,且重复检查应在启动时执行。当前实现存在两个问题:
性能问题:每次调用
startProvisioning()都会遍历providerList重建Map,虽然列表通常较小,但这是不必要的重复工作。延迟错误检测:如果存在重复的
ProvisionProvider,异常会在运行时(provisioning 请求期间)抛出,而不是在应用启动时。这会导致难以调试的问题。建议在 Bean 初始化时(使用
@PostConstruct)构建并缓存 provider map:♻️ 建议的重构
+import javax.annotation.PostConstruct; + public class PhysicalServerProvisionService { private static final CLogger logger = Utils.getLogger(PhysicalServerProvisionService.class); private static final Pattern MAC_PATTERN = Pattern.compile("(?i)([0-9a-f]{2}(:[0-9a-f]{2}){5})"); `@Autowired` private DatabaseFacade dbf; `@Autowired`(required = false) private List<ProvisionProvider> providerList = Collections.emptyList(); + private Map<ProvisionNetworkType, ProvisionProvider> providerMap; + + `@PostConstruct` + public void init() { + providerMap = new HashMap<>(); + for (ProvisionProvider provider : providerList) { + ProvisionProvider old = providerMap.put(provider.getType(), provider); + if (old != null) { + throw new CloudRuntimeException(String.format( + "duplicate ProvisionProvider for ProvisionNetworkType[%s]: %s and %s", + provider.getType().toString(), old.getClass().getName(), provider.getClass().getName())); + } + } + } // In startProvisioning(): - ProvisionProvider provider = providers().get(network.getType()); + ProvisionProvider provider = providerMap.get(network.getType());🤖 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/physicalServer/src/main/java/org/zstack/server/PhysicalServerProvisionService.java` around lines 208 - 219, The providers() method rebuilds the provider map on every call and defers duplicate detection until runtime; instead, add a cached Map<ProvisionNetworkType, ProvisionProvider> field in PhysicalServerProvisionService, populate and validate it once in a `@PostConstruct` method (iterate providerList, detect duplicates and throw the same OperationFailureException if found), and change providers() to return the cached map (or remove callers to use the cached field directly), ensuring startProvisioning() uses the prebuilt map rather than reconstructing it each time.header/src/main/java/org/zstack/header/server/PhysicalServerProvisionDataPlane.java (1)
5-9: ⚡ Quick win接口方法缺少 Javadoc 注释。
根据编码规范,接口方法必须配有有效的 Javadoc 注释。建议为
getType()和provision(...)方法添加文档说明,描述参数含义、返回值以及回调行为。📝 建议添加 Javadoc
public interface PhysicalServerProvisionDataPlane { + /** + * Returns the provision network type this data-plane supports. + * + * `@return` the supported {`@link` ProvisionNetworkType} + */ ProvisionNetworkType getType(); + /** + * Initiates provisioning for the given target starting from the specified phase. + * + * `@param` target the provisioning target containing server and network details + * `@param` startPhase the phase from which provisioning should begin + * `@param` completion callback invoked upon completion or failure + */ void provision(PhysicalServerProvisionTarget target, ProvisionPhase startPhase, Completion completion); }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/server/PhysicalServerProvisionDataPlane.java` around lines 5 - 9, 为接口 PhysicalServerProvisionDataPlane 的方法补充 Javadoc:在 getType() 上添加一段简短注释说明该方法返回的 ProvisionNetworkType 的含义和使用场景;在 provision(PhysicalServerProvisionTarget target, ProvisionPhase startPhase, Completion completion) 上添加注释描述每个参数的含义(PhysicalServerProvisionTarget、ProvisionPhase)、方法的行为(启动哪个阶段的部署/配置)以及 Completion 回调的语义(何时调用、成功/失败约定和异常处理)并注明返回值(若无则说明为 void)。确保注释清晰、简洁,遵循现有编码规范格式即可。plugin/physicalServer/src/main/java/org/zstack/server/hardware/HardwareDiscoveryScheduler.java (1)
53-61: 💤 Low value建议在
destroy()中等待任务终止当前的
shutdown()仅发起关闭请求,但不等待已提交任务完成。在管理节点关闭期间,正在进行的硬件发现任务可能会被中断,导致日志中出现异常或数据不一致。♻️ 建议的改进
`@PreDestroy` public void destroy() { if (executor != null) { executor.shutdown(); + try { + if (!executor.awaitTermination(5, TimeUnit.SECONDS)) { + executor.shutdownNow(); + } + } catch (InterruptedException e) { + executor.shutdownNow(); + Thread.currentThread().interrupt(); + } } if (timeoutExecutor != null) { timeoutExecutor.shutdown(); + try { + if (!timeoutExecutor.awaitTermination(5, TimeUnit.SECONDS)) { + timeoutExecutor.shutdownNow(); + } + } catch (InterruptedException e) { + timeoutExecutor.shutdownNow(); + Thread.currentThread().interrupt(); + } } }🤖 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/physicalServer/src/main/java/org/zstack/server/hardware/HardwareDiscoveryScheduler.java` around lines 53 - 61, The destroy() currently calls executor.shutdown() and timeoutExecutor.shutdown() but does not wait for tasks to finish; update destroy() to call shutdown(), then await termination for both executor and timeoutExecutor (use executor.awaitTermination with a reasonable timeout), handle InterruptedException by restoring the thread interrupt status and, if necessary, call shutdownNow() to cancel remaining tasks; reference the existing destroy(), executor, and timeoutExecutor symbols and ensure both executors are properly awaited and cleaned up before method returns.plugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerPathTwoOrchestrator.java (1)
107-115: 💤 Low value
classify()被调用了两次,可以优化
findOwningProvider内部调用p.classify(hvo).isPresent()找到 provider 后,第 112 行又调用了一次owner.classify(hvo)。如果classify是幂等的(应该是),这只是轻微的性能浪费;但如果classify有副作用或开销较大,建议缓存结果。♻️ 建议的优化
可以让
findOwningProvider返回一个包含 provider 和 roleType 的复合对象,避免重复调用:private static class ProviderMatch { final PhysicalServerRoleProvider provider; final ServerRoleType roleType; ProviderMatch(PhysicalServerRoleProvider p, ServerRoleType t) { this.provider = p; this.roleType = t; } } private Optional<ProviderMatch> findOwningProvider(HostVO hvo) { for (PhysicalServerRoleProvider p : pluginRgty.getExtensionList(PhysicalServerRoleProvider.class)) { Optional<ServerRoleType> type = p.classify(hvo); if (type.isPresent()) { return Optional.of(new ProviderMatch(p, type.get())); } } return Optional.empty(); }🤖 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/physicalServer/src/main/java/org/zstack/server/PhysicalServerPathTwoOrchestrator.java` around lines 107 - 115, The code calls owner.classify(hvo) twice; change findOwningProvider to return both the matched PhysicalServerRoleProvider and its computed ServerRoleType (e.g., a small ProviderMatch {PhysicalServerRoleProvider provider; ServerRoleType roleType}) so you can avoid re-invoking classify; update the call site in PhysicalServerPathTwoOrchestrator to use the returned ProviderMatch (use match.provider where owner was used and match.roleType instead of owner.classify(hvo)) when calling doAppendFlows (keeping owner.getSchedulingMode(), hvo.getUuid(), preResolvedServerUuid, matchCtx, clusterUuid).plugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerManagerImpl.java (1)
150-153: ⚖️ Poor tradeoffMN 启动时全量扫描 PhysicalServerVO 可能影响大规模部署
当前实现在 MN 启动时查询所有
PhysicalServerVO的 UUID 并逐一入队发现任务。在拥有数千台物理服务器的大规模部署中,这会导致启动时大量任务涌入调度器。如 TODO 注释所述,建议增加过滤条件排除已发现硬件信息的服务器。此外,考虑分批入队或延迟启动以减轻启动时的压力。
🤖 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/physicalServer/src/main/java/org/zstack/server/PhysicalServerManagerImpl.java` around lines 150 - 153, The current startup code selects all PhysicalServerVO UUIDs and enqueues them all, which can flood the scheduler; update the query that builds orphanServerUuids (using PhysicalServerVO and PhysicalServerAO_.uuid) to filter out servers that already have discovered hardware info (e.g., where hardwareInfo/discovered flag is null/false) and then enqueue in controlled batches instead of all at once: iterate the result in chunks (configurable batch size) and call hardwareDiscoveryScheduler.enqueueDiscovery for each UUID with a small inter-batch delay or schedule (to spread load), or use a delayed/scheduled enqueue API if available; ensure you update the orphanServerUuids usage to a batched loop and preserve existing semantics of hardwareDiscoveryScheduler.enqueueDiscovery.
| try { | ||
| if (vo.getRoles() != null && !vo.getRoles().isEmpty()) { | ||
| inv.setRoles(PhysicalServerRoleInventory.valueOf(vo.getRoles())); | ||
| } | ||
| } catch (Exception e) { | ||
| // LAZY collection may not be initialized outside session | ||
| } |
There was a problem hiding this comment.
避免用 catch (Exception) 静默吞错,防止掩盖真实映射故障
Line 52-58 当前会吞掉所有异常,roles 映射中的真实 bug(而不只是懒加载场景)会被静默忽略,返回不完整 inventory 且难以排查。建议仅忽略“可预期异常”,其余异常至少记录并继续抛出。
建议修改
- try {
- if (vo.getRoles() != null && !vo.getRoles().isEmpty()) {
- inv.setRoles(PhysicalServerRoleInventory.valueOf(vo.getRoles()));
- }
- } catch (Exception e) {
- // LAZY collection may not be initialized outside session
- }
+ try {
+ if (vo.getRoles() != null && !vo.getRoles().isEmpty()) {
+ inv.setRoles(PhysicalServerRoleInventory.valueOf(vo.getRoles()));
+ }
+ } catch (Exception e) {
+ // only ignore expected lazy-load case; do not swallow unexpected mapping failures
+ if (!e.getClass().getSimpleName().contains("LazyInitializationException")) {
+ throw e;
+ }
+ }🤖 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/server/PhysicalServerInventory.java`
around lines 52 - 58, The current catch-all around mapping vo.getRoles() to
PhysicalServerRoleInventory.valueOf in PhysicalServerInventory silently swallows
all exceptions; change it to only catch the expected lazy-loading exception
(e.g., org.hibernate.LazyInitializationException or the specific ORM lazy init
exception your project uses) for the LAZY collection case, and for any other
Exception log the error with context (include vo and roles info) and rethrow or
propagate it so real mapping bugs are not hidden; specifically update the
try/catch around vo.getRoles() / PhysicalServerRoleInventory.valueOf(...) to
handle the lazy-init exception only and handle other exceptions by logging and
rethrowing.
| public ServerRoleType(String typeName) { | ||
| this.typeName = typeName; | ||
| types.put(typeName, this); | ||
| } |
There was a problem hiding this comment.
阻止空值和重复类型污染全局注册表。
这里的构造函数既不校验 typeName,也不阻止重复注册。null/空白值会让后面的 hashCode()、toString() 和 valueOf() 行为变得不可靠;重复注册则会静默覆盖已有类型,后续基于这个注册表的 provider 查找会很难排查。
建议修正
public class ServerRoleType {
private static Map<String, ServerRoleType> types = Collections.synchronizedMap(new HashMap<String, ServerRoleType>());
private final String typeName;
@@
public ServerRoleType(String typeName) {
- this.typeName = typeName;
- types.put(typeName, this);
+ if (typeName == null || typeName.trim().isEmpty()) {
+ throw new IllegalArgumentException("ServerRoleType cannot be blank");
+ }
+
+ String normalizedTypeName = typeName.trim();
+ synchronized (types) {
+ if (types.containsKey(normalizedTypeName)) {
+ throw new IllegalArgumentException("ServerRoleType already registered: " + normalizedTypeName);
+ }
+ this.typeName = normalizedTypeName;
+ types.put(normalizedTypeName, this);
+ }
}🤖 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/server/ServerRoleType.java` around
lines 13 - 16, Constructor ServerRoleType(String typeName) currently accepts
null/blank names and silently overwrites duplicates in the static types
registry; update the ServerRoleType(String typeName) constructor to validate
that typeName is non-null and not blank (throw IllegalArgumentException if
invalid) and to check the static types map (types.containsKey(typeName)) before
inserting, throwing an exception on duplicate registration to avoid silent
overwrite; reference ServerRoleType(String) and the static types map as the
locations to change and be mindful of impacts on hashCode(), toString(), and
valueOf() which assume valid registrations.
| public ServerPoolVO ensureDefaultPool(String zoneUuid) { | ||
| ServerPoolVO existing = findDefaultPool(zoneUuid); | ||
| if (existing != null) { | ||
| return existing; | ||
| } | ||
|
|
||
| ServerPoolVO vo = new ServerPoolVO(); | ||
| vo.setUuid(DigestUtils.md5Hex(zoneUuid + "-default-pool")); | ||
| vo.setName(PhysicalServerConstant.DEFAULT_SERVER_POOL_NAME); | ||
| vo.setDescription("Default server pool created automatically"); | ||
| vo.setZoneUuid(zoneUuid); | ||
| vo.setState(ServerPoolState.Enabled); | ||
| vo.setDefault(true); | ||
| return dbf.persistAndRefresh(vo); | ||
| } |
There was a problem hiding this comment.
确保默认池创建在并发下是幂等的
Line [31] 到 Line [43] 是“先查后建”,并发请求下可能在持久化时撞唯一键并抛异常,导致上层创建流程被误判失败。建议在持久化失败后回查一次并返回已存在默认池。
建议修复
public ServerPoolVO ensureDefaultPool(String zoneUuid) {
ServerPoolVO existing = findDefaultPool(zoneUuid);
if (existing != null) {
return existing;
}
ServerPoolVO vo = new ServerPoolVO();
vo.setUuid(DigestUtils.md5Hex(zoneUuid + "-default-pool"));
vo.setName(PhysicalServerConstant.DEFAULT_SERVER_POOL_NAME);
vo.setDescription("Default server pool created automatically");
vo.setZoneUuid(zoneUuid);
vo.setState(ServerPoolState.Enabled);
vo.setDefault(true);
- return dbf.persistAndRefresh(vo);
+ try {
+ return dbf.persistAndRefresh(vo);
+ } catch (Exception e) {
+ ServerPoolVO raced = findDefaultPool(zoneUuid);
+ if (raced != null) {
+ return raced;
+ }
+ throw e;
+ }
}🤖 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/physicalServer/src/main/java/org/zstack/server/DefaultServerPoolFactory.java`
around lines 30 - 44, The ensureDefaultPool method currently does a
"find-then-create" which can race under concurrency; modify ensureDefaultPool
(and the persist call using dbf.persistAndRefresh) to catch the
persistence/unique-key exception when creating the ServerPoolVO (uuid computed
via DigestUtils.md5Hex(zoneUuid + "-default-pool")) and in the catch block call
findDefaultPool(zoneUuid) again and return that if present; if the re-query
still fails, rethrow the original exception. This makes ensureDefaultPool
idempotent under concurrent creates.
| String clusterUuid = (String) data.get(PathTwoFlowDataKey.CLUSTER_UUID); | ||
|
|
||
| String serverUuid; | ||
| try { | ||
| serverUuid = autoAssociator.findOrCreate(ctx, clusterUuid); |
There was a problem hiding this comment.
在调用 findOrCreate() 前补齐 clusterUuid 的前置校验
Line 53 读取 clusterUuid 后未校验,null/空串 会把错误延迟到下游,导致失败原因不明确且可能触发内部异常。建议在当前 Flow 快速失败。
建议修改
RoleMatchContext ctx = (RoleMatchContext) ctxObj;
String clusterUuid = (String) data.get(PathTwoFlowDataKey.CLUSTER_UUID);
+ if (clusterUuid == null || clusterUuid.trim().isEmpty()) {
+ trigger.fail(operr("AutoAssociateFlow missing required data: clusterUuid"));
+ return;
+ }
String serverUuid;
try {
serverUuid = autoAssociator.findOrCreate(ctx, clusterUuid);🤖 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/physicalServer/src/main/java/org/zstack/server/flow/AutoAssociateFlow.java`
around lines 53 - 57, After reading PathTwoFlowDataKey.CLUSTER_UUID into
variable clusterUuid, add a precondition check that it is not null or empty and
fast-fail with a clear error before calling autoAssociator.findOrCreate;
specifically, validate clusterUuid and if invalid call the flow failure path
(e.g. ctx.fail(...) or throw an appropriate Flow/IllegalArgumentException) with
a message like "clusterUuid is required" so autoAssociator.findOrCreate(ctx,
clusterUuid) is never invoked with a null/empty clusterUuid.
| void persistHardwareInfo(String serverUuid, UnifiedHardwareInfo info, String discoverSource) { | ||
| PhysicalServerHardwareInfoVO existing = Q.New(PhysicalServerHardwareInfoVO.class) | ||
| .eq(PhysicalServerHardwareInfoVO_.serverUuid, serverUuid) | ||
| .find(); | ||
| Timestamp now = new Timestamp(System.currentTimeMillis()); | ||
|
|
||
| if (existing == null) { | ||
| PhysicalServerHardwareInfoVO row = new PhysicalServerHardwareInfoVO(); | ||
| row.setServerUuid(serverUuid); | ||
| applyNonNull(row, info); | ||
| row.setDiscoverSource(discoverSource); | ||
| row.setLastDiscoverDate(now); | ||
| row.setCreateDate(now); | ||
| row.setLastOpDate(now); | ||
| dbf.persist(row); | ||
| logger.debug(String.format("persisted hardware info for server[uuid:%s] source=%s", serverUuid, discoverSource)); | ||
| return; | ||
| } | ||
| applyNonNull(existing, info); | ||
| // P1-3: first-writer-wins for discoverSource. The INSERT branch above writes the | ||
| // initial source tag; subsequent passes refresh the data fields and lastDiscoverDate | ||
| // but MUST NOT overwrite the source. Rationale: a fleet's discoverSource column | ||
| // should be a stable signal of "who first identified this host" — not a churning | ||
| // value that flips when an IPMI tier appears mid-life or a K8s-only adapter | ||
| // contributes one extra field. Operators wanting "currently strongest contributor" | ||
| // should derive it from the per-source field provenance once that's wired (out of | ||
| // scope for v5.5.18); lastDiscoverDate alone tells when the row was last touched. | ||
| existing.setLastDiscoverDate(now); | ||
| dbf.update(existing); | ||
| logger.debug(String.format("updated hardware info for server[uuid:%s] originalSource=%s", | ||
| serverUuid, existing.getDiscoverSource())); | ||
| } |
There was a problem hiding this comment.
persistHardwareInfo 存在并发插入的竞态条件
当两个线程同时对同一个 serverUuid 调用 persistHardwareInfo 且该行尚不存在时,两者都可能在第 237-239 行查询到 existing=null,然后都尝试 INSERT,导致主键/唯一键冲突异常。
建议使用数据库级别的 UPSERT(INSERT ... ON DUPLICATE KEY UPDATE)或在查询时加锁。
🔧 建议的修复方案
方案 A:使用 PESSIMISTIC_WRITE 锁定 PhysicalServerVO 行(与 PhysicalServerManagerImpl.lockPhysicalServerForAttach 类似)
方案 B:使用 try-catch 捕获重复键异常,然后重试更新:
void persistHardwareInfo(String serverUuid, UnifiedHardwareInfo info, String discoverSource) {
PhysicalServerHardwareInfoVO existing = Q.New(PhysicalServerHardwareInfoVO.class)
.eq(PhysicalServerHardwareInfoVO_.serverUuid, serverUuid)
.find();
Timestamp now = new Timestamp(System.currentTimeMillis());
if (existing == null) {
- PhysicalServerHardwareInfoVO row = new PhysicalServerHardwareInfoVO();
- row.setServerUuid(serverUuid);
- applyNonNull(row, info);
- row.setDiscoverSource(discoverSource);
- row.setLastDiscoverDate(now);
- row.setCreateDate(now);
- row.setLastOpDate(now);
- dbf.persist(row);
+ try {
+ PhysicalServerHardwareInfoVO row = new PhysicalServerHardwareInfoVO();
+ row.setServerUuid(serverUuid);
+ applyNonNull(row, info);
+ row.setDiscoverSource(discoverSource);
+ row.setLastDiscoverDate(now);
+ row.setCreateDate(now);
+ row.setLastOpDate(now);
+ dbf.persist(row);
+ } catch (Exception e) {
+ // Retry as update if another thread inserted first
+ existing = Q.New(PhysicalServerHardwareInfoVO.class)
+ .eq(PhysicalServerHardwareInfoVO_.serverUuid, serverUuid)
+ .find();
+ if (existing != null) {
+ applyNonNull(existing, info);
+ existing.setLastDiscoverDate(now);
+ dbf.update(existing);
+ }
+ }
logger.debug(...);
return;
}
// ... rest unchanged
}🤖 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/physicalServer/src/main/java/org/zstack/server/hardware/PhysicalServerHardwareService.java`
around lines 236 - 267, persistHardwareInfo currently has a race where two
threads can both see existing==null and try to INSERT; fix by making the
insert/update atomic: either (preferred) perform a DB-level upsert (INSERT ...
ON DUPLICATE KEY UPDATE) instead of separate find+persist so persistHardwareInfo
writes/refreshes in one statement, or emulate by acquiring a PESSIMISTIC_WRITE
lock on the PhysicalServer row (like
PhysicalServerManagerImpl.lockPhysicalServerForAttach) before checking
Q.New(...).eq(...).find(), or wrap the persist in a try/catch that catches the
duplicate-key/constraint violation, then re-query and applyNonNull+dbf.update in
the catch to retry; apply the change inside persistHardwareInfo and ensure
lastDiscoverDate and discoverSource semantics are preserved (only set
discoverSource on initial insert).
| public String findOrCreate(RoleMatchContext ctx, String clusterUuid) { | ||
| // Tier 1: match by serialNumber | ||
| String sn = ctx.getSerialNumber(); | ||
| if (sn != null && !SERIAL_NUMBER_BLACKLIST.contains(sn.trim())) { | ||
| PhysicalServerVO vo = Q.New(PhysicalServerVO.class) | ||
| .eq(PhysicalServerAO_.serialNumber, sn.trim()) | ||
| .eq(PhysicalServerAO_.zoneUuid, ctx.getZoneUuid()) | ||
| .find(); | ||
| if (vo != null) { | ||
| return vo.getUuid(); | ||
| } | ||
| } | ||
|
|
||
| // Tier 2: match by oobAddress + zoneUuid | ||
| String oobAddr = ctx.getOobAddress(); | ||
| if (oobAddr != null && !oobAddr.isEmpty() && ctx.getZoneUuid() != null) { | ||
| PhysicalServerVO vo = Q.New(PhysicalServerVO.class) | ||
| .eq(PhysicalServerAO_.oobAddress, oobAddr) | ||
| .eq(PhysicalServerAO_.zoneUuid, ctx.getZoneUuid()) | ||
| .find(); | ||
| if (vo != null) { | ||
| return vo.getUuid(); | ||
| } | ||
| } | ||
|
|
||
| // Tier 3: match by managementIp + zoneUuid | ||
| String mgmtIp = ctx.getManagementIp(); | ||
| if (mgmtIp != null && !mgmtIp.isEmpty() && ctx.getZoneUuid() != null) { | ||
| PhysicalServerVO vo = Q.New(PhysicalServerVO.class) | ||
| .eq(PhysicalServerAO_.managementIp, mgmtIp) | ||
| .eq(PhysicalServerAO_.zoneUuid, ctx.getZoneUuid()) | ||
| .find(); | ||
| if (vo != null) { | ||
| return vo.getUuid(); | ||
| } | ||
| } | ||
|
|
||
| // No match — auto-create if pool is available | ||
| String poolUuid = Q.New(ClusterVO.class) | ||
| .eq(ClusterAO_.uuid, clusterUuid) | ||
| .select(ClusterAO_.serverPoolUuid) | ||
| .findValue(); | ||
|
|
||
| if (poolUuid == null) { | ||
| return null; | ||
| } | ||
|
|
||
| PhysicalServerVO vo = new PhysicalServerVO(); | ||
| vo.setUuid(Platform.getUuid()); | ||
| vo.setName("auto-" + ctx.getManagementIp()); | ||
| vo.setZoneUuid(ctx.getZoneUuid()); | ||
| vo.setPoolUuid(poolUuid); | ||
| vo.setManagementIp(ctx.getManagementIp()); | ||
| vo.setSerialNumber(ctx.getSerialNumber()); | ||
| vo.setOobAddress(ctx.getOobAddress()); |
There was a problem hiding this comment.
先统一归一化,再做匹配和落库。
这里的读写规则现在不一致:serialNumber 查询时用了 trim(),但创建时又把原始值写回;oobAddress 和 managementIp 则完全没有做归一化。像 " SN123 " 这种输入第一次会创建出带空格的记录,后续再用同一个序列号匹配时会查不到,最后重复创建出多条 PhysicalServerVO。
建议修正
public String findOrCreate(RoleMatchContext ctx, String clusterUuid) {
+ String zoneUuid = normalize(ctx.getZoneUuid());
+ String serialNumber = normalize(ctx.getSerialNumber());
+ String oobAddress = normalize(ctx.getOobAddress());
+ String managementIp = normalize(ctx.getManagementIp());
+
// Tier 1: match by serialNumber
- String sn = ctx.getSerialNumber();
- if (sn != null && !SERIAL_NUMBER_BLACKLIST.contains(sn.trim())) {
+ if (serialNumber != null && !SERIAL_NUMBER_BLACKLIST.contains(serialNumber)) {
PhysicalServerVO vo = Q.New(PhysicalServerVO.class)
- .eq(PhysicalServerAO_.serialNumber, sn.trim())
- .eq(PhysicalServerAO_.zoneUuid, ctx.getZoneUuid())
+ .eq(PhysicalServerAO_.serialNumber, serialNumber)
+ .eq(PhysicalServerAO_.zoneUuid, zoneUuid)
.find();
if (vo != null) {
return vo.getUuid();
}
}
@@
- String oobAddr = ctx.getOobAddress();
- if (oobAddr != null && !oobAddr.isEmpty() && ctx.getZoneUuid() != null) {
+ if (oobAddress != null && zoneUuid != null) {
PhysicalServerVO vo = Q.New(PhysicalServerVO.class)
- .eq(PhysicalServerAO_.oobAddress, oobAddr)
- .eq(PhysicalServerAO_.zoneUuid, ctx.getZoneUuid())
+ .eq(PhysicalServerAO_.oobAddress, oobAddress)
+ .eq(PhysicalServerAO_.zoneUuid, zoneUuid)
.find();
@@
- String mgmtIp = ctx.getManagementIp();
- if (mgmtIp != null && !mgmtIp.isEmpty() && ctx.getZoneUuid() != null) {
+ if (managementIp != null && zoneUuid != null) {
PhysicalServerVO vo = Q.New(PhysicalServerVO.class)
- .eq(PhysicalServerAO_.managementIp, mgmtIp)
- .eq(PhysicalServerAO_.zoneUuid, ctx.getZoneUuid())
+ .eq(PhysicalServerAO_.managementIp, managementIp)
+ .eq(PhysicalServerAO_.zoneUuid, zoneUuid)
.find();
@@
- vo.setName("auto-" + ctx.getManagementIp());
- vo.setZoneUuid(ctx.getZoneUuid());
+ vo.setName("auto-" + managementIp);
+ vo.setZoneUuid(zoneUuid);
vo.setPoolUuid(poolUuid);
- vo.setManagementIp(ctx.getManagementIp());
- vo.setSerialNumber(ctx.getSerialNumber());
- vo.setOobAddress(ctx.getOobAddress());
+ vo.setManagementIp(managementIp);
+ vo.setSerialNumber(serialNumber);
+ vo.setOobAddress(oobAddress);private static String normalize(String value) {
if (value == null) {
return null;
}
String trimmed = value.trim();
return trimmed.isEmpty() ? null : trimmed;
}As per coding guidelines, "注意检查来自 Message 的参数是否做过 trim,用户可能在浏览器上复制粘贴的数据带有空格、换行符等".
🤖 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/physicalServer/src/main/java/org/zstack/server/PhysicalServerAutoAssociator.java`
around lines 39 - 93, findOrCreate in PhysicalServerAutoAssociator currently
trims serialNumber only for the query but writes raw values (and doesn't
normalize oobAddress/managementIp), causing duplicate records; add a private
normalize(String) helper that returns null for null/blank and trimmed otherwise,
call normalize on ctx.getSerialNumber(), ctx.getOobAddress(), and
ctx.getManagementIp() at the start of findOrCreate, use the normalized values
for all Q.New(...) comparisons (and blacklist checks) and also set the
normalized values onto the new PhysicalServerVO before saving so reads and
writes use the same canonical form.
| // No match — auto-create if pool is available | ||
| String poolUuid = Q.New(ClusterVO.class) | ||
| .eq(ClusterAO_.uuid, clusterUuid) | ||
| .select(ClusterAO_.serverPoolUuid) | ||
| .findValue(); | ||
|
|
||
| if (poolUuid == null) { | ||
| return null; | ||
| } | ||
|
|
||
| PhysicalServerVO vo = new PhysicalServerVO(); | ||
| vo.setUuid(Platform.getUuid()); | ||
| vo.setName("auto-" + ctx.getManagementIp()); | ||
| vo.setZoneUuid(ctx.getZoneUuid()); | ||
| vo.setPoolUuid(poolUuid); | ||
| vo.setManagementIp(ctx.getManagementIp()); | ||
| vo.setSerialNumber(ctx.getSerialNumber()); | ||
| vo.setOobAddress(ctx.getOobAddress()); | ||
| vo.setState(PhysicalServerState.Enabled); | ||
| vo.setPowerStatus(PhysicalServerPowerStatus.POWER_UNKNOWN); | ||
| dbf.persistAndRefresh(vo); | ||
| return vo.getUuid(); |
There was a problem hiding this comment.
这段“先查后建”在并发下不是幂等的。
两个并发请求如果指向同一台机器,都可能在 Line 77 之前查不到记录,然后同时走到 persistAndRefresh()。有唯一约束时,其中一个请求会直接撞出数据库异常;没有唯一约束时,就会落出重复的 PhysicalServerVO。这类自动关联入口最好把匹配键收敛到数据库唯一约束/幂等插入,再在冲突后回查已有记录。
🤖 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/physicalServer/src/main/java/org/zstack/server/PhysicalServerAutoAssociator.java`
around lines 76 - 97, 当前“先查后建”逻辑在并发下不幂等:在 PhysicalServerAutoAssociator 中先用
Q.New(...) 查 poolUuid 后直接 new PhysicalServerVO() 并 dbf.persistAndRefresh(vo)
会导致并发插入冲突或重复记录。修改为以数据库唯一键(例如 poolUuid+managementIp 或
serialNumber)为幂等键,在插入时使用幂等插入/UPSERT 或在 persistAndRefresh 抛出唯一约束异常时捕获异常并回查已有
PhysicalServerVO(根据 poolUuid + ctx.getManagementIp() 或 ctx.getSerialNumber())返回其
uuid,从而保证并发安全;也可在创建前加短期分布式锁(基于 managementIp)作为备选方案。
| validate(server); | ||
| if (CoreGlobalProperty.UNIT_TEST_ON) { | ||
| completion.success(); | ||
| return; | ||
| } |
There was a problem hiding this comment.
Completion 风格接口中不应直接抛业务异常,应回调 completion.fail(...)
这里直接 throw OperationFailureException 可能让调用方拿不到预期回调,破坏异步错误处理语义。
建议修改
public void powerOnPxe(PhysicalServerVO server, Completion completion) {
- validate(server);
+ try {
+ validate(server);
+ } catch (OperationFailureException e) {
+ completion.fail(e.getErrorCode());
+ return;
+ }
if (CoreGlobalProperty.UNIT_TEST_ON) {
completion.success();
return;
}
@@
private void runIpmi(PhysicalServerVO server, String op, IpmiAction action, Completion completion) {
- validate(server);
+ try {
+ validate(server);
+ } catch (OperationFailureException e) {
+ completion.fail(e.getErrorCode());
+ return;
+ }
if (CoreGlobalProperty.UNIT_TEST_ON) {
completion.success();
return;
}Also applies to: 56-61, 70-80
🤖 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/physicalServer/src/main/java/org/zstack/server/PhysicalServerIpmiPowerExecutor.java`
around lines 29 - 33, The code in PhysicalServerIpmiPowerExecutor uses direct
throws of OperationFailureException (and other business exceptions) which breaks
the async Completion contract; instead, catch validation and runtime exceptions
around validate(server) and all places noted (the blocks currently throwing at
lines ~29-33, ~56-61, ~70-80) and invoke completion.fail(...) with an
appropriate OperationFailureException (or the caught exception) so callers
receive the failure callback; keep the existing completion.success() path when
UNIT_TEST_ON is true and replace any direct "throw OperationFailureException"
with completion.fail(new OperationFailureException(...)) (or completion.fail(e))
in those methods.
| private void handle(APIAttachProvisionNetworkToClusterMsg msg) { | ||
| PhysicalServerProvisionNetworkClusterRefVO ref = new PhysicalServerProvisionNetworkClusterRefVO(); | ||
| ref.setNetworkUuid(msg.getNetworkUuid()); | ||
| ref.setClusterUuid(msg.getClusterUuid()); | ||
| dbf.persist(ref); | ||
|
|
||
| PhysicalServerProvisionNetworkVO networkVO = dbf.findByUuid(msg.getNetworkUuid(), PhysicalServerProvisionNetworkVO.class); | ||
| if (networkVO == null) { | ||
| throw new OperationFailureException(operr("ProvisionNetwork[uuid:%s] not found", msg.getNetworkUuid())); | ||
| } | ||
|
|
||
| APIAttachProvisionNetworkToClusterEvent evt = new APIAttachProvisionNetworkToClusterEvent(msg.getId()); | ||
| evt.setInventory(PhysicalServerProvisionNetworkInventory.valueOf(networkVO)); | ||
| bus.publish(evt); | ||
| } |
There was a problem hiding this comment.
先持久化引用再检查网络存在性会导致孤儿数据
第 678 行先执行 dbf.persist(ref) 创建了 PhysicalServerProvisionNetworkClusterRefVO,然后第 680-682 行才检查 networkVO 是否存在。如果 networkVO 为 null,抛出异常时引用记录已经被持久化,造成孤儿数据。
🐛 建议的修复
private void handle(APIAttachProvisionNetworkToClusterMsg msg) {
+ PhysicalServerProvisionNetworkVO networkVO = dbf.findByUuid(msg.getNetworkUuid(), PhysicalServerProvisionNetworkVO.class);
+ if (networkVO == null) {
+ throw new OperationFailureException(operr("ProvisionNetwork[uuid:%s] not found", msg.getNetworkUuid()));
+ }
+
PhysicalServerProvisionNetworkClusterRefVO ref = new PhysicalServerProvisionNetworkClusterRefVO();
ref.setNetworkUuid(msg.getNetworkUuid());
ref.setClusterUuid(msg.getClusterUuid());
dbf.persist(ref);
- PhysicalServerProvisionNetworkVO networkVO = dbf.findByUuid(msg.getNetworkUuid(), PhysicalServerProvisionNetworkVO.class);
- if (networkVO == null) {
- throw new OperationFailureException(operr("ProvisionNetwork[uuid:%s] not found", msg.getNetworkUuid()));
- }
-
APIAttachProvisionNetworkToClusterEvent evt = new APIAttachProvisionNetworkToClusterEvent(msg.getId());
evt.setInventory(PhysicalServerProvisionNetworkInventory.valueOf(networkVO));
bus.publish(evt);
}🤖 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/physicalServer/src/main/java/org/zstack/server/PhysicalServerManagerImpl.java`
around lines 674 - 688, In handle(APIAttachProvisionNetworkToClusterMsg msg) you
persist a PhysicalServerProvisionNetworkClusterRefVO before verifying the
referenced PhysicalServerProvisionNetworkVO exists, which can create orphan
refs; change the logic to first call dbf.findByUuid(msg.getNetworkUuid(),
PhysicalServerProvisionNetworkVO.class) and throw the OperationFailureException
if null, then create and dbf.persist(new
PhysicalServerProvisionNetworkClusterRefVO()) and finally build/publish the
APIAttachProvisionNetworkToClusterEvent (using
PhysicalServerProvisionNetworkInventory.valueOf(networkVO)).
|
Comment from gitlab: 自上次添加REVIEWED标签(2026-05-09 06:48:36.000Z)后, 有新的COMMIT更新(2026-05-09 08:13:06.155Z), 所以移除了REVIEWED标签 |
|
Comment from gitlab: 自上次添加REVIEWED标签(2026-05-09 09:35:45.000Z)后, 有新的COMMIT更新(2026-05-09 09:54:30.252Z), 所以移除了REVIEWED标签 |
| @Autowired | ||
| private PluginRegistry pluginRgty; | ||
|
|
||
| // 铁律 15: lazy getter pattern — never field-initialize from pluginRgty. |
There was a problem hiding this comment.
Comment from jin.ma:
Translated. PhysicalServerCapacityUpdater.java:82 — 铁律 15 → Rule 15.
| → 通过 PhysicalServerRoleVO 映射回 HostVO UUID 集合 | ||
| → 注入 HostAllocatorSpec.candidateHostUuids | ||
| → 阶段2: 现有 HostAllocatorChain 在预筛选集合上正常执行(L2/PS/BS/Tag 等 KVM Flow 全部保留) | ||
| ``` |
There was a problem hiding this comment.
Comment from jin.ma:
N/A — docs/architecture-allocator-2026-03-18.md was removed from this MR (obsolete planning doc not for upstream). Resolving.
| (重建 FK) | ||
| → 现有系统恢复正常,新的 PhysicalServer* 表可安全删除 | ||
| ``` | ||
|
|
There was a problem hiding this comment.
Comment from jin.ma:
N/A — docs/api-flow-qa-testplan.md was removed from this MR (obsolete planning doc not for upstream). Resolving.
PhysicalServer-first model unifying KVM hosts, BareMetal2 chassis, and container NativeHosts under a single resource family with a RoleProvider SPI. Adds: * PhysicalServer*VO family + Hibernate registration * HostCapacityVO TABLE -> VIEW (ALGORITHM=MERGE + COALESCE), @immutable * W1-W6 capacity write paths consolidated on PhysicalServerCapacityVO with NB-22/24/30 invariants (single PESSIMISTIC_WRITE keyed by serverUuid) * PhysicalServerRoleProvider SPI v3 + KVM / BareMetal2 / Container RoleProvider implementations * AttachPhysicalServerRole / DetachPhysicalServerRole API + auto- associate (serialNumber / oobAddress / managementIp tier-1/2/3) * PhysicalServerHardwareService + UnifiedHardwareInfo flat DTO + HardwareDiscoveryScheduler queue (3 GlobalConfig) * Schema migration: ServerPool / PS+Role / vcenter half-migration / ResourceVO+ARR / admin-only AccountRef (V5.5.18__schema.sql) * ProvisionProvider SPI (PhysicalServer-first PXE) + GATEWAY_PXE data-plane stage-based + LongJob phase tracking + gateway-agent ping monitoring * Path-2 FlowChain tail extension (KVM / BM2 / Container per-node) AutoAssociate -> CreatePhysicalServerRoleFlow -> InitPhysicalServerCapacityFlow -> enqueueDiscoveryHook * PSC writer collapse two-layer model: Layer 1 module sync entries write PSC.total*, Layer 2 PhysicalServerCapacityUpdater.recalculate is the sole virtual-quantity writer (single lock, NB-30); HostCapacityUpdater POJO path marked @deprecated. * Container cordon production triggers (AC-CM-13): - ContainerNodeCordonService.mirrorFromK8s observe-only registry write driven by KubernetesNodeInventory.unschedulable in processNodeTransactional - Capacity-driven hysteresis evaluate() invoked from ContainerEndpointBase.success() after recalculate, with safety buffers shared via PhysicalServerCapacityBuffers helper Tests: 3 RoleProviderIntegrationCase (KVM / BM2 / Container) all green; PhysicalServerCapacityCase / RoleCase / CompatCase / ServerPoolCrudCase green; KvmReportHostCapacityRecalcCase / ContainerSyncRecalcCase / ContainerCordonReservedCase green; new ContainerK8sCordonMirrorCase / ContainerCapacityCordonEvaluateCase / ContainerCapacityHysteresisUncordonCase green; production verified on 172.26.201.160 (Layer 1 + Layer 2 PSC writes; KVM host availableCpu 80 -> 72 with cpuBuffer=8; 7 NativeHost PSC.totalCpu populated from K8s capacity). Out of scope: BM1 retirement (ADR-010), vcenter half-migration option C (ADR-009, NB-25), ServerAllocatorChain Group C (deferred to v5.5.18.x). Change-Id: Iaa1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0
Test: regression sweep needs them for compile VmModelMountInventory + VmModelMountStatus. YuccaBareMetal2DpuChassisConfig. Restored from upstream feature-unifi-host. Change-Id: I08f19554ab780f3ddba391708c8aac38e836c2e3
Test: 8 case sweep testlib CORE_SERVICES + schema sentinels. Change-Id: I0015b4919de9ba5de41af1b6407c8017a06b9bdf
c9a3d25 to
4c07c05
Compare
There was a problem hiding this comment.
Actionable comments posted: 9
♻️ Duplicate comments (1)
docs/STATUS.md (1)
118-130:⚠️ Potential issue | 🟡 Minor | ⚡ Quick win修复表格列数不一致问题
此表格标题行定义了 3 列(区段/范围/状态),但第 124 行 U18-U20 条目实际包含 4 个单元格,导致 Markdown 渲染错位。建议将第 3 和第 4 个单元格的内容合并到"状态"列中。
🔧 建议的修复
将 line 124 从:
| **U18-U20** ProvisionProvider SPI | U18 SPI / U19 PhysicalServer-first PXE provider / U20 LongJob | ✅ DONE | stage-based + ping monitoring GREEN...改为:
| **U18-U20** ProvisionProvider SPI | U18 SPI / U19 PhysicalServer-first PXE provider / U20 LongJob | ✅ DONE — stage-based + ping monitoring GREEN...使用
—或换行符在状态列内分隔子项。🤖 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 `@docs/STATUS.md` around lines 118 - 130, The Markdown table row for U18-U20 in STATUS.md has 4 cells which breaks the 3-column header; fix the row for the unique entry "**U18-U20** ProvisionProvider SPI" by merging the 3rd and 4th cells into the single "状态" cell (e.g., change the separate "✅ DONE" and the following text into one cell like "✅ DONE — stage-based + ping monitoring GREEN, phase tracked..."), ensuring there are only three pipe-separated columns for that row so the table renders correctly.
🧹 Nitpick comments (2)
docs/runbooks/v5518-recalculate-perf.md (1)
147-161: 💤 Low value可选:为基准测试输出代码块添加语言标识符
此代码块包含基准测试输出,可以添加
text或plaintext语言标识符以提高 Markdown 渲染一致性。♻️ 建议的修复
-``` +```text ================================================================ PhysicalServerCapacityUpdater perf bench (AC-CM-PERF-01)🤖 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 `@docs/runbooks/v5518-recalculate-perf.md` around lines 147 - 161, The fenced code block showing the benchmark output for "PhysicalServerCapacityUpdater perf bench (AC-CM-PERF-01)" is missing a language tag; update that code fence to start with ```text (or ```plaintext) so Markdown renderers treat it as plain text and preserve formatting—locate the block containing the benchmark lines (the lines starting with "================================================================" and "PhysicalServerCapacityUpdater perf bench (AC-CM-PERF-01)") and change the opening backticks to ```text.plugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerScanner.java (1)
219-235: 💤 Low value架构类型硬编码为 "x86_64" 可能不适用于所有服务器。
扫描发现的服务器可能是 ARM (aarch64) 架构。IPMI 探测无法确定架构类型,建议:
- 将
architecture设为可配置参数通过ScanSpec传入- 或者初始值设为
null/UNKNOWN,后续通过其他方式探测🤖 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/physicalServer/src/main/java/org/zstack/server/PhysicalServerScanner.java` around lines 219 - 235, The createPhysicalServer method hardcodes architecture to "x86_64"; change it to use a value from ScanSpec or a neutral default: update ScanSpec to include an architecture field/getter (e.g., getArchitecture()) and set vo.setArchitecture(spec.getArchitecture()) when present, otherwise set vo.setArchitecture(null) or vo.setArchitecture("UNKNOWN"); adjust createPhysicalServer to reference ScanSpec.getArchitecture() and ensure PhysicalServerVO.accepts nullable/UNKNOWN values so architecture is not incorrectly assumed.
🤖 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 `@docs/runbooks/physical-server-pxe-real-env-validation.md`:
- Line 897: The PRD Reference currently uses a local absolute path
`/home/mj/zstack-workspace/cloud_prd/prd/v5.5.18-unified-hardware/provision/feat-unified_provision_network_prd.md`
which cannot be reproduced by others; update the PRD Reference line in
physical-server-pxe-real-env-validation.md to point to a repository-relative
path (e.g., docs/prd/... or relative path within the repo) or a shared URL (PRD
in the repo or a web link) so other readers can access it; locate the line
containing the exact string
`/home/mj/zstack-workspace/cloud_prd/.../feat-unified_provision_network_prd.md`
and replace it with the chosen relative path or link.
- Line 39: Multiple fenced code blocks in the markdown are missing language
identifiers (triggering MD040); update each triple-backtick code block in this
document to include an appropriate language tag (e.g., bash, text, json, sql)
consistent with the content—replace ``` with ```text or ```bash etc., and apply
the same change to the example blocks shown in the review so all occurrences are
annotated uniformly to satisfy markdownlint.
In
`@header/src/main/java/org/zstack/header/server/APIUpdateProvisionNetworkMsg.java`:
- Around line 9-11: The class APIUpdateProvisionNetworkMsg is missing the
required `@RestResponse` annotation for its declared response class; add a
`@RestResponse`(APIUpdateProvisionNetworkEvent.class) annotation on the
APIUpdateProvisionNetworkMsg class declaration so the response contract matches
the `@RestRequest`(responseClass=...) and satisfies the repository convention that
API message classes declare their reply/event via `@RestResponse`.
- Around line 40-98: The setters for Message string fields (setUuid, setName,
setDhcpInterface, setDhcpRangeStartIp, setDhcpRangeEndIp, setDhcpRangeNetmask,
setDhcpRangeGateway) currently assign values directly; change each to perform a
null-safe trim before assignment (e.g., assign field = input == null ? null :
input.trim()) so leading/trailing whitespace/newlines are normalized and won't
leak into persistence or matching logic.
In `@header/src/main/java/org/zstack/header/server/PhysicalServerCapacityVO.java`:
- Around line 117-120: The `@PreUpdate` hook in PhysicalServerCapacityVO currently
nulls out lastOpDate, which is wrong; change the preUpdate() method to set the
lastOpDate field to the current timestamp (i.e., assign the current time / new
timestamp to lastOpDate) instead of null so the entity refreshes its last
operation time on updates; update the preUpdate() implementation in class
PhysicalServerCapacityVO (method preUpdate and field lastOpDate) accordingly.
In
`@plugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerApiInterceptor.java`:
- Around line 67-80: The DHCP range IP fields are not trimmed before validation
in validate(APICreateProvisionNetworkMsg), so trim dhcpRangeStartIp,
dhcpRangeEndIp, dhcpRangeNetmask and dhcpRangeGateway (use their getters, trim
the strings and set them back on msg or use trimmed local variables) before
calling NetworkUtils.isIpv4Address; then perform the existing null check and
throw ApiMessageInterceptionException with the trimmed value in the argerr
message so validation and error text use the cleaned inputs.
- Around line 44-65: Trim the managementIp before validating in both
validate(APICreatePhysicalServerMsg) and validate(APIUpdatePhysicalServerMsg):
call trim() on msg.getManagementIp() (handle nulls) and use the trimmed value
for NetworkUtils.isIpv4Address checks and in error messages; ensure you do not
modify other logic (e.g., pool/zone validation) and use the trimmed value
consistently when constructing the argerr message.
In
`@plugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerStandalonePxeProvisionProvider.java`:
- Around line 11-35: PhysicalServerStandalonePxeProvisionProvider is not
registered as a Spring bean so it never gets injected into the
List<ProvisionProvider> and providers().get(ProvisionNetworkType.STANDALONE_PXE)
returns null; fix by registering the class as a Spring bean (e.g., add
`@Component` to the PhysicalServerStandalonePxeProvisionProvider class or define
an explicit `@Bean` that returns a new
PhysicalServerStandalonePxeProvisionProvider), ensure the appropriate import
(org.springframework.stereotype.Component) or configuration class is updated and
that component scanning covers this package so ProvisionProvider instances are
discovered for PhysicalServerProvisionService.
In `@scripts/mvn-safe-install.sh`:
- Around line 27-30: The timestamp check only finds files under
header/src/main/java and abstraction/src/main/java (used to set
newest_src_mtime) and therefore misses shared *VO.java/*AO.java in other dirs
(e.g., core), causing stale detection to fail; update the find invocation that
sets newest_src_mtime to include the shared locations or explicitly search by
name patterns for '*VO.java' and '*AO.java' across the repo (for example add
core/src/main/java or use -name '*VO.java' -o -name '*AO.java' combined with the
existing paths), preserve the existing cut/sort/tail logic and the empty-result
guard ([ -z "$newest_src_mtime" ]), and keep comparisons against jar_mtime
unchanged so shared VO/AO edits are considered when deciding staleness.
---
Duplicate comments:
In `@docs/STATUS.md`:
- Around line 118-130: The Markdown table row for U18-U20 in STATUS.md has 4
cells which breaks the 3-column header; fix the row for the unique entry
"**U18-U20** ProvisionProvider SPI" by merging the 3rd and 4th cells into the
single "状态" cell (e.g., change the separate "✅ DONE" and the following text into
one cell like "✅ DONE — stage-based + ping monitoring GREEN, phase tracked..."),
ensuring there are only three pipe-separated columns for that row so the table
renders correctly.
---
Nitpick comments:
In `@docs/runbooks/v5518-recalculate-perf.md`:
- Around line 147-161: The fenced code block showing the benchmark output for
"PhysicalServerCapacityUpdater perf bench (AC-CM-PERF-01)" is missing a language
tag; update that code fence to start with ```text (or ```plaintext) so Markdown
renderers treat it as plain text and preserve formatting—locate the block
containing the benchmark lines (the lines starting with
"================================================================" and
"PhysicalServerCapacityUpdater perf bench (AC-CM-PERF-01)") and change the
opening backticks to ```text.
In
`@plugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerScanner.java`:
- Around line 219-235: The createPhysicalServer method hardcodes architecture to
"x86_64"; change it to use a value from ScanSpec or a neutral default: update
ScanSpec to include an architecture field/getter (e.g., getArchitecture()) and
set vo.setArchitecture(spec.getArchitecture()) when present, otherwise set
vo.setArchitecture(null) or vo.setArchitecture("UNKNOWN"); adjust
createPhysicalServer to reference ScanSpec.getArchitecture() and ensure
PhysicalServerVO.accepts nullable/UNKNOWN values so architecture is not
incorrectly assumed.
🪄 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: f549a1dd-ce29-456c-8160-4590575b738d
⛔ Files ignored due to path filters (81)
compute/pom.xmlis excluded by!**/*.xmlcompute/src/test/resources/searchConfig/indexConfig.xmlis excluded by!**/*.xmlconf/globalConfig/hostAllocator.xmlis excluded by!**/*.xmlconf/globalConfig/physicalServer.xmlis excluded by!**/*.xmlconf/persistence.xmlis excluded by!**/*.xmlconf/serviceConfig/physicalServer.xmlis excluded by!**/*.xmlconf/serviceConfig/provisionNetwork.xmlis excluded by!**/*.xmlconf/serviceConfig/serverPool.xmlis excluded by!**/*.xmlconf/springConfigXml/HostAllocatorManager.xmlis excluded by!**/*.xmlconf/springConfigXml/Kvm.xmlis excluded by!**/*.xmlconf/springConfigXml/PhysicalServerManager.xmlis excluded by!**/*.xmlconf/zstack.xmlis excluded by!**/*.xmlplugin/kvm/pom.xmlis excluded by!**/*.xmlplugin/kvm/src/test/resources/searchConfig/indexConfig.xmlis excluded by!**/*.xmlplugin/physicalServer/pom.xmlis excluded by!**/*.xmlplugin/pom.xmlis excluded by!**/*.xmlsdk/src/main/java/SourceClassMap.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/AddBareMetal2DpuChassisAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/AddBareMetal2IpmiChassisAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/AddKVMHostAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/AttachPhysicalServerRoleAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/AttachPhysicalServerRoleResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/AttachProvisionNetworkToClusterAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/AttachProvisionNetworkToClusterResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/AttachProvisionNetworkToPoolAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/AttachProvisionNetworkToPoolResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/BareMetal2DpuChassisConfig.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/ChangeClusterServerPoolAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/ChangeClusterServerPoolResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/ChangePhysicalServerStateAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/ChangePhysicalServerStateResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/ClusterInventory.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/CreatePhysicalServerAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/CreatePhysicalServerResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/CreateProvisionNetworkAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/CreateProvisionNetworkResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/CreateServerPoolAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/CreateServerPoolResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/DeletePhysicalServerAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/DeletePhysicalServerResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/DeleteProvisionNetworkAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/DeleteProvisionNetworkResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/DeleteServerPoolAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/DeleteServerPoolResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/DetachPhysicalServerRoleAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/DetachPhysicalServerRoleResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/DetachProvisionNetworkFromClusterAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/DetachProvisionNetworkFromClusterResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/DetachProvisionNetworkFromPoolAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/DetachProvisionNetworkFromPoolResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/DiscoverPhysicalServerHardwareAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/DiscoverPhysicalServerHardwareResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/MountModelToVmInstanceResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/PhysicalServerInventory.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/PhysicalServerProvisionNetworkInventory.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/PhysicalServerRoleInventory.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/PowerOffPhysicalServerAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/PowerOffPhysicalServerResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/PowerOnPhysicalServerAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/PowerOnPhysicalServerResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/PowerResetPhysicalServerAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/PowerResetPhysicalServerResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/QueryPhysicalServerAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/QueryPhysicalServerResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/QueryPhysicalServerRoleAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/QueryPhysicalServerRoleResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/QueryProvisionNetworkAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/QueryProvisionNetworkResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/QueryServerPoolAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/QueryServerPoolResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/ScanPhysicalServersAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/ScanPhysicalServersResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/ServerPoolInventory.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/UnmountModelFromVmInstanceResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/UpdatePhysicalServerAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/UpdatePhysicalServerResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/UpdateProvisionNetworkAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/UpdateProvisionNetworkResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/UpdateServerPoolAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/UpdateServerPoolResult.javais excluded by!sdk/**test/pom.xmlis excluded by!**/*.xml
📒 Files selected for processing (219)
.gitignorecompute/src/main/java/org/zstack/compute/allocator/HostAllocatorGlobalConfig.javacompute/src/main/java/org/zstack/compute/allocator/HostAllocatorManagerImpl.javacompute/src/main/java/org/zstack/compute/allocator/HostCapacityOverProvisioningManagerImpl.javacompute/src/main/java/org/zstack/compute/allocator/HostCapacityUpdater.javacompute/src/main/java/org/zstack/compute/allocator/HostCpuOverProvisioningManagerImpl.javacompute/src/main/java/org/zstack/compute/allocator/PhysicalServerCapacityBuffers.javacompute/src/main/java/org/zstack/compute/allocator/PhysicalServerCapacityUpdater.javacompute/src/main/java/org/zstack/compute/cluster/ClusterExtensionPointEmitter.javacompute/src/main/java/org/zstack/compute/cluster/ClusterManagerImpl.javacompute/src/main/java/org/zstack/compute/host/HostManagerImpl.javacompute/src/main/java/org/zstack/compute/vm/StaticIpOperator.javacompute/src/main/java/org/zstack/compute/vm/VmCreateOnHypervisorFlow.javacompute/src/main/java/org/zstack/compute/vm/VmInstantiateResourceForChangeImageFlow.javacompute/src/main/java/org/zstack/compute/vm/VmInstantiateResourcePostFlow.javacompute/src/main/java/org/zstack/compute/vm/VmInstantiateResourcePreFlow.javacompute/src/main/java/org/zstack/compute/vm/VmReleaseResourceFlow.javacompute/src/main/java/org/zstack/compute/vm/VmStartOnHypervisorFlow.javacompute/src/main/java/org/zstack/compute/zone/ZoneExtensionPointEmitter.javacompute/src/main/java/org/zstack/compute/zone/ZoneManagerImpl.javacompute/src/test/java/org/zstack/compute/allocator/HostCpuOverProvisioningManagerImplTest.javacompute/src/test/java/org/zstack/compute/allocator/PhysicalServerCapacityUpdaterOrchestrationOverheadTest.javacompute/src/test/java/org/zstack/compute/allocator/PhysicalServerCapacityUpdaterTest.javacompute/src/test/resources/zstack.propertiesconf/db/upgrade/V5.5.18__schema.sqlcore/src/main/java/org/zstack/core/aspect/EncryptColumnAspect.ajcore/src/main/java/org/zstack/core/db/GLock.javadocs/STATUS.mddocs/decisions/ADR-001-hostcapacity-updater-static-resolve.mddocs/decisions/ADR-002-hostcapacity-updater-uuid-semantics.mddocs/decisions/ADR-003-hami-3field-flush.mddocs/decisions/ADR-004-psc-no-fk-vcenter.mddocs/decisions/ADR-005-hcv-view-algorithm-merge.mddocs/decisions/ADR-006-pspn-inplace-rename.mddocs/decisions/ADR-007-no-backup-tables.mddocs/decisions/ADR-008-fk-rename-follows-parent.mddocs/decisions/ADR-009-vcenter-option-c.mddocs/decisions/ADR-010-bm1-out-of-scope.mddocs/decisions/ADR-011-md5-salt-uuid-derivation.mddocs/decisions/ADR-012-roleprovider-pre-generated-role-uuid.mddocs/decisions/ADR-013-bm2-clusterref-table-not-view.mddocs/decisions/ADR-014-incremental-rebuild-antipattern.mddocs/decisions/README.mddocs/runbooks/physical-server-pxe-real-env-validation.mddocs/runbooks/testing-envs.mddocs/runbooks/v5518-recalculate-perf.mddocs/runbooks/v5518-sql-ddl-pitfalls.mddocs/runbooks/v5518-unified-hardware-rollback.mdheader/src/main/java/org/zstack/header/allocator/HostCapacityStruct.javaheader/src/main/java/org/zstack/header/allocator/HostCapacityVO.javaheader/src/main/java/org/zstack/header/allocator/ReportHostCapacityExtensionPoint.javaheader/src/main/java/org/zstack/header/allocator/ServerReservedCapacityExtensionPoint.javaheader/src/main/java/org/zstack/header/cluster/ClusterAO.javaheader/src/main/java/org/zstack/header/cluster/ClusterAO_.javaheader/src/main/java/org/zstack/header/cluster/ClusterCreateExtensionPoint.javaheader/src/main/java/org/zstack/header/cluster/ClusterInventory.javaheader/src/main/java/org/zstack/header/host/AddHostMessage.javaheader/src/main/java/org/zstack/header/server/APIAttachPhysicalServerRoleEvent.javaheader/src/main/java/org/zstack/header/server/APIAttachPhysicalServerRoleMsg.javaheader/src/main/java/org/zstack/header/server/APIAttachProvisionNetworkToClusterEvent.javaheader/src/main/java/org/zstack/header/server/APIAttachProvisionNetworkToClusterMsg.javaheader/src/main/java/org/zstack/header/server/APIAttachProvisionNetworkToPoolEvent.javaheader/src/main/java/org/zstack/header/server/APIAttachProvisionNetworkToPoolMsg.javaheader/src/main/java/org/zstack/header/server/APIChangeClusterServerPoolEvent.javaheader/src/main/java/org/zstack/header/server/APIChangeClusterServerPoolMsg.javaheader/src/main/java/org/zstack/header/server/APIChangePhysicalServerStateEvent.javaheader/src/main/java/org/zstack/header/server/APIChangePhysicalServerStateMsg.javaheader/src/main/java/org/zstack/header/server/APICreatePhysicalServerEvent.javaheader/src/main/java/org/zstack/header/server/APICreatePhysicalServerMsg.javaheader/src/main/java/org/zstack/header/server/APICreateProvisionNetworkEvent.javaheader/src/main/java/org/zstack/header/server/APICreateProvisionNetworkMsg.javaheader/src/main/java/org/zstack/header/server/APICreateServerPoolEvent.javaheader/src/main/java/org/zstack/header/server/APICreateServerPoolMsg.javaheader/src/main/java/org/zstack/header/server/APIDeletePhysicalServerEvent.javaheader/src/main/java/org/zstack/header/server/APIDeletePhysicalServerMsg.javaheader/src/main/java/org/zstack/header/server/APIDeleteProvisionNetworkEvent.javaheader/src/main/java/org/zstack/header/server/APIDeleteProvisionNetworkMsg.javaheader/src/main/java/org/zstack/header/server/APIDeleteServerPoolEvent.javaheader/src/main/java/org/zstack/header/server/APIDeleteServerPoolMsg.javaheader/src/main/java/org/zstack/header/server/APIDetachPhysicalServerRoleEvent.javaheader/src/main/java/org/zstack/header/server/APIDetachPhysicalServerRoleMsg.javaheader/src/main/java/org/zstack/header/server/APIDetachProvisionNetworkFromClusterEvent.javaheader/src/main/java/org/zstack/header/server/APIDetachProvisionNetworkFromClusterMsg.javaheader/src/main/java/org/zstack/header/server/APIDetachProvisionNetworkFromPoolEvent.javaheader/src/main/java/org/zstack/header/server/APIDetachProvisionNetworkFromPoolMsg.javaheader/src/main/java/org/zstack/header/server/APIDiscoverPhysicalServerHardwareEvent.javaheader/src/main/java/org/zstack/header/server/APIDiscoverPhysicalServerHardwareMsg.javaheader/src/main/java/org/zstack/header/server/APIPowerOffPhysicalServerEvent.javaheader/src/main/java/org/zstack/header/server/APIPowerOffPhysicalServerMsg.javaheader/src/main/java/org/zstack/header/server/APIPowerOnPhysicalServerEvent.javaheader/src/main/java/org/zstack/header/server/APIPowerOnPhysicalServerMsg.javaheader/src/main/java/org/zstack/header/server/APIPowerResetPhysicalServerEvent.javaheader/src/main/java/org/zstack/header/server/APIPowerResetPhysicalServerMsg.javaheader/src/main/java/org/zstack/header/server/APIProvisionPhysicalServerEvent.javaheader/src/main/java/org/zstack/header/server/APIProvisionPhysicalServerMsg.javaheader/src/main/java/org/zstack/header/server/APIQueryPhysicalServerMsg.javaheader/src/main/java/org/zstack/header/server/APIQueryPhysicalServerReply.javaheader/src/main/java/org/zstack/header/server/APIQueryPhysicalServerRoleMsg.javaheader/src/main/java/org/zstack/header/server/APIQueryPhysicalServerRoleReply.javaheader/src/main/java/org/zstack/header/server/APIQueryProvisionNetworkMsg.javaheader/src/main/java/org/zstack/header/server/APIQueryProvisionNetworkReply.javaheader/src/main/java/org/zstack/header/server/APIQueryServerPoolMsg.javaheader/src/main/java/org/zstack/header/server/APIQueryServerPoolReply.javaheader/src/main/java/org/zstack/header/server/APIScanPhysicalServersEvent.javaheader/src/main/java/org/zstack/header/server/APIScanPhysicalServersMsg.javaheader/src/main/java/org/zstack/header/server/APIUpdatePhysicalServerEvent.javaheader/src/main/java/org/zstack/header/server/APIUpdatePhysicalServerMsg.javaheader/src/main/java/org/zstack/header/server/APIUpdateProvisionNetworkEvent.javaheader/src/main/java/org/zstack/header/server/APIUpdateProvisionNetworkMsg.javaheader/src/main/java/org/zstack/header/server/APIUpdateServerPoolEvent.javaheader/src/main/java/org/zstack/header/server/APIUpdateServerPoolMsg.javaheader/src/main/java/org/zstack/header/server/CapacityUsage.javaheader/src/main/java/org/zstack/header/server/CreateRoleEntityContext.javaheader/src/main/java/org/zstack/header/server/PhysicalServerAO.javaheader/src/main/java/org/zstack/header/server/PhysicalServerAO_.javaheader/src/main/java/org/zstack/header/server/PhysicalServerCapacityState.javaheader/src/main/java/org/zstack/header/server/PhysicalServerCapacityVO.javaheader/src/main/java/org/zstack/header/server/PhysicalServerCapacityVO_.javaheader/src/main/java/org/zstack/header/server/PhysicalServerConstant.javaheader/src/main/java/org/zstack/header/server/PhysicalServerEnqueueDiscoveryHook.javaheader/src/main/java/org/zstack/header/server/PhysicalServerHardwareDetailVO.javaheader/src/main/java/org/zstack/header/server/PhysicalServerHardwareDetailVO_.javaheader/src/main/java/org/zstack/header/server/PhysicalServerHardwareDiscoveryExtensionPoint.javaheader/src/main/java/org/zstack/header/server/PhysicalServerHardwareInfoVO.javaheader/src/main/java/org/zstack/header/server/PhysicalServerHardwareInfoVO_.javaheader/src/main/java/org/zstack/header/server/PhysicalServerInventory.javaheader/src/main/java/org/zstack/header/server/PhysicalServerManager.javaheader/src/main/java/org/zstack/header/server/PhysicalServerPathTwoExtensionPoint.javaheader/src/main/java/org/zstack/header/server/PhysicalServerPowerStatus.javaheader/src/main/java/org/zstack/header/server/PhysicalServerProvisionDataPlane.javaheader/src/main/java/org/zstack/header/server/PhysicalServerProvisionNetworkClusterRefVO.javaheader/src/main/java/org/zstack/header/server/PhysicalServerProvisionNetworkClusterRefVO_.javaheader/src/main/java/org/zstack/header/server/PhysicalServerProvisionNetworkInventory.javaheader/src/main/java/org/zstack/header/server/PhysicalServerProvisionNetworkPoolRefVO.javaheader/src/main/java/org/zstack/header/server/PhysicalServerProvisionNetworkPoolRefVO_.javaheader/src/main/java/org/zstack/header/server/PhysicalServerProvisionNetworkVO.javaheader/src/main/java/org/zstack/header/server/PhysicalServerProvisionNetworkVO_.javaheader/src/main/java/org/zstack/header/server/PhysicalServerProvisionTarget.javaheader/src/main/java/org/zstack/header/server/PhysicalServerRoleInventory.javaheader/src/main/java/org/zstack/header/server/PhysicalServerRoleProvider.javaheader/src/main/java/org/zstack/header/server/PhysicalServerRoleVO.javaheader/src/main/java/org/zstack/header/server/PhysicalServerRoleVO_.javaheader/src/main/java/org/zstack/header/server/PhysicalServerState.javaheader/src/main/java/org/zstack/header/server/PhysicalServerStateEvent.javaheader/src/main/java/org/zstack/header/server/PhysicalServerVO.javaheader/src/main/java/org/zstack/header/server/ProvisionNetworkState.javaheader/src/main/java/org/zstack/header/server/ProvisionNetworkType.javaheader/src/main/java/org/zstack/header/server/ProvisionPhase.javaheader/src/main/java/org/zstack/header/server/ProvisionProvider.javaheader/src/main/java/org/zstack/header/server/ProvisionRequest.javaheader/src/main/java/org/zstack/header/server/ProvisionResult.javaheader/src/main/java/org/zstack/header/server/RoleMatchContext.javaheader/src/main/java/org/zstack/header/server/RoleWorkloadStatus.javaheader/src/main/java/org/zstack/header/server/SchedulingMode.javaheader/src/main/java/org/zstack/header/server/ServerPoolInventory.javaheader/src/main/java/org/zstack/header/server/ServerPoolState.javaheader/src/main/java/org/zstack/header/server/ServerPoolVO.javaheader/src/main/java/org/zstack/header/server/ServerPoolVO_.javaheader/src/main/java/org/zstack/header/server/ServerRoleType.javaheader/src/main/java/org/zstack/header/server/WorkloadRef.javaheader/src/main/java/org/zstack/header/server/flow/PathTwoFlowDataKey.javaheader/src/main/java/org/zstack/header/zone/ZoneCreateExtensionPoint.javaidentity/src/main/java/org/zstack/identity/QuotaAPIRequestChecker.javaidentity/src/main/java/org/zstack/identity/QuotaUtil.javalongjob/src/main/java/org/zstack/longjob/LongJobManagerImpl.javaplugin/kvm/src/main/java/org/zstack/kvm/APIAddKVMHostMsg.javaplugin/kvm/src/main/java/org/zstack/kvm/AddKVMHostMsg.javaplugin/kvm/src/main/java/org/zstack/kvm/KVMHost.javaplugin/kvm/src/main/java/org/zstack/kvm/KvmPhysicalServerRoleSoftDeleteExtension.javaplugin/kvm/src/main/java/org/zstack/kvm/KvmRoleProvider.javaplugin/kvm/src/main/java/org/zstack/kvm/server/KvmHardwareDiscoveryAdapter.javaplugin/kvm/src/test/java/org/zstack/core/Platform.javaplugin/kvm/src/test/java/org/zstack/kvm/KvmRoleProviderTest.javaplugin/kvm/src/test/resources/zstack.propertiesplugin/physicalServer/src/main/java/org/zstack/server/DefaultServerPoolClusterCreateExtension.javaplugin/physicalServer/src/main/java/org/zstack/server/DefaultServerPoolCreationPolicy.javaplugin/physicalServer/src/main/java/org/zstack/server/DefaultServerPoolFactory.javaplugin/physicalServer/src/main/java/org/zstack/server/DefaultServerPoolZoneCreateExtension.javaplugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerApiInterceptor.javaplugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerAutoAssociator.javaplugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerCapacityCascadeExtension.javaplugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerCascadeExtension.javaplugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerGlobalConfig.javaplugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerHardwareDetailCascadeExtension.javaplugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerHardwareInfoCascadeExtension.javaplugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerIpmiPowerExecutor.javaplugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerManagerImpl.javaplugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerPathTwoContributor.javaplugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerPathTwoOrchestrator.javaplugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerProvisionNetworkCascadeExtension.javaplugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerProvisionService.javaplugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerRoleCascadeExtension.javaplugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerScanner.javaplugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerStandalonePxeProvisionProvider.javaplugin/physicalServer/src/main/java/org/zstack/server/ProvisionPhysicalServerLongJob.javaplugin/physicalServer/src/main/java/org/zstack/server/ServerPoolCascadeExtension.javaplugin/physicalServer/src/main/java/org/zstack/server/flow/AutoAssociateFlow.javaplugin/physicalServer/src/main/java/org/zstack/server/flow/CreatePhysicalServerRoleFlow.javaplugin/physicalServer/src/main/java/org/zstack/server/flow/InitPhysicalServerCapacityFlow.javaplugin/physicalServer/src/main/java/org/zstack/server/hardware/HardwareDiscoveryScheduler.javaplugin/physicalServer/src/main/java/org/zstack/server/hardware/PhysicalServerEnqueueDiscoveryHookImpl.javaplugin/physicalServer/src/main/java/org/zstack/server/hardware/PhysicalServerHardwareService.javaplugin/physicalServer/src/main/java/org/zstack/server/hardware/UnifiedHardwareInfo.javaplugin/physicalServer/src/test/java/org/zstack/server/hardware/UnifiedHardwareInfoMergeTest.javaportal/src/main/java/org/zstack/portal/apimediator/ApiMessageProcessorImpl.javaresourceconfig/src/main/java/org/zstack/resourceconfig/ResourceConfig.javascripts/mvn-safe-install.shscripts/run-case.shsearch/src/main/java/org/zstack/query/QueryFacadeImpl.javastorage/src/main/java/org/zstack/storage/primary/PrimaryStorageFeatureAllocatorFlow.javastorage/src/main/java/org/zstack/storage/primary/PrimaryStorageTagAllocatorFlow.javatag/src/main/java/org/zstack/tag/PatternedSystemTag.javatag/src/main/java/org/zstack/tag/SystemTag.javatest/src/test/groovy/org/zstack/test/integration/core/taskqueue/CoalesceQueueCase.groovytest/src/test/groovy/org/zstack/test/integration/kvm/AddKvmHostPath2Case.groovytest/src/test/groovy/org/zstack/test/integration/kvm/KvmReportHostCapacityRecalcCase.groovytest/src/test/groovy/org/zstack/test/integration/kvm/KvmRoleProviderIntegrationCase.groovytest/src/test/groovy/org/zstack/test/integration/kvm/KvmTest.groovytest/src/test/groovy/org/zstack/test/integration/server/PhysicalServerCascadeCase.groovy
💤 Files with no reviewable changes (2)
- header/src/main/java/org/zstack/header/allocator/ReportHostCapacityExtensionPoint.java
- header/src/main/java/org/zstack/header/allocator/HostCapacityStruct.java
✅ Files skipped from review due to trivial changes (50)
- header/src/main/java/org/zstack/header/server/PhysicalServerCapacityState.java
- header/src/main/java/org/zstack/header/server/ProvisionNetworkType.java
- plugin/kvm/src/test/resources/zstack.properties
- header/src/main/java/org/zstack/header/server/PhysicalServerConstant.java
- header/src/main/java/org/zstack/header/server/PhysicalServerEnqueueDiscoveryHook.java
- docs/decisions/README.md
- header/src/main/java/org/zstack/header/server/APIDetachProvisionNetworkFromClusterEvent.java
- header/src/main/java/org/zstack/header/server/CapacityUsage.java
- header/src/main/java/org/zstack/header/server/PhysicalServerRoleVO_.java
- header/src/main/java/org/zstack/header/server/APIDeletePhysicalServerEvent.java
- header/src/main/java/org/zstack/header/server/APIAttachProvisionNetworkToPoolEvent.java
- header/src/main/java/org/zstack/header/server/APIQueryServerPoolReply.java
- header/src/main/java/org/zstack/header/server/ServerPoolVO_.java
- header/src/main/java/org/zstack/header/server/APIDeleteProvisionNetworkEvent.java
- header/src/main/java/org/zstack/header/server/APIQueryPhysicalServerMsg.java
- header/src/main/java/org/zstack/header/server/PhysicalServerProvisionNetworkClusterRefVO_.java
- header/src/main/java/org/zstack/header/server/PhysicalServerManager.java
- header/src/main/java/org/zstack/header/server/PhysicalServerProvisionNetworkPoolRefVO.java
- header/src/main/java/org/zstack/header/server/APIDeleteServerPoolMsg.java
- header/src/main/java/org/zstack/header/server/SchedulingMode.java
- header/src/main/java/org/zstack/header/host/AddHostMessage.java
- header/src/main/java/org/zstack/header/server/APIPowerOffPhysicalServerEvent.java
- compute/src/main/java/org/zstack/compute/vm/VmReleaseResourceFlow.java
- header/src/main/java/org/zstack/header/cluster/ClusterCreateExtensionPoint.java
- plugin/physicalServer/src/main/java/org/zstack/server/DefaultServerPoolFactory.java
- header/src/main/java/org/zstack/header/server/RoleMatchContext.java
- docs/decisions/ADR-004-psc-no-fk-vcenter.md
- header/src/main/java/org/zstack/header/server/APIQueryPhysicalServerReply.java
- scripts/run-case.sh
- header/src/main/java/org/zstack/header/server/PhysicalServerStateEvent.java
- header/src/main/java/org/zstack/header/server/PhysicalServerProvisionNetworkVO_.java
- docs/decisions/ADR-008-fk-rename-follows-parent.md
- header/src/main/java/org/zstack/header/server/ServerRoleType.java
- .gitignore
- docs/decisions/ADR-011-md5-salt-uuid-derivation.md
- docs/decisions/ADR-003-hami-3field-flush.md
- header/src/main/java/org/zstack/header/server/APIDetachProvisionNetworkFromClusterMsg.java
- docs/decisions/ADR-014-incremental-rebuild-antipattern.md
- docs/decisions/ADR-007-no-backup-tables.md
- docs/decisions/ADR-010-bm1-out-of-scope.md
- docs/decisions/ADR-006-pspn-inplace-rename.md
- header/src/main/java/org/zstack/header/server/PhysicalServerCapacityVO_.java
- docs/decisions/ADR-009-vcenter-option-c.md
- docs/decisions/ADR-001-hostcapacity-updater-static-resolve.md
- docs/decisions/ADR-002-hostcapacity-updater-uuid-semantics.md
- plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java
- header/src/main/java/org/zstack/header/server/PhysicalServerAO_.java
- header/src/main/java/org/zstack/header/server/APIQueryPhysicalServerRoleMsg.java
- header/src/main/java/org/zstack/header/server/PhysicalServerHardwareInfoVO_.java
- plugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerGlobalConfig.java
🚧 Files skipped from review as they are similar to previous changes (132)
- header/src/main/java/org/zstack/header/server/PhysicalServerProvisionDataPlane.java
- header/src/main/java/org/zstack/header/allocator/HostCapacityVO.java
- header/src/main/java/org/zstack/header/server/ProvisionNetworkState.java
- compute/src/main/java/org/zstack/compute/vm/VmStartOnHypervisorFlow.java
- header/src/main/java/org/zstack/header/server/APIDetachProvisionNetworkFromPoolEvent.java
- header/src/main/java/org/zstack/header/server/ProvisionResult.java
- plugin/physicalServer/src/main/java/org/zstack/server/DefaultServerPoolCreationPolicy.java
- header/src/main/java/org/zstack/header/cluster/ClusterAO.java
- header/src/main/java/org/zstack/header/server/APIScanPhysicalServersEvent.java
- plugin/kvm/src/main/java/org/zstack/kvm/APIAddKVMHostMsg.java
- header/src/main/java/org/zstack/header/server/PhysicalServerPowerStatus.java
- header/src/main/java/org/zstack/header/server/APIProvisionPhysicalServerEvent.java
- header/src/main/java/org/zstack/header/cluster/ClusterAO_.java
- core/src/main/java/org/zstack/core/db/GLock.java
- header/src/main/java/org/zstack/header/server/ServerPoolState.java
- header/src/main/java/org/zstack/header/server/APIPowerOnPhysicalServerMsg.java
- plugin/physicalServer/src/main/java/org/zstack/server/DefaultServerPoolZoneCreateExtension.java
- compute/src/main/java/org/zstack/compute/vm/VmCreateOnHypervisorFlow.java
- header/src/main/java/org/zstack/header/server/WorkloadRef.java
- header/src/main/java/org/zstack/header/server/APIPowerResetPhysicalServerMsg.java
- compute/src/main/java/org/zstack/compute/vm/VmInstantiateResourcePostFlow.java
- header/src/main/java/org/zstack/header/server/APIDeleteServerPoolEvent.java
- header/src/main/java/org/zstack/header/server/APIQueryProvisionNetworkReply.java
- core/src/main/java/org/zstack/core/aspect/EncryptColumnAspect.aj
- compute/src/test/resources/zstack.properties
- header/src/main/java/org/zstack/header/server/PhysicalServerHardwareDetailVO_.java
- header/src/main/java/org/zstack/header/server/APIUpdatePhysicalServerEvent.java
- header/src/main/java/org/zstack/header/server/APICreateServerPoolEvent.java
- compute/src/main/java/org/zstack/compute/zone/ZoneManagerImpl.java
- header/src/main/java/org/zstack/header/server/APICreateProvisionNetworkMsg.java
- plugin/physicalServer/src/main/java/org/zstack/server/ProvisionPhysicalServerLongJob.java
- header/src/main/java/org/zstack/header/server/flow/PathTwoFlowDataKey.java
- header/src/main/java/org/zstack/header/server/PhysicalServerState.java
- compute/src/main/java/org/zstack/compute/vm/VmInstantiateResourcePreFlow.java
- header/src/main/java/org/zstack/header/server/APIChangePhysicalServerStateEvent.java
- longjob/src/main/java/org/zstack/longjob/LongJobManagerImpl.java
- header/src/main/java/org/zstack/header/server/APICreateProvisionNetworkEvent.java
- header/src/main/java/org/zstack/header/server/APIDiscoverPhysicalServerHardwareEvent.java
- header/src/main/java/org/zstack/header/server/APIDetachProvisionNetworkFromPoolMsg.java
- plugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerPathTwoContributor.java
- compute/src/main/java/org/zstack/compute/allocator/PhysicalServerCapacityBuffers.java
- header/src/main/java/org/zstack/header/server/PhysicalServerProvisionNetworkPoolRefVO_.java
- header/src/main/java/org/zstack/header/server/APIAttachProvisionNetworkToClusterEvent.java
- compute/src/main/java/org/zstack/compute/cluster/ClusterManagerImpl.java
- plugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerProvisionService.java
- header/src/main/java/org/zstack/header/server/APIAttachPhysicalServerRoleMsg.java
- plugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerProvisionNetworkCascadeExtension.java
- header/src/main/java/org/zstack/header/server/APIQueryProvisionNetworkMsg.java
- header/src/main/java/org/zstack/header/server/APIUpdateServerPoolEvent.java
- header/src/main/java/org/zstack/header/server/APIDetachPhysicalServerRoleMsg.java
- compute/src/test/java/org/zstack/compute/allocator/HostCpuOverProvisioningManagerImplTest.java
- header/src/main/java/org/zstack/header/server/PhysicalServerHardwareDiscoveryExtensionPoint.java
- header/src/main/java/org/zstack/header/server/APIPowerResetPhysicalServerEvent.java
- header/src/main/java/org/zstack/header/server/PhysicalServerAO.java
- header/src/main/java/org/zstack/header/server/APIPowerOffPhysicalServerMsg.java
- identity/src/main/java/org/zstack/identity/QuotaAPIRequestChecker.java
- header/src/main/java/org/zstack/header/server/APIAttachProvisionNetworkToClusterMsg.java
- plugin/physicalServer/src/main/java/org/zstack/server/flow/AutoAssociateFlow.java
- plugin/physicalServer/src/main/java/org/zstack/server/DefaultServerPoolClusterCreateExtension.java
- header/src/main/java/org/zstack/header/server/APICreateServerPoolMsg.java
- header/src/main/java/org/zstack/header/server/PhysicalServerHardwareInfoVO.java
- plugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerPathTwoOrchestrator.java
- header/src/main/java/org/zstack/header/server/APIUpdatePhysicalServerMsg.java
- header/src/main/java/org/zstack/header/server/PhysicalServerInventory.java
- compute/src/main/java/org/zstack/compute/allocator/HostCapacityOverProvisioningManagerImpl.java
- header/src/main/java/org/zstack/header/server/PhysicalServerRoleVO.java
- header/src/main/java/org/zstack/header/server/PhysicalServerRoleProvider.java
- header/src/main/java/org/zstack/header/server/PhysicalServerPathTwoExtensionPoint.java
- plugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerHardwareDetailCascadeExtension.java
- plugin/kvm/src/main/java/org/zstack/kvm/KvmPhysicalServerRoleSoftDeleteExtension.java
- header/src/main/java/org/zstack/header/server/APICreatePhysicalServerMsg.java
- header/src/main/java/org/zstack/header/server/APIDeletePhysicalServerMsg.java
- header/src/main/java/org/zstack/header/server/APIAttachProvisionNetworkToPoolMsg.java
- header/src/main/java/org/zstack/header/server/APIUpdateProvisionNetworkEvent.java
- header/src/main/java/org/zstack/header/server/PhysicalServerProvisionNetworkClusterRefVO.java
- header/src/main/java/org/zstack/header/server/APICreatePhysicalServerEvent.java
- header/src/main/java/org/zstack/header/server/APIDetachPhysicalServerRoleEvent.java
- compute/src/main/java/org/zstack/compute/allocator/HostAllocatorGlobalConfig.java
- header/src/main/java/org/zstack/header/server/APIChangeClusterServerPoolMsg.java
- header/src/main/java/org/zstack/header/server/ProvisionProvider.java
- header/src/main/java/org/zstack/header/server/ServerPoolVO.java
- plugin/kvm/src/test/java/org/zstack/core/Platform.java
- header/src/main/java/org/zstack/header/zone/ZoneCreateExtensionPoint.java
- plugin/physicalServer/src/main/java/org/zstack/server/flow/CreatePhysicalServerRoleFlow.java
- compute/src/main/java/org/zstack/compute/vm/StaticIpOperator.java
- plugin/physicalServer/src/main/java/org/zstack/server/flow/InitPhysicalServerCapacityFlow.java
- header/src/main/java/org/zstack/header/server/PhysicalServerProvisionTarget.java
- compute/src/main/java/org/zstack/compute/zone/ZoneExtensionPointEmitter.java
- header/src/main/java/org/zstack/header/server/CreateRoleEntityContext.java
- header/src/main/java/org/zstack/header/server/APIChangePhysicalServerStateMsg.java
- header/src/main/java/org/zstack/header/server/APIUpdateServerPoolMsg.java
- header/src/main/java/org/zstack/header/server/APIDiscoverPhysicalServerHardwareMsg.java
- plugin/kvm/src/main/java/org/zstack/kvm/AddKVMHostMsg.java
- header/src/main/java/org/zstack/header/server/APIQueryServerPoolMsg.java
- header/src/main/java/org/zstack/header/server/APIScanPhysicalServersMsg.java
- header/src/main/java/org/zstack/header/server/APIDeleteProvisionNetworkMsg.java
- portal/src/main/java/org/zstack/portal/apimediator/ApiMessageProcessorImpl.java
- plugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerCapacityCascadeExtension.java
- header/src/main/java/org/zstack/header/server/PhysicalServerProvisionNetworkVO.java
- compute/src/main/java/org/zstack/compute/host/HostManagerImpl.java
- identity/src/main/java/org/zstack/identity/QuotaUtil.java
- header/src/main/java/org/zstack/header/server/PhysicalServerProvisionNetworkInventory.java
- plugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerIpmiPowerExecutor.java
- header/src/main/java/org/zstack/header/allocator/ServerReservedCapacityExtensionPoint.java
- plugin/physicalServer/src/main/java/org/zstack/server/hardware/PhysicalServerHardwareService.java
- plugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerCascadeExtension.java
- header/src/main/java/org/zstack/header/server/RoleWorkloadStatus.java
- header/src/main/java/org/zstack/header/server/ProvisionRequest.java
- header/src/main/java/org/zstack/header/server/PhysicalServerHardwareDetailVO.java
- header/src/main/java/org/zstack/header/server/PhysicalServerRoleInventory.java
- header/src/main/java/org/zstack/header/cluster/ClusterInventory.java
- compute/src/main/java/org/zstack/compute/allocator/HostCapacityUpdater.java
- header/src/main/java/org/zstack/header/server/ServerPoolInventory.java
- plugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerRoleCascadeExtension.java
- plugin/kvm/src/main/java/org/zstack/kvm/KvmRoleProvider.java
- plugin/physicalServer/src/main/java/org/zstack/server/hardware/PhysicalServerEnqueueDiscoveryHookImpl.java
- plugin/kvm/src/main/java/org/zstack/kvm/server/KvmHardwareDiscoveryAdapter.java
- compute/src/main/java/org/zstack/compute/vm/VmInstantiateResourceForChangeImageFlow.java
- plugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerManagerImpl.java
- plugin/physicalServer/src/main/java/org/zstack/server/hardware/UnifiedHardwareInfo.java
- compute/src/test/java/org/zstack/compute/allocator/PhysicalServerCapacityUpdaterOrchestrationOverheadTest.java
- header/src/main/java/org/zstack/header/server/APIAttachPhysicalServerRoleEvent.java
- compute/src/main/java/org/zstack/compute/allocator/HostAllocatorManagerImpl.java
- compute/src/main/java/org/zstack/compute/allocator/PhysicalServerCapacityUpdater.java
- plugin/physicalServer/src/test/java/org/zstack/server/hardware/UnifiedHardwareInfoMergeTest.java
- plugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerHardwareInfoCascadeExtension.java
- compute/src/test/java/org/zstack/compute/allocator/PhysicalServerCapacityUpdaterTest.java
- plugin/physicalServer/src/main/java/org/zstack/server/ServerPoolCascadeExtension.java
- compute/src/main/java/org/zstack/compute/allocator/HostCpuOverProvisioningManagerImpl.java
- conf/db/upgrade/V5.5.18__schema.sql
- plugin/physicalServer/src/main/java/org/zstack/server/hardware/HardwareDiscoveryScheduler.java
- plugin/kvm/src/test/java/org/zstack/kvm/KvmRoleProviderTest.java
|
|
||
| ### 1.3 Boundary: Simulator vs Real Harness | ||
|
|
||
| ``` |
There was a problem hiding this comment.
多个 fenced code block 缺少语言标识,持续触发 markdownlint MD040
建议给这些代码块补充语言(如 bash、sql、json、text),可直接清理静态检查告警。
🧩 示例修复(同类块请统一处理)
-```
+```text
┌─────────────────────────────────────────────────────────────┐
...
-```
+```
-```
+```text
Time Source Dest Protocol Payload
...
-```
+```Also applies to: 107-107, 138-138, 390-390, 450-450, 787-787, 951-951, 1046-1046, 1072-1072, 1101-1101
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 39-39: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 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 `@docs/runbooks/physical-server-pxe-real-env-validation.md` at line 39,
Multiple fenced code blocks in the markdown are missing language identifiers
(triggering MD040); update each triple-backtick code block in this document to
include an appropriate language tag (e.g., bash, text, json, sql) consistent
with the content—replace ``` with ```text or ```bash etc., and apply the same
change to the example blocks shown in the review so all occurrences are
annotated uniformly to satisfy markdownlint.
| - **Focused Harness Tests (Simulator):** `premium/test-premium/src/test/groovy/org/zstack/test/integration/baremetal2/ProvisionPhysicalServerBm2Case.groovy` (Unit/integration, not real hardware) | ||
| - **Provider Interface:** `plugin/physicalServer/src/main/java/org/zstack/server/ProvisionProvider.java` | ||
| - **LongJob API:** `APIProvisionPhysicalServerMsg` in `header/` | ||
| - **PRD Reference:** `/home/mj/zstack-workspace/cloud_prd/prd/v5.5.18-unified-hardware/provision/feat-unified_provision_network_prd.md` (§2.3 PhysicalServer-first provision) |
There was a problem hiding this comment.
PRD 引用使用本地绝对路径,仓库内不可复现
这里的 /home/... 路径对其他读者不可访问,建议改成仓库内相对路径或可共享链接,避免 runbook 断链。
🤖 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 `@docs/runbooks/physical-server-pxe-real-env-validation.md` at line 897, The
PRD Reference currently uses a local absolute path
`/home/mj/zstack-workspace/cloud_prd/prd/v5.5.18-unified-hardware/provision/feat-unified_provision_network_prd.md`
which cannot be reproduced by others; update the PRD Reference line in
physical-server-pxe-real-env-validation.md to point to a repository-relative
path (e.g., docs/prd/... or relative path within the repo) or a shared URL (PRD
in the repo or a web link) so other readers can access it; locate the line
containing the exact string
`/home/mj/zstack-workspace/cloud_prd/.../feat-unified_provision_network_prd.md`
and replace it with the chosen relative path or link.
| @Action(adminOnly = true, category = PhysicalServerConstant.ACTION_CATEGORY) | ||
| @RestRequest(path = "/provision-networks/{uuid}/actions", method = HttpMethod.PUT, isAction = true, responseClass = APIUpdateProvisionNetworkEvent.class) | ||
| public class APIUpdateProvisionNetworkMsg extends APIMessage { |
There was a problem hiding this comment.
补充 @RestResponse 标注以满足 API 返回契约规范
当前类已声明 @RestRequest(...responseClass=...),但未看到 @RestResponse 标注(或等效注释标注)。按仓库规范,这一项需要显式补齐。
As per coding guidelines: "API 消息类需要继承 APIMessage;其返回类必须继承 APIReply 或 APIEvent,并在注释中用 @RestResponse 进行标注。"
🤖 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/server/APIUpdateProvisionNetworkMsg.java`
around lines 9 - 11, The class APIUpdateProvisionNetworkMsg is missing the
required `@RestResponse` annotation for its declared response class; add a
`@RestResponse`(APIUpdateProvisionNetworkEvent.class) annotation on the
APIUpdateProvisionNetworkMsg class declaration so the response contract matches
the `@RestRequest`(responseClass=...) and satisfies the repository convention that
API message classes declare their reply/event via `@RestResponse`.
| public void setUuid(String uuid) { | ||
| this.uuid = uuid; | ||
| } | ||
|
|
||
| public String getName() { | ||
| return name; | ||
| } | ||
|
|
||
| public void setName(String name) { | ||
| this.name = name; | ||
| } | ||
|
|
||
| public String getDescription() { | ||
| return description; | ||
| } | ||
|
|
||
| public void setDescription(String description) { | ||
| this.description = description; | ||
| } | ||
|
|
||
| public String getDhcpInterface() { | ||
| return dhcpInterface; | ||
| } | ||
|
|
||
| public void setDhcpInterface(String dhcpInterface) { | ||
| this.dhcpInterface = dhcpInterface; | ||
| } | ||
|
|
||
| public String getDhcpRangeStartIp() { | ||
| return dhcpRangeStartIp; | ||
| } | ||
|
|
||
| public void setDhcpRangeStartIp(String dhcpRangeStartIp) { | ||
| this.dhcpRangeStartIp = dhcpRangeStartIp; | ||
| } | ||
|
|
||
| public String getDhcpRangeEndIp() { | ||
| return dhcpRangeEndIp; | ||
| } | ||
|
|
||
| public void setDhcpRangeEndIp(String dhcpRangeEndIp) { | ||
| this.dhcpRangeEndIp = dhcpRangeEndIp; | ||
| } | ||
|
|
||
| public String getDhcpRangeNetmask() { | ||
| return dhcpRangeNetmask; | ||
| } | ||
|
|
||
| public void setDhcpRangeNetmask(String dhcpRangeNetmask) { | ||
| this.dhcpRangeNetmask = dhcpRangeNetmask; | ||
| } | ||
|
|
||
| public String getDhcpRangeGateway() { | ||
| return dhcpRangeGateway; | ||
| } | ||
|
|
||
| public void setDhcpRangeGateway(String dhcpRangeGateway) { | ||
| this.dhcpRangeGateway = dhcpRangeGateway; | ||
| } |
There was a problem hiding this comment.
建议在 Message 字符串入参的 setter 中统一做 trim()
uuid/name/dhcp* 当前均直接赋值,建议做 null 安全的 trim() 归一化,避免复制粘贴导致的首尾空格/换行进入持久化或匹配流程。
🔧 可参考的最小改动
+ private String normalize(String value) {
+ return value == null ? null : value.trim();
+ }
+
public void setUuid(String uuid) {
- this.uuid = uuid;
+ this.uuid = normalize(uuid);
}
@@
public void setName(String name) {
- this.name = name;
+ this.name = normalize(name);
}
@@
public void setDhcpInterface(String dhcpInterface) {
- this.dhcpInterface = dhcpInterface;
+ this.dhcpInterface = normalize(dhcpInterface);
}
@@
public void setDhcpRangeStartIp(String dhcpRangeStartIp) {
- this.dhcpRangeStartIp = dhcpRangeStartIp;
+ this.dhcpRangeStartIp = normalize(dhcpRangeStartIp);
}
@@
public void setDhcpRangeEndIp(String dhcpRangeEndIp) {
- this.dhcpRangeEndIp = dhcpRangeEndIp;
+ this.dhcpRangeEndIp = normalize(dhcpRangeEndIp);
}
@@
public void setDhcpRangeNetmask(String dhcpRangeNetmask) {
- this.dhcpRangeNetmask = dhcpRangeNetmask;
+ this.dhcpRangeNetmask = normalize(dhcpRangeNetmask);
}
@@
public void setDhcpRangeGateway(String dhcpRangeGateway) {
- this.dhcpRangeGateway = dhcpRangeGateway;
+ this.dhcpRangeGateway = normalize(dhcpRangeGateway);
}As per coding guidelines: "注意检查来自 Message 的参数是否做过 trim,用户可能在浏览器上复制粘贴的数据带有空格、换行符等。"
🤖 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/server/APIUpdateProvisionNetworkMsg.java`
around lines 40 - 98, The setters for Message string fields (setUuid, setName,
setDhcpInterface, setDhcpRangeStartIp, setDhcpRangeEndIp, setDhcpRangeNetmask,
setDhcpRangeGateway) currently assign values directly; change each to perform a
null-safe trim before assignment (e.g., assign field = input == null ? null :
input.trim()) so leading/trailing whitespace/newlines are normalized and won't
leak into persistence or matching logic.
| @PreUpdate | ||
| private void preUpdate() { | ||
| lastOpDate = null; | ||
| } |
There was a problem hiding this comment.
@PreUpdate 中将 lastOpDate 设为 null 是一个 bug。
更新时应该将 lastOpDate 设置为当前时间戳,而不是 null。当前实现会导致每次更新后 lastOpDate 变为 NULL,这与 ZStack 的约定(在更新时刷新最后操作时间)不符。
🐛 修复建议
`@PreUpdate`
private void preUpdate() {
- lastOpDate = null;
+ lastOpDate = new Timestamp(System.currentTimeMillis());
}📝 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.
| @PreUpdate | |
| private void preUpdate() { | |
| lastOpDate = null; | |
| } | |
| `@PreUpdate` | |
| private void preUpdate() { | |
| lastOpDate = new Timestamp(System.currentTimeMillis()); | |
| } |
🤖 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/server/PhysicalServerCapacityVO.java`
around lines 117 - 120, The `@PreUpdate` hook in PhysicalServerCapacityVO
currently nulls out lastOpDate, which is wrong; change the preUpdate() method to
set the lastOpDate field to the current timestamp (i.e., assign the current time
/ new timestamp to lastOpDate) instead of null so the entity refreshes its last
operation time on updates; update the preUpdate() implementation in class
PhysicalServerCapacityVO (method preUpdate and field lastOpDate) accordingly.
| private void validate(APICreatePhysicalServerMsg msg) { | ||
| if (!NetworkUtils.isIpv4Address(msg.getManagementIp())) { | ||
| throw new ApiMessageInterceptionException(argerr("invalid managementIp[%s]", msg.getManagementIp())); | ||
| } | ||
|
|
||
| // Validate poolUuid belongs to same zone | ||
| if (msg.getPoolUuid() != null && msg.getZoneUuid() != null) { | ||
| ServerPoolVO pool = dbf.findByUuid(msg.getPoolUuid(), ServerPoolVO.class); | ||
| if (pool != null && !pool.getZoneUuid().equals(msg.getZoneUuid())) { | ||
| throw new ApiMessageInterceptionException(argerr( | ||
| "ServerPool[uuid:%s] belongs to Zone[uuid:%s], but PhysicalServer specifies Zone[uuid:%s]", | ||
| msg.getPoolUuid(), pool.getZoneUuid(), msg.getZoneUuid() | ||
| )); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private void validate(APIUpdatePhysicalServerMsg msg) { | ||
| if (msg.getManagementIp() != null && !NetworkUtils.isIpv4Address(msg.getManagementIp())) { | ||
| throw new ApiMessageInterceptionException(argerr("invalid managementIp[%s]", msg.getManagementIp())); | ||
| } | ||
| } |
There was a problem hiding this comment.
IP 地址验证前应先进行 trim 处理。
用户从浏览器复制粘贴 IP 地址时可能带有空格或换行符。当前实现直接验证未经处理的输入,可能导致有效 IP 被误判为无效。
🛠️ 建议修复
private void validate(APICreatePhysicalServerMsg msg) {
- if (!NetworkUtils.isIpv4Address(msg.getManagementIp())) {
- throw new ApiMessageInterceptionException(argerr("invalid managementIp[%s]", msg.getManagementIp()));
+ String managementIp = msg.getManagementIp() == null ? null : msg.getManagementIp().trim();
+ if (!NetworkUtils.isIpv4Address(managementIp)) {
+ throw new ApiMessageInterceptionException(argerr("invalid managementIp[%s]", managementIp));
}
+ msg.setManagementIp(managementIp);
// Validate poolUuid belongs to same zone
if (msg.getPoolUuid() != null && msg.getZoneUuid() != null) { private void validate(APIUpdatePhysicalServerMsg msg) {
- if (msg.getManagementIp() != null && !NetworkUtils.isIpv4Address(msg.getManagementIp())) {
- throw new ApiMessageInterceptionException(argerr("invalid managementIp[%s]", msg.getManagementIp()));
+ if (msg.getManagementIp() != null) {
+ String managementIp = msg.getManagementIp().trim();
+ if (!NetworkUtils.isIpv4Address(managementIp)) {
+ throw new ApiMessageInterceptionException(argerr("invalid managementIp[%s]", managementIp));
+ }
+ msg.setManagementIp(managementIp);
}
}As per coding guidelines: "注意检查来自 Message 的参数是否做过 trim,用户可能在浏览器上复制粘贴的数据带有空格、换行符等"
🤖 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/physicalServer/src/main/java/org/zstack/server/PhysicalServerApiInterceptor.java`
around lines 44 - 65, Trim the managementIp before validating in both
validate(APICreatePhysicalServerMsg) and validate(APIUpdatePhysicalServerMsg):
call trim() on msg.getManagementIp() (handle nulls) and use the trimmed value
for NetworkUtils.isIpv4Address checks and in error messages; ensure you do not
modify other logic (e.g., pool/zone validation) and use the trimmed value
consistently when constructing the argerr message.
| private void validate(APICreateProvisionNetworkMsg msg) { | ||
| if (msg.getDhcpRangeStartIp() != null && !NetworkUtils.isIpv4Address(msg.getDhcpRangeStartIp())) { | ||
| throw new ApiMessageInterceptionException(argerr("invalid dhcpRangeStartIp[%s]", msg.getDhcpRangeStartIp())); | ||
| } | ||
| if (msg.getDhcpRangeEndIp() != null && !NetworkUtils.isIpv4Address(msg.getDhcpRangeEndIp())) { | ||
| throw new ApiMessageInterceptionException(argerr("invalid dhcpRangeEndIp[%s]", msg.getDhcpRangeEndIp())); | ||
| } | ||
| if (msg.getDhcpRangeNetmask() != null && !NetworkUtils.isIpv4Address(msg.getDhcpRangeNetmask())) { | ||
| throw new ApiMessageInterceptionException(argerr("invalid dhcpRangeNetmask[%s]", msg.getDhcpRangeNetmask())); | ||
| } | ||
| if (msg.getDhcpRangeGateway() != null && !NetworkUtils.isIpv4Address(msg.getDhcpRangeGateway())) { | ||
| throw new ApiMessageInterceptionException(argerr("invalid dhcpRangeGateway[%s]", msg.getDhcpRangeGateway())); | ||
| } | ||
| } |
There was a problem hiding this comment.
DHCP 范围 IP 字段同样需要 trim 处理。
与 managementIp 相同的问题,dhcpRangeStartIp、dhcpRangeEndIp、dhcpRangeNetmask、dhcpRangeGateway 这些字段在验证前也应先 trim。
🛠️ 建议修复
private void validate(APICreateProvisionNetworkMsg msg) {
- if (msg.getDhcpRangeStartIp() != null && !NetworkUtils.isIpv4Address(msg.getDhcpRangeStartIp())) {
- throw new ApiMessageInterceptionException(argerr("invalid dhcpRangeStartIp[%s]", msg.getDhcpRangeStartIp()));
+ if (msg.getDhcpRangeStartIp() != null) {
+ String ip = msg.getDhcpRangeStartIp().trim();
+ if (!NetworkUtils.isIpv4Address(ip)) {
+ throw new ApiMessageInterceptionException(argerr("invalid dhcpRangeStartIp[%s]", ip));
+ }
+ msg.setDhcpRangeStartIp(ip);
}
- if (msg.getDhcpRangeEndIp() != null && !NetworkUtils.isIpv4Address(msg.getDhcpRangeEndIp())) {
- throw new ApiMessageInterceptionException(argerr("invalid dhcpRangeEndIp[%s]", msg.getDhcpRangeEndIp()));
+ if (msg.getDhcpRangeEndIp() != null) {
+ String ip = msg.getDhcpRangeEndIp().trim();
+ if (!NetworkUtils.isIpv4Address(ip)) {
+ throw new ApiMessageInterceptionException(argerr("invalid dhcpRangeEndIp[%s]", ip));
+ }
+ msg.setDhcpRangeEndIp(ip);
}
- if (msg.getDhcpRangeNetmask() != null && !NetworkUtils.isIpv4Address(msg.getDhcpRangeNetmask())) {
- throw new ApiMessageInterceptionException(argerr("invalid dhcpRangeNetmask[%s]", msg.getDhcpRangeNetmask()));
+ if (msg.getDhcpRangeNetmask() != null) {
+ String ip = msg.getDhcpRangeNetmask().trim();
+ if (!NetworkUtils.isIpv4Address(ip)) {
+ throw new ApiMessageInterceptionException(argerr("invalid dhcpRangeNetmask[%s]", ip));
+ }
+ msg.setDhcpRangeNetmask(ip);
}
- if (msg.getDhcpRangeGateway() != null && !NetworkUtils.isIpv4Address(msg.getDhcpRangeGateway())) {
- throw new ApiMessageInterceptionException(argerr("invalid dhcpRangeGateway[%s]", msg.getDhcpRangeGateway()));
+ if (msg.getDhcpRangeGateway() != null) {
+ String ip = msg.getDhcpRangeGateway().trim();
+ if (!NetworkUtils.isIpv4Address(ip)) {
+ throw new ApiMessageInterceptionException(argerr("invalid dhcpRangeGateway[%s]", ip));
+ }
+ msg.setDhcpRangeGateway(ip);
}
}🤖 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/physicalServer/src/main/java/org/zstack/server/PhysicalServerApiInterceptor.java`
around lines 67 - 80, The DHCP range IP fields are not trimmed before validation
in validate(APICreateProvisionNetworkMsg), so trim dhcpRangeStartIp,
dhcpRangeEndIp, dhcpRangeNetmask and dhcpRangeGateway (use their getters, trim
the strings and set them back on msg or use trimmed local variables) before
calling NetworkUtils.isIpv4Address; then perform the existing null check and
throw ApiMessageInterceptionException with the trimmed value in the argerr
message so validation and error text use the cleaned inputs.
| public class PhysicalServerStandalonePxeProvisionProvider implements ProvisionProvider { | ||
| @Override | ||
| public ProvisionNetworkType getType() { | ||
| return ProvisionNetworkType.STANDALONE_PXE; | ||
| } | ||
|
|
||
| @Override | ||
| public void prepareNetwork(PhysicalServerProvisionNetworkInventory network, String poolUuid, Completion completion) { | ||
| completion.success(); | ||
| } | ||
|
|
||
| @Override | ||
| public void destroyNetwork(PhysicalServerProvisionNetworkInventory network, String poolUuid, Completion completion) { | ||
| completion.success(); | ||
| } | ||
|
|
||
| @Override | ||
| public void startProvisioning(ProvisionRequest request, ReturnValueCompletion<ProvisionResult> completion) { | ||
| completion.fail(operrf("STANDALONE_PXE ProvisionProvider is reserved and not implemented yet")); | ||
| } | ||
|
|
||
| private ErrorCode operrf(String fmt, Object... args) { | ||
| return operr(SysErrors.OPERATION_ERROR.toString(), fmt, args); | ||
| } | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# 只读核查:确认 PhysicalServerStandalonePxeProvisionProvider 的注册与映射路径
# 1) 找出所有 ProvisionProvider 实现及常见注册注解模式
rg -nP --type=java -C2 'class\s+\w+\s+implements\s+ProvisionProvider'
rg -nP --type=java -C2 '@(Component|Extension|Configurable)\b'
# 2) 检查该类是否被引用/实例化
rg -n -C3 'PhysicalServerStandalonePxeProvisionProvider'
# 3) 检查服务侧是否按 ProvisionNetworkType 建立映射并覆盖 STANDALONE_PXE
rg -nP --type=java -C4 'ProvisionNetworkType\.(STANDALONE_PXE)|Map<\s*ProvisionNetworkType\s*,\s*ProvisionProvider\s*>|getType\s*\(\)'Repository: MatheMatrix/zstack
Length of output: 50376
🏁 Script executed:
# 查看 PhysicalServerStandalonePxeProvisionProvider 文件的完整内容
cat -n plugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerStandalonePxeProvisionProvider.java
# 搜索 ProvisionNetworkType 的定义
rg -n --type=java 'enum\s+ProvisionNetworkType|class\s+ProvisionNetworkType' -A 10
# 搜索 STANDALONE_PXE 的使用
rg -n --type=java 'STANDALONE_PXE' -B 2 -A 2
# 查找 ProvisionProvider 的注册机制
rg -n --type=java 'ProvisionNetworkType.*ProvisionProvider|getProvisionProvider|provisionProvider\s*=' -B 2 -A 2 | head -100Repository: MatheMatrix/zstack
Length of output: 7613
🏁 Script executed:
# 查看 PhysicalServerProvisionService 中 providerList 的定义和初始化
rg -n --type=java 'providerList' plugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerProvisionService.java -B 3 -A 3
# 查看 PhysicalServerProvisionService 的类定义和注解
head -100 plugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerProvisionService.java
# 搜索 PhysicalServerStandalonePxeProvisionProvider 的实例化
rg -n --type=java 'new PhysicalServerStandalonePxeProvisionProvider|PhysicalServerStandalonePxeProvisionProvider()' -B 2 -A 2Repository: MatheMatrix/zstack
Length of output: 5879
🏁 Script executed:
# 查找 plugin/physicalServer 目录下的配置文件
find plugin/physicalServer -type f -name "*.xml" -o -name "*.properties"
# 查找 Spring 配置类
rg -n --type=java '@Configuration|@Bean.*ProvisionProvider' plugin/physicalServer/
# 查找 applicationContext 或相关配置
fd -e xml plugin/physicalServer/ | head -20Repository: MatheMatrix/zstack
Length of output: 504
🏁 Script executed:
# 修正命令查找 plugin/physicalServer 下的配置文件
fd . plugin/physicalServer --type f -name "*.xml"
# 查找 Spring 配置类
rg -n --type=java '@Configuration' plugin/physicalServer/
# 查找 pom.xml 中的配置或模块信息
cat plugin/physicalServer/pom.xml | head -50Repository: MatheMatrix/zstack
Length of output: 2403
🏁 Script executed:
# 查找 plugin/physicalServer 下所有 XML 文件
find plugin/physicalServer -name "*.xml"
# 查找 src/main/resources 目录
find plugin/physicalServer/src -type d -name resources
# 在整个 plugin/physicalServer 中查找是否有工厂方法或 PluginRegistry 相关注册
rg -n --type=java 'PluginRegistry|register.*ProvisionProvider|extends.*Extension' plugin/physicalServer/src/main/java/Repository: MatheMatrix/zstack
Length of output: 2011
🏁 Script executed:
# 查看 PhysicalServerPathTwoOrchestrator 中 PluginRegistry 的使用
rg -n --type=java 'pluginRgty|ProvisionProvider' plugin/physicalServer/src/main/java/org/zstack/server/PhysicalServerPathTwoOrchestrator.java -B 2 -A 2
# 搜索整个项目中 PhysicalServerStandalonePxeProvisionProvider 的任何引用
rg -r 'PhysicalServerStandalonePxeProvisionProvider' --type=java
# 查找 Spring 自动扫描的包配置
rg -n '@ComponentScan|<context:component-scan' plugin/physicalServer/Repository: MatheMatrix/zstack
Length of output: 902
🏁 Script executed:
# 正确搜索 PhysicalServerStandalonePxeProvisionProvider 的所有引用
rg 'PhysicalServerStandalonePxeProvisionProvider' --type=java
# 查找 Spring 自动扫描的包配置
rg '@ComponentScan' --type=java | head -20
# 查找 application 相关配置文件
find . -name "application*.xml" -o -name "application*.properties" | head -20
# 查找 spring.factories 或 META-INF 配置
find plugin/physicalServer -name "spring.factories" -o -name "MANIFEST.MF"Repository: MatheMatrix/zstack
Length of output: 255
🏁 Script executed:
# 查找项目中的主 Spring 配置类
rg '@SpringBootApplication|@ComponentScan' --type=java | head -20
# 查找 plugin 目录下的配置
find plugin -name "*.xml" -path "*/resources/*" | head -20
# 检查是否有插件加载机制
rg 'class.*Loader|Plugin.*Manager' plugin/physicalServer/src/main/java/ --type=java | head -20Repository: MatheMatrix/zstack
Length of output: 121
该 Provider 的注册机制不完整,运行时无法被发现。
PhysicalServerStandalonePxeProvisionProvider 类缺少 Spring 注解,未被正确注册为 Bean。PhysicalServerProvisionService 通过 @Autowired(required = false) 注入 List<ProvisionProvider> providerList,但该 Provider 类没有任何注解(如 @Component、@Service 等),无法被 Spring 自动扫描和装配。运行时 providers().get(ProvisionNetworkType.STANDALONE_PXE) 会返回 null,导致错误:"no ProvisionProvider registered for ProvisionNetworkType[STANDALONE_PXE]"。
需要为该类添加 @Component 注解或通过 Spring 配置显式注册为 Bean。
🤖 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/physicalServer/src/main/java/org/zstack/server/PhysicalServerStandalonePxeProvisionProvider.java`
around lines 11 - 35, PhysicalServerStandalonePxeProvisionProvider is not
registered as a Spring bean so it never gets injected into the
List<ProvisionProvider> and providers().get(ProvisionNetworkType.STANDALONE_PXE)
returns null; fix by registering the class as a Spring bean (e.g., add
`@Component` to the PhysicalServerStandalonePxeProvisionProvider class or define
an explicit `@Bean` that returns a new
PhysicalServerStandalonePxeProvisionProvider), ensure the appropriate import
(org.springframework.stereotype.Component) or configuration class is updated and
that component scanning covers this package so ProvisionProvider instances are
discovered for PhysicalServerProvisionService.
| newest_src_mtime=$(find header/src/main/java abstraction/src/main/java -name '*.java' \ | ||
| -printf '%T@\n' 2>/dev/null | cut -d. -f1 | sort -n | tail -1) | ||
| [ -z "$newest_src_mtime" ] && return 1 | ||
| [ "$newest_src_mtime" -gt "$jar_mtime" ] |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
# 验证“声明扫描范围”与“实际扫描范围”
rg -n '\*VO\.java|\*AO\.java|find header/src/main/java abstraction/src/main/java' scripts/mvn-safe-install.shRepository: MatheMatrix/zstack
Length of output: 238
🏁 Script executed:
# View the full is_stale function and surrounding comments
cat -n scripts/mvn-safe-install.sh | head -40Repository: MatheMatrix/zstack
Length of output: 1845
🏁 Script executed:
# Also check if there are any other mentions of VO.java or AO.java in the entire script
rg -n 'VO\.java|AO\.java' scripts/mvn-safe-install.shRepository: MatheMatrix/zstack
Length of output: 143
🏁 Script executed:
# Check the repository structure for shared VO/AO files
find . -name '*VO.java' -o -name '*AO.java' | head -20Repository: MatheMatrix/zstack
Length of output: 1399
is_stale 漏检共享 *VO.java/*AO.java,会误判为"非陈旧"
第 10 行注释声明"检查 header/abstraction Java 或共享 *VO.java",但第 27-28 行的实现仅扫描 header/src/main/java 和 abstraction/src/main/java 目录。共享 VO/AO 文件(如 core/ 下的众多 VO/AO 类)实际未被纳入时间戳比较。当这些共享文件修改而上述两个目录未变化时,脚本会错误判定为"非陈旧",导致跳过 clean install,重新暴露 AspectJ VerifyError 风险,与脚本注释和设计意图不一致。
建议将共享 VO/AO 文件纳入时间戳检查:
修复建议
is_stale() {
[ ! -f "$COMPUTE_JAR" ] && return 0
local jar_mtime newest_src_mtime
jar_mtime=$(stat -c '%Y' "$COMPUTE_JAR")
- newest_src_mtime=$(find header/src/main/java abstraction/src/main/java -name '*.java' \
- -printf '%T@\n' 2>/dev/null | cut -d. -f1 | sort -n | tail -1)
+ newest_src_mtime=$(
+ {
+ find header/src/main/java abstraction/src/main/java -name '*.java' -print0 2>/dev/null
+ git ls-files -z '*VO.java' '*AO.java' 2>/dev/null
+ } | xargs -0 -r stat -c '%Y' | sort -n | tail -1
+ )
[ -z "$newest_src_mtime" ] && return 1
[ "$newest_src_mtime" -gt "$jar_mtime" ]
}🤖 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 `@scripts/mvn-safe-install.sh` around lines 27 - 30, The timestamp check only
finds files under header/src/main/java and abstraction/src/main/java (used to
set newest_src_mtime) and therefore misses shared *VO.java/*AO.java in other
dirs (e.g., core), causing stale detection to fail; update the find invocation
that sets newest_src_mtime to include the shared locations or explicitly search
by name patterns for '*VO.java' and '*AO.java' across the repo (for example add
core/src/main/java or use -name '*VO.java' -o -name '*AO.java' combined with the
existing paths), preserve the existing cut/sort/tail logic and the empty-result
guard ([ -z "$newest_src_mtime" ]), and keep comparisons against jar_mtime
unchanged so shared VO/AO edits are considered when deciding staleness.
What
补 Container cordon production trigger 的 zstack 侧改动,配对 premium 仓 MR (jin.ma/premium fix/cordon-production-trigger@@2 → feature-unifi-host) 整体落地 AC-CM-13 production trigger gap。
Why
2026-05-09 真机 172.26.201.160 PSC writer collapse 收尾时 grep 全仓发现
cordonService.cordon() / evaluate() / 反向 mirror isUnschedulable(V1Node)三个入口在 production 全 0 caller,cordonedHostUuidsregistry 在生产侧永远空 → AC-CM-13 reservation extension 链条接对了但 trigger 永不发生。详细分析 + 设计:docs/plans/2026-05-09-001-cordon-production-trigger-plan.md
Changes (1 commit)
00e0964
<doc>[plans]: cordon production trigger plan + STATUS R3 closecompute/PhysicalServerCapacityBuffers.calc{Cpu,Mem}Buffer静态 helper,从PhysicalServerCapacityUpdater._recalculate把 floor + percent buffer 算法搬出来,让 capacity-driven hysteresis trigger(U-ε in premium)能复用同口径upstream
feature-unifi-host当前 HEAD (3330ffa) 是早于 PSC writer collapse 的老分支,不含compute/PhysicalServerCapacityUpdater.java和docs/STATUS.md。本 MR 中对这两个文件的修改在合并时会冲突。建议合并顺序:Pairing
必须配对 premium MR:jin.ma/premium fix/cordon-production-trigger@@2 → feature-unifi-host
ContainerEndpointBaseimportorg.zstack.compute.allocator.PhysicalServerCapacityBuffers(本 MR 提供)。两边必须一起合,否则 premium 编译 fail。Test
IT (3 new + 3 regression all GREEN via run-case.sh)
ContainerK8sCordonMirrorCase✅ — operator-cordon mirror(K8sspec.unschedulable=true→ registry,0 patchNode 调用)ContainerCapacityCordonEvaluateCase✅ — capacity-driven cordon (free<buffer → cordon → PATCH K8s)ContainerCapacityHysteresisUncordonCase✅ — hysteresis 中间区 no-opContainerSyncRecalcCase/ContainerCordonReservedCase/ContainerNodeCordonServiceCase全 PASSReal-env 验证 (limited)
2026-05-09 真机 172.26.201.160 hot-deploy 验证 PSC writer collapse Layer 1+2 GREEN(属于上一个 plan);本 plan 的 cordon trigger real-env 验证(kubectl cordon → ZStack mirror)需要 K8s 管理面权限,留 next-session。IT case 已覆盖 production trigger 路径。
sync from gitlab !9790