Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions conf/db/upgrade/V5.5.6__schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,78 @@ ALTER TABLE `GuestVmScriptExecutedRecordDetailVO`

CALL ADD_COLUMN('PciDeviceVO', 'vmPciDeviceAddress', 'varchar(32)', 1, NULL);
CALL ADD_COLUMN('MdevDeviceVO', 'mdevDeviceAddress', 'varchar(32)', 1, NULL);

DELIMITER $$

CREATE PROCEDURE UpdateBareMetal2InstanceProvisionNicVO()
BEGIN
DECLARE instanceUuid_exists INT;
DECLARE isPrimaryProvisionNic_exists_in_ProvisionNicVO INT;
DECLARE isPrimaryProvisionNic_exists_in_ChassisNicVO INT;

DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'An error occurred during the update process.';
END;

START TRANSACTION;

SELECT COUNT(*)
INTO instanceUuid_exists
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'zstack'
AND TABLE_NAME = 'BareMetal2InstanceProvisionNicVO'
AND COLUMN_NAME = 'instanceUuid';

IF instanceUuid_exists = 0 THEN
CALL ADD_COLUMN('BareMetal2InstanceProvisionNicVO', 'instanceUuid', 'VARCHAR(32)', FALSE, '');

UPDATE `zstack`.`BareMetal2InstanceProvisionNicVO`
SET `instanceUuid` = `uuid`;

ALTER TABLE `zstack`.`BareMetal2InstanceProvisionNicVO`
DROP FOREIGN KEY `fkBareMetal2InstanceProvisionNicVOInstanceVO`;

CALL ADD_CONSTRAINT('BareMetal2InstanceProvisionNicVO', 'fkBareMetal2InstanceProvisionNicVOInstanceVO',
'instanceUuid', 'BareMetal2InstanceVO', 'uuid', 'CASCADE');
Comment on lines +39 to +43
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

幂等性问题:DROP FOREIGN KEY 前应检查约束是否存在。

当前实现直接执行 DROP FOREIGN KEY,但未检查该外键约束是否存在。如果升级脚本部分执行后失败重试,或者数据库状态与预期不同,此操作会失败并导致整个迁移回滚。

根据 ZStack 项目的最佳实践,应通过 INFORMATION_SCHEMA.TABLE_CONSTRAINTS 检查约束存在性后再删除。

🐛 建议的修复方案
+       -- Check if FK exists before dropping
+       SET `@fk_exists` = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS 
+                         WHERE CONSTRAINT_SCHEMA = 'zstack' 
+                         AND TABLE_NAME = 'BareMetal2InstanceProvisionNicVO' 
+                         AND CONSTRAINT_NAME = 'fkBareMetal2InstanceProvisionNicVOInstanceVO'
+                         AND CONSTRAINT_TYPE = 'FOREIGN KEY');
+       
+       IF `@fk_exists` > 0 THEN
            ALTER TABLE `zstack`.`BareMetal2InstanceProvisionNicVO`
            DROP FOREIGN KEY `fkBareMetal2InstanceProvisionNicVOInstanceVO`;
+       END IF;

        CALL ADD_CONSTRAINT('BareMetal2InstanceProvisionNicVO', 'fkBareMetal2InstanceProvisionNicVOInstanceVO',
                                    'instanceUuid', 'BareMetal2InstanceVO', 'uuid', 'CASCADE');
🤖 Prompt for AI Agents
In `@conf/db/upgrade/V5.5.6__schema.sql` around lines 36 - 40, Wrap the DROP
FOREIGN KEY in a presence check against INFORMATION_SCHEMA.TABLE_CONSTRAINTS for
constraint name fkBareMetal2InstanceProvisionNicVOInstanceVO on table
BareMetal2InstanceProvisionNicVO, and only execute the DROP if the constraint
exists; leave the subsequent CALL
ADD_CONSTRAINT('BareMetal2InstanceProvisionNicVO',
'fkBareMetal2InstanceProvisionNicVOInstanceVO', 'instanceUuid',
'BareMetal2InstanceVO', 'uuid', 'CASCADE') unchanged. Use a conditional SELECT
(e.g., COUNT(*) from INFORMATION_SCHEMA.TABLE_CONSTRAINTS filtering by
CONSTRAINT_NAME and TABLE_NAME/schema) and execute the ALTER TABLE ... DROP
FOREIGN KEY only when the count > 0 to ensure idempotency.


UPDATE `zstack`.`BareMetal2InstanceProvisionNicVO`
SET `uuid` = REPLACE(UUID(), '-', '');
Comment on lines +36 to +46
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 检索数据库升级脚本中对 BareMetal2InstanceProvisionNicVO 的引用
echo "=== SQL 脚本中的引用 ==="
rg -n --type=sql 'BareMetal2InstanceProvisionNicVO' -C2

echo ""
echo "=== Java 代码中的引用 ==="
rg -n --type=java 'BareMetal2InstanceProvisionNicVO' -C2

echo ""
echo "=== 相关的字段引用 ==="
rg -n --type=java 'bareMetal2InstanceProvisionNic|provisionNicUuid' -C1

Repository: MatheMatrix/zstack

Length of output: 6855


🏁 Script executed:

cat -n conf/db/upgrade/V5.5.6__schema.sql

Repository: MatheMatrix/zstack

Length of output: 3556


🏁 Script executed:

#!/bin/bash
# 查看 BareMetal2BondingNicRefVO 的表定义
echo "=== BareMetal2BondingNicRefVO 表定义 ==="
rg -n --type=sql 'CREATE TABLE.*BareMetal2BondingNicRefVO|CREATE TABLE.*IF NOT EXISTS.*BareMetal2BondingNicRefVO' -A 20

echo ""
echo "=== 查看所有升级脚本中对 BareMetal2BondingNicRefVO 的修改 ==="
rg -n --type=sql 'BareMetal2BondingNicRefVO' -B 2 -A 2

Repository: MatheMatrix/zstack

Length of output: 3202


🏁 Script executed:

#!/bin/bash
# 查看 V5.5.6 之后的升级脚本是否有处理 provisionNicUuid
echo "=== V5.5.6 之后的升级脚本 ==="
fd --type f 'V[0-9]+\.[0-9]+\.[0-9]+__schema\.sql' conf/db/upgrade | sort | tail -10

echo ""
echo "=== 搜索升级脚本中对 provisionNicUuid 的更新 ==="
rg -n --type=sql 'provisionNicUuid' -B 2 -A 2

echo ""
echo "=== 检查是否在升级脚本中有其他对 BareMetal2BondingNicRefVO 的更新 ==="
rg -n --type=sql 'UPDATE.*BareMetal2BondingNicRefVO|UPDATE.*provisionNicUuid' -B 2 -A 2

Repository: MatheMatrix/zstack

Length of output: 1045


🏁 Script executed:

#!/bin/bash
# 查看外键约束的完整定义
echo "=== 外键约束定义 ==="
rg -n --type=sql "FOREIGN KEY.*BareMetal2InstanceProvisionNicVO.*uuid" -B 2 -A 2

echo ""
echo "=== 确认约束配置中是否有 ON UPDATE CASCADE ==="
rg -n --type=sql "fkBareMetal2BondingNicRefVOProvisionNicVO" -B 2 -A 2

# 查看 ADD_CONSTRAINT 函数的定义
echo ""
echo "=== 查找 ADD_CONSTRAINT 存储过程的定义 ==="
rg -n --type=sql "CREATE PROCEDURE.*ADD_CONSTRAINT|PROCEDURE.*ADD_CONSTRAINT" -A 30 | head -60

Repository: MatheMatrix/zstack

Length of output: 3097


🏁 Script executed:

#!/bin/bash
# 详细查看 ADD_CONSTRAINT 的完整实现
sed -n '187,212p' conf/db/upgrade/beforeMigrate.sql

echo ""
echo "=== 确认当前是否有 ON UPDATE 处理 ==="
grep -n "ON UPDATE" conf/db/upgrade/beforeMigrate.sql

Repository: MatheMatrix/zstack

Length of output: 1118


在重置 uuid 前需同步更新引用该字段的表。

BareMetal2BondingNicRefVO.provisionNicUuid 有外键约束指向 BareMetal2InstanceProvisionNicVO.uuid(见 V4.7.13__schema.sql),但该约束只配置了 ON DELETE CASCADE,没有 ON UPDATE CASCADE(ADD_CONSTRAINT 存储过程仅生成 ON DELETE 子句)。

升级脚本在第 42-43 行直接更新 uuid 为新值,会导致 BareMetal2BondingNicRefVO.provisionNicUuid 指向不存在的 uuid,造成数据完整性破坏。

需在更新 uuid 之前,先同步更新所有引用该字段的表中的值,或在升级脚本中先删除外键再重建。

🤖 Prompt for AI Agents
In `@conf/db/upgrade/V5.5.6__schema.sql` around lines 33 - 43, The migration
updates BareMetal2InstanceProvisionNicVO.uuid directly but ignores referencing
rows in BareMetal2BondingNicRefVO.provisionNicUuid (FK from V4.7.13) which lacks
ON UPDATE CASCADE; before running the UPDATE that sets `uuid = REPLACE(UUID(),
'-', '')` in BareMetal2InstanceProvisionNicVO, either (A) compute and apply the
same uuid-mapping to update BareMetal2BondingNicRefVO.provisionNicUuid to the
new UUIDs so references remain valid, or (B) DROP the foreign key
fkBareMetal2BondingNicRefVOProvisionNic (remove the FK constraint), perform the
uuid update on BareMetal2InstanceProvisionNicVO, then re-CALL ADD_CONSTRAINT to
recreate the FK (or create it with ON UPDATE CASCADE) afterwards; reference the
tables BareMetal2InstanceProvisionNicVO, BareMetal2BondingNicRefVO and the
ALTER/DROP FOREIGN KEY + CALL ADD_CONSTRAINT statements in the diff when making
the change.

END IF;

SELECT COUNT(*)
INTO isPrimaryProvisionNic_exists_in_ProvisionNicVO
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'zstack'
AND TABLE_NAME = 'BareMetal2InstanceProvisionNicVO'
AND COLUMN_NAME = 'isPrimaryProvisionNic';

IF isPrimaryProvisionNic_exists_in_ProvisionNicVO = 0 THEN
CALL ADD_COLUMN('BareMetal2InstanceProvisionNicVO', 'isPrimaryProvisionNic', 'BOOLEAN', FALSE, FALSE);

UPDATE `zstack`.`BareMetal2InstanceProvisionNicVO`
SET `isPrimaryProvisionNic` = TRUE;
END IF;

SELECT COUNT(*)
INTO isPrimaryProvisionNic_exists_in_ChassisNicVO
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'zstack'
AND TABLE_NAME = 'BareMetal2ChassisNicVO'
AND COLUMN_NAME = 'isPrimaryProvisionNic';

IF isPrimaryProvisionNic_exists_in_ChassisNicVO = 0 THEN
CALL ADD_COLUMN('BareMetal2ChassisNicVO', 'isPrimaryProvisionNic', 'BOOLEAN', FALSE, FALSE);

UPDATE `zstack`.`BareMetal2ChassisNicVO`
SET `isPrimaryProvisionNic` = TRUE
WHERE `isProvisionNic` = TRUE;
END IF;

COMMIT;
Comment on lines +18 to +78
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

DDL 隐式提交会让事务回滚失效。

此处在事务内执行 ALTER TABLE / ADD_COLUMN 等 DDL,MySQL/MariaDB 会隐式 COMMIT,EXIT HANDLER 的 ROLLBACK 无法还原已执行的 DDL,升级中断可能留下半完成状态。建议移除事务假象或改为可逆、分步且幂等的升级方案。Based on learnings, ...

🤖 Prompt for AI Agents
In `@conf/db/upgrade/V5.5.6__schema.sql` around lines 18 - 78, The migration wraps
DDL (ADD_COLUMN, ALTER TABLE) inside a START TRANSACTION/COMMIT with a DECLARE
EXIT HANDLER FOR SQLEXCEPTION that attempts ROLLBACK, but MySQL/MariaDB issues
implicit commits for DDL so the ROLLBACK is ineffective; remove the
transactional illusion and rewrite the script into safe, idempotent, stepwise
operations: drop START TRANSACTION/COMMIT and the EXIT HANDLER, ensure each step
(checks using INFORMATION_SCHEMA, CALL ADD_COLUMN for
BareMetal2InstanceProvisionNicVO and BareMetal2ChassisNicVO, ALTER TABLE DROP
FOREIGN KEY, CALL ADD_CONSTRAINT, and UPDATE statements) is guarded by existence
checks and can be re-run without side effects (or split into separate reversible
stages), and keep references to the identifiers in this file (ADD_COLUMN,
ADD_CONSTRAINT, ALTER TABLE `BareMetal2InstanceProvisionNicVO`, UPDATE
statements, DECLARE EXIT HANDLER) so the migration no longer relies on
transaction rollback for DDL failures.

END$$

DELIMITER ;
CALL UpdateBareMetal2InstanceProvisionNicVO();
DROP PROCEDURE IF EXISTS UpdateBareMetal2InstanceProvisionNicVO;
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,12 @@
import org.zstack.header.storage.primary.*;
import org.zstack.header.storage.primary.VolumeSnapshotCapability.VolumeSnapshotArrangementType;
import org.zstack.header.storage.snapshot.*;
import org.zstack.header.tag.SystemTagVO;
import org.zstack.header.tag.SystemTagVO_;
import org.zstack.header.vm.VmInstanceSpec;
import org.zstack.header.vm.VmInstanceSpec.ImageSpec;
import org.zstack.header.vm.VmInstanceVO;
import org.zstack.header.vm.VmInstanceVO_;
import org.zstack.header.vo.ResourceVO;
import org.zstack.header.volume.*;
import org.zstack.identity.AccountManager;
Expand Down Expand Up @@ -485,6 +489,8 @@ public static class CloneRsp extends AgentResponse {
public Long size;
public Long actualSize;
public String installPath;
public String volumeId;
public String volumeStatus;

public String getInstallPath() {
return installPath;
Expand All @@ -493,6 +499,22 @@ public String getInstallPath() {
public void setInstallPath(String installPath) {
this.installPath = installPath;
}

public String getVolumeId() {
return volumeId;
}

public void setVolumeId(String volumeId) {
this.volumeId = volumeId;
}

public String getVolumeStatus() {
return volumeStatus;
}

public void setVolumeStatus(String volumeStatus) {
this.volumeStatus = volumeStatus;
}
}

public static class FlattenCmd extends AgentCommand implements HasThreadContext {
Expand Down Expand Up @@ -2636,6 +2658,40 @@ public void run(final FlowTrigger trigger, Map data) {
CloneCmd cmd = new CloneCmd();
cmd.srcPath = cloneInstallPath;
cmd.dstPath = volumePath;
List<CephPrimaryStorageCheckInstanceTypeExtensionPoint> exts = pluginRgty.getExtensionList(CephPrimaryStorageCheckInstanceTypeExtensionPoint.class);
for (CephPrimaryStorageCheckInstanceTypeExtensionPoint ext : exts) {
Boolean result = ext.isSupportCloneByThirdParty(msg.getVolume().getVmInstanceUuid());
if (!result) {
break;
}
boolean isRootVolume = Q.New(VolumeVO.class)
.eq(VolumeVO_.uuid, msg.getVolume().getUuid())
.eq(VolumeVO_.type, VolumeType.Root)
.isExists();
boolean isBareMetal2Instance = Q.New(VmInstanceVO.class)
.eq(VmInstanceVO_.uuid, msg.getVolume().getVmInstanceUuid())
.eq(VmInstanceVO_.type, "baremetal2")
.isExists();
boolean hasToken = CephSystemTags.THIRDPARTY_PLATFORM.hasTag(msg.getPrimaryStorageUuid());
if (isRootVolume && isBareMetal2Instance && hasToken) {
CephPrimaryStorageVO cephPrimaryStorageVO = dbf.findByUuid(msg.getPrimaryStorageUuid(), CephPrimaryStorageVO.class);
String monIp = cephPrimaryStorageVO.getMons()
.stream()
.filter(v -> v.getStatus() == MonStatus.Connected)
.map(CephPrimaryStorageMonVO::getHostname)
.findAny()
.orElseThrow(() -> new OperationFailureException(
operr("all ceph mons of primary storage[uuid:%s] are not in Connected state", cephPrimaryStorageVO.getUuid())
));
cmd.token = CephSystemTags.THIRDPARTY_PLATFORM.getTokenByResourceUuid(msg.getPrimaryStorageUuid(),
CephSystemTags.THIRDPARTY_PLATFORM_TOKEN);
cmd.monIp = monIp;
cmd.tpTimeout = CephGlobalConfig.THIRD_PARTY_SDK_TIMEOUT.value(String.class);
VolumeVO vo = Q.New(VolumeVO.class)
.eq(VolumeVO_.uuid, msg.getVolume().getUuid()).find();
ext.convertToBlockVolume(vo);
}
}
Comment on lines +2661 to +2694
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

扩展点遍历不应使用 break,否则会跳过后续扩展

当前在遇到 false 时直接 break,会导致后续扩展点被跳过;同时若返回 null 也可能触发 NPE。建议使用 continue 并做 null-safe 判断。

🐛 建议修改
-                        for (CephPrimaryStorageCheckInstanceTypeExtensionPoint ext : exts) {
-                            Boolean result = ext.isSupportCloneByThirdParty(msg.getVolume().getVmInstanceUuid());
-                            if (!result) {
-                                break;
-                            }
+                        for (CephPrimaryStorageCheckInstanceTypeExtensionPoint ext : exts) {
+                            if (!Boolean.TRUE.equals(ext.isSupportCloneByThirdParty(msg.getVolume().getVmInstanceUuid()))) {
+                                continue;
+                            }
                             boolean isRootVolume = Q.New(VolumeVO.class)
                                     .eq(VolumeVO_.uuid, msg.getVolume().getUuid())
                                     .eq(VolumeVO_.type, VolumeType.Root)
                                     .isExists();
                             boolean isBareMetal2Instance = Q.New(VmInstanceVO.class)
                                     .eq(VmInstanceVO_.uuid, msg.getVolume().getVmInstanceUuid())
                                     .eq(VmInstanceVO_.type, "baremetal2")
                                     .isExists();
                             boolean hasToken = CephSystemTags.THIRDPARTY_PLATFORM.hasTag(msg.getPrimaryStorageUuid());
                             if (isRootVolume && isBareMetal2Instance && hasToken) {
                                 ...
                             }
                         }
-                                for (CephPrimaryStorageCheckInstanceTypeExtensionPoint ext : exts) {
-                                    if (!ext.isBlockVolume(msg.getVolume().getUuid())) {
-                                        break;
-                                    }
+                                for (CephPrimaryStorageCheckInstanceTypeExtensionPoint ext : exts) {
+                                    if (!Boolean.TRUE.equals(ext.isBlockVolume(msg.getVolume().getUuid()))) {
+                                        continue;
+                                    }
                                     volumePath = ret.installPath == null ? volumePath : makeVolumeInstallPathByTargetPool(ret.installPath, targetCephPoolName);
                                     ext.populateBlockVolumeDetails(msg.getVolume().getUuid(), ret.volumeId, ret.volumeStatus);
                                 }

Also applies to: 2705-2712

🤖 Prompt for AI Agents
In
`@plugin/ceph/src/main/java/org/zstack/storage/ceph/primary/CephPrimaryStorageBase.java`
around lines 2661 - 2694, The loop over extensions (variable exts of type
CephPrimaryStorageCheckInstanceTypeExtensionPoint) must not use break on a false
result and must be null-safe: replace the current "if (!result) { break; }"
logic with a null-safe check such as "if (Boolean.FALSE.equals(result)) {
continue; }" so that a null result does not throw an NPE and false only skips
the current extension instead of stopping the whole loop; apply the same change
to the other extension loop that calls ext.isSupportCloneByThirdParty(...) and
ext.convertToBlockVolume(...).


httpCall(CLONE_PATH, cmd, CloneRsp.class, new ReturnValueCompletion<CloneRsp>(trigger) {
@Override
Expand All @@ -2646,6 +2702,14 @@ public void fail(ErrorCode err) {
@Override
public void success(CloneRsp ret) {
actualSize = ret.actualSize;
List<CephPrimaryStorageCheckInstanceTypeExtensionPoint> exts = pluginRgty.getExtensionList(CephPrimaryStorageCheckInstanceTypeExtensionPoint.class);
for (CephPrimaryStorageCheckInstanceTypeExtensionPoint ext : exts) {
if (!ext.isBlockVolume(msg.getVolume().getUuid())) {
break;
}
volumePath = ret.installPath == null ? volumePath : makeVolumeInstallPathByTargetPool(ret.installPath, targetCephPoolName);
ext.populateBlockVolumeDetails(msg.getVolume().getUuid(), ret.volumeId, ret.volumeStatus);
}
trigger.next();
}
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.zstack.storage.ceph.primary;

import org.zstack.header.volume.VolumeVO;

public interface CephPrimaryStorageCheckInstanceTypeExtensionPoint {
Boolean isSupportCloneByThirdParty(String uuid);

void convertToBlockVolume(VolumeVO vo);

Boolean isBlockVolume(String uuid);

void populateBlockVolumeDetails(String uuid, String volumeId, String volumeStatus);
Comment on lines +5 to +12
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# 查找接口实现类
rg -n --type=java 'implements\s+CephPrimaryStorageCheckInstanceTypeExtensionPoint' -A 3

Repository: MatheMatrix/zstack

Length of output: 44


🏁 Script executed:

# 查找接口方法的调用
rg -n --type=java '\.isSupportCloneByThirdParty\(|\.isBlockVolume\(' -B 2 -A 2

Repository: MatheMatrix/zstack

Length of output: 1892


🏁 Script executed:

# 查找接口的注入和使用
rg -n --type=java 'CephPrimaryStorageCheckInstanceTypeExtensionPoint'

Repository: MatheMatrix/zstack

Length of output: 1157


🏁 Script executed:

# 检查当前文件的完整内容和是否有其他定义
cat -n plugin/ceph/src/main/java/org/zstack/storage/ceph/primary/CephPrimaryStorageCheckInstanceTypeExtensionPoint.java

Repository: MatheMatrix/zstack

Length of output: 536


🏁 Script executed:

# 查找接口实现类
find . -name "*.java" -type f -exec grep -l "implements.*CephPrimaryStorageCheckInstanceTypeExtensionPoint" {} \;

Repository: MatheMatrix/zstack

Length of output: 44


🏁 Script executed:

# 检查具体的调用代码上下文
sed -n '2660,2710p' plugin/ceph/src/main/java/org/zstack/storage/ceph/primary/CephPrimaryStorageBase.java

Repository: MatheMatrix/zstack

Length of output: 3914


🏁 Script executed:

# 搜索所有实现该接口的类
rg -n 'class\s+\w+.*implements.*CephPrimaryStorageCheckInstanceTypeExtensionPoint' -A 10

Repository: MatheMatrix/zstack

Length of output: 44


Boolean 改为 boolean 避免调用侧 NPE

调用代码在 CephPrimaryStorageBase.java 的 2664 行和 2707 行直接对返回值进行拆箱操作(!result!ext.isBlockVolume(...)),未做 null 检查。若任何实现返回 null,将触发 NullPointerException

建议修改
-    Boolean isSupportCloneByThirdParty(String uuid);
+    boolean isSupportCloneByThirdParty(String uuid);

-    Boolean isBlockVolume(String uuid);
+    boolean isBlockVolume(String uuid);
🤖 Prompt for AI Agents
In
`@plugin/ceph/src/main/java/org/zstack/storage/ceph/primary/CephPrimaryStorageCheckInstanceTypeExtensionPoint.java`
around lines 5 - 12, The interface
CephPrimaryStorageCheckInstanceTypeExtensionPoint uses nullable Boolean for
isSupportCloneByThirdParty and isBlockVolume which allows implementations to
return null and causes NPEs when callers in CephPrimaryStorageBase unwrap the
value; change both method signatures to return primitive boolean, then update
all implementing classes of CephPrimaryStorageCheckInstanceTypeExtensionPoint to
return true/false (not null), and adjust any unit tests or mocks accordingly so
callers (e.g., the checks in CephPrimaryStorageBase) can safely use !result or
!ext.isBlockVolume(...) without null checks.

}
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@ public java.lang.Boolean getIsProvisionNic() {
return this.isProvisionNic;
}

public java.lang.Boolean isPrimaryProvisionNic;
public void setIsPrimaryProvisionNic(java.lang.Boolean isPrimaryProvisionNic) {
this.isPrimaryProvisionNic = isPrimaryProvisionNic;
}
public java.lang.Boolean getIsPrimaryProvisionNic() {
return this.isPrimaryProvisionNic;
}

public java.sql.Timestamp createDate;
public void setCreateDate(java.sql.Timestamp createDate) {
this.createDate = createDate;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package org.zstack.sdk;

import org.zstack.sdk.BareMetal2InstanceProvisionNicInventory;


public class BareMetal2InstanceInventory extends org.zstack.sdk.VmInstanceInventory {

Expand Down Expand Up @@ -84,12 +84,12 @@ public boolean getIsLatestAgent() {
return this.isLatestAgent;
}

public BareMetal2InstanceProvisionNicInventory provisionNic;
public void setProvisionNic(BareMetal2InstanceProvisionNicInventory provisionNic) {
this.provisionNic = provisionNic;
public java.util.List provisionNics;
public void setProvisionNics(java.util.List provisionNics) {
this.provisionNics = provisionNics;
}
public BareMetal2InstanceProvisionNicInventory getProvisionNic() {
return this.provisionNic;
public java.util.List getProvisionNics() {
return this.provisionNics;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,22 @@ public java.lang.String getGateway() {
return this.gateway;
}

public java.lang.String instanceUuid;
public void setInstanceUuid(java.lang.String instanceUuid) {
this.instanceUuid = instanceUuid;
}
public java.lang.String getInstanceUuid() {
return this.instanceUuid;
}

public java.lang.Boolean isPrimaryProvisionNic;
public void setIsPrimaryProvisionNic(java.lang.Boolean isPrimaryProvisionNic) {
this.isPrimaryProvisionNic = isPrimaryProvisionNic;
}
public java.lang.Boolean getIsPrimaryProvisionNic() {
return this.isPrimaryProvisionNic;
}

public java.lang.String metadata;
public void setMetadata(java.lang.String metadata) {
this.metadata = metadata;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7678,6 +7678,10 @@ public class CloudOperationsErrorCode {

public static final String ORG_ZSTACK_BAREMETAL2_INSTANCE_10087 = "ORG_ZSTACK_BAREMETAL2_INSTANCE_10087";

public static final String ORG_ZSTACK_BAREMETAL2_INSTANCE_10088 = "ORG_ZSTACK_BAREMETAL2_INSTANCE_10088";

public static final String ORG_ZSTACK_BAREMETAL2_INSTANCE_10089 = "ORG_ZSTACK_BAREMETAL2_INSTANCE_10089";

public static final String ORG_ZSTACK_CRYPTO_SECURITYMACHINE_SECRETRESOURCEPOOL_10000 = "ORG_ZSTACK_CRYPTO_SECURITYMACHINE_SECRETRESOURCEPOOL_10000";

public static final String ORG_ZSTACK_CRYPTO_SECURITYMACHINE_SECRETRESOURCEPOOL_10001 = "ORG_ZSTACK_CRYPTO_SECURITYMACHINE_SECRETRESOURCEPOOL_10001";
Expand Down Expand Up @@ -13706,6 +13710,10 @@ public class CloudOperationsErrorCode {

public static final String ORG_ZSTACK_BAREMETAL2_CHASSIS_10023 = "ORG_ZSTACK_BAREMETAL2_CHASSIS_10023";

public static final String ORG_ZSTACK_BAREMETAL2_CHASSIS_10024 = "ORG_ZSTACK_BAREMETAL2_CHASSIS_10024";

public static final String ORG_ZSTACK_BAREMETAL2_CHASSIS_10025 = "ORG_ZSTACK_BAREMETAL2_CHASSIS_10025";

public static final String ORG_ZSTACK_BAREMETAL2_CLUSTER_10000 = "ORG_ZSTACK_BAREMETAL2_CLUSTER_10000";

public static final String ORG_ZSTACK_BAREMETAL2_CLUSTER_10001 = "ORG_ZSTACK_BAREMETAL2_CLUSTER_10001";
Expand Down