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
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package org.zstack.compute.vm.devices;

import org.springframework.beans.factory.annotation.Autowired;
import org.zstack.core.asyncbatch.While;
import org.zstack.core.cascade.AbstractAsyncCascadeExtension;
import org.zstack.core.cascade.CascadeAction;
import org.zstack.core.cascade.CascadeConstant;
import org.zstack.core.cloudbus.CloudBus;
import org.zstack.core.cloudbus.CloudBusCallBack;
import org.zstack.core.db.DatabaseFacade;
import org.zstack.core.db.Q;
import org.zstack.header.core.Completion;
import org.zstack.header.core.WhileDoneCompletion;
import org.zstack.header.errorcode.ErrorCodeList;
import org.zstack.header.message.MessageReply;
import org.zstack.header.tpm.entity.TpmVO;
import org.zstack.header.tpm.entity.TpmVO_;
import org.zstack.header.tpm.message.TpmDeletionMsg;
import org.zstack.header.vm.VmInstanceInventory;
import org.zstack.header.vm.VmInstanceVO;
import org.zstack.utils.CollectionUtils;
import org.zstack.utils.Utils;
import org.zstack.utils.logging.CLogger;

import java.util.List;

import static org.zstack.core.Platform.operr;
import static org.zstack.header.tpm.TpmConstants.SERVICE_ID;
import static org.zstack.utils.CollectionDSL.list;
import static org.zstack.utils.CollectionUtils.transformAndRemoveNull;

public class TpmCascadeExtension extends AbstractAsyncCascadeExtension {
private static final CLogger logger = Utils.getLogger(TpmCascadeExtension.class);

@Autowired
private DatabaseFacade dbf;
@Autowired
private CloudBus bus;

private static final String NAME = TpmVO.class.getSimpleName();

@Override
public void asyncCascade(CascadeAction action, Completion completion) {
if (action.isActionCode(CascadeConstant.DELETION_CHECK_CODE)) {
handleDeletionCheck(action, completion);
} else if (action.isActionCode(CascadeConstant.DELETION_DELETE_CODE, CascadeConstant.DELETION_FORCE_DELETE_CODE)) {
handleDeletion(action, completion);
} else if (action.isActionCode(CascadeConstant.DELETION_CLEANUP_CODE)) {
handleDeletionCleanup(action, completion);
} else {
completion.success();
}
}

private List<TpmVO> tpmFromAction(CascadeAction action) {
if (VmInstanceVO.class.getSimpleName().equals(action.getParentIssuer())) {
List<VmInstanceInventory> vmInventories = action.getParentIssuerContext();
List<String> vmUuidList = transformAndRemoveNull(vmInventories, VmInstanceInventory::getUuid);

if (vmUuidList.isEmpty()) {
return null;
}
return Q.New(TpmVO.class)
.in(TpmVO_.vmInstanceUuid, vmUuidList)
.list();
} else if (NAME.equals(action.getParentIssuer())) {
return action.getParentIssuerContext();
}

return null;
}

private void handleDeletionCheck(CascadeAction action, Completion completion) {
completion.success();
}

private void handleDeletion(CascadeAction action, Completion completion) {
final List<TpmVO> tpmList = tpmFromAction(action);
if (CollectionUtils.isEmpty(tpmList)) {
completion.success();
return;
}

new While<>(tpmList).each((tpm, whileCompletion) -> {
TpmDeletionMsg msg = new TpmDeletionMsg();
msg.setTpmUuid(tpm.getUuid());
msg.setVmInstanceUuid(tpm.getVmInstanceUuid());
msg.setForceDelete(true);
bus.makeTargetServiceIdByResourceUuid(msg, SERVICE_ID, msg.getTpmUuid());
Comment thread
coderabbitai[bot] marked this conversation as resolved.
bus.send(msg, new CloudBusCallBack(whileCompletion) {
@Override
public void run(MessageReply reply) {
if (reply.isSuccess()) {
logger.debug(String.format("deleted Tpm[uuid:%s] from VM[uuid:%s]",
tpm.getUuid(), tpm.getVmInstanceUuid()));
} else {
whileCompletion.addError(reply.getError());
whileCompletion.allDone();
}
whileCompletion.done();
}
});
}).run(new WhileDoneCompletion(completion) {
@Override
public void done(ErrorCodeList errorCodeList) {
if (!errorCodeList.isEmpty()) {
completion.fail(operr("failed to delete Tpm from VM[uuid:%s]", tpmList.get(0).getVmInstanceUuid())
.withCause(errorCodeList));
return;
}
completion.success();
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
});
}

private void handleDeletionCleanup(CascadeAction action, Completion completion) {
completion.success();
}

@Override
public List<String> getEdgeNames() {
return list(VmInstanceVO.class.getSimpleName());
}

@Override
public String getCascadeResourceName() {
return NAME;
}

@Override
public CascadeAction createActionForChildResource(CascadeAction action) {
if (CascadeConstant.DELETION_CODES.contains(action.getActionCode())) {
List<TpmVO> ctx = tpmFromAction(action);
if (ctx != null) {
return action.copy().setParentIssuer(NAME).setParentIssuerContext(ctx);
}
}

return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package org.zstack.compute.vm.devices;

import org.springframework.beans.factory.annotation.Autowired;
import org.zstack.core.asyncbatch.While;
import org.zstack.core.cascade.AbstractAsyncCascadeExtension;
import org.zstack.core.cascade.CascadeAction;
import org.zstack.core.cascade.CascadeConstant;
import org.zstack.core.cloudbus.CloudBus;
import org.zstack.core.cloudbus.CloudBusCallBack;
import org.zstack.core.db.DatabaseFacade;
import org.zstack.core.db.Q;
import org.zstack.header.core.Completion;
import org.zstack.header.core.WhileDoneCompletion;
import org.zstack.header.errorcode.ErrorCodeList;
import org.zstack.header.message.MessageReply;
import org.zstack.header.vm.VmInstanceConstant;
import org.zstack.header.vm.VmInstanceInventory;
import org.zstack.header.vm.VmInstanceVO;
import org.zstack.header.vm.additions.VmHostBackupFileDeletionMsg;
import org.zstack.header.vm.additions.VmHostBackupFileVO;
import org.zstack.header.vm.additions.VmHostBackupFileVO_;
import org.zstack.utils.CollectionUtils;
import org.zstack.utils.Utils;
import org.zstack.utils.logging.CLogger;

import java.util.List;

import static org.zstack.core.Platform.operr;
import static org.zstack.utils.CollectionDSL.list;
import static org.zstack.utils.CollectionUtils.transformAndRemoveNull;

public class VmHostBackupFileCascadeExtension extends AbstractAsyncCascadeExtension {
private static final CLogger logger = Utils.getLogger(VmHostBackupFileCascadeExtension.class);

@Autowired
private DatabaseFacade dbf;
@Autowired
private CloudBus bus;

private static final String NAME = VmHostBackupFileVO.class.getSimpleName();

@Override
public void asyncCascade(CascadeAction action, Completion completion) {
if (action.isActionCode(CascadeConstant.DELETION_CHECK_CODE)) {
handleDeletionCheck(action, completion);
} else if (action.isActionCode(CascadeConstant.DELETION_DELETE_CODE, CascadeConstant.DELETION_FORCE_DELETE_CODE)) {
handleDeletion(action, completion);
} else if (action.isActionCode(CascadeConstant.DELETION_CLEANUP_CODE)) {
handleDeletionCleanup(action, completion);
} else {
completion.success();
}
}

private List<VmHostBackupFileVO> voFromAction(CascadeAction action) {
if (VmInstanceVO.class.getSimpleName().equals(action.getParentIssuer())) {
List<VmInstanceInventory> vmInventories = action.getParentIssuerContext();
List<String> vmUuidList = transformAndRemoveNull(vmInventories, VmInstanceInventory::getUuid);

if (vmUuidList.isEmpty()) {
return null;
}
return Q.New(VmHostBackupFileVO.class)
.in(VmHostBackupFileVO_.resourceUuid, vmUuidList)
.list();
}
// Note: VolumeSnapshotGroupVO has no cascade extension!
// skip: else if (VolumeSnapshotGroupVO.class.getSimpleName().equals(action.getParentIssuer()))
else if (NAME.equals(action.getParentIssuer())) {
return action.getParentIssuerContext();
}

return null;
Comment on lines +55 to +73
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

快照组上的备份文件不会被这个级联清掉。

resourceUuid 在这里同时承载了 VM UUID 和快照组 UUID,但 voFromAction() / getEdgeNames() 只处理 VmInstanceVO,而且 Line 67-69 还明确跳过了 VolumeSnapshotGroupVO。这样删除快照组时,对应的 VmHostBackupFileVO 会直接遗留在库里。

Also applies to: 123-130

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@compute/src/main/java/org/zstack/compute/vm/devices/VmHostBackupFileCascadeExtension.java`
around lines 55 - 73, The voFromAction in VmHostBackupFileCascadeExtension
currently only handles VmInstanceVO and explicitly skips VolumeSnapshotGroupVO,
leaving VmHostBackupFileVO rows whose resourceUuid holds snapshot-group UUIDs
orphaned; update voFromAction to also handle VolumeSnapshotGroupVO (or when
action.getParentIssuer() equals VolumeSnapshotGroupVO.class.getSimpleName() or
NAME where appropriate) by extracting the parent context UUIDs (like you do for
VmInstanceInventory) and querying
Q.New(VmHostBackupFileVO.class).in(VmHostBackupFileVO_.resourceUuid,
<snapshotGroupUuidList>).list(); also remove the explicit skip of
VolumeSnapshotGroupVO and ensure getEdgeNames covers the snapshot-group edge so
cascade deletes include those backup files.

}

private void handleDeletionCheck(CascadeAction action, Completion completion) {
completion.success();
}

private void handleDeletion(CascadeAction action, Completion completion) {
final List<VmHostBackupFileVO> voList = voFromAction(action);
if (CollectionUtils.isEmpty(voList)) {
completion.success();
return;
}

new While<>(voList).each((vo, whileCompletion) -> {
VmHostBackupFileDeletionMsg msg = new VmHostBackupFileDeletionMsg();
msg.setUuid(vo.getUuid());
msg.setForceDelete(true);
bus.makeLocalServiceId(msg, VmInstanceConstant.SECURE_BOOT_SERVICE_ID);
bus.send(msg, new CloudBusCallBack(whileCompletion) {
@Override
public void run(MessageReply reply) {
if (reply.isSuccess()) {
logger.debug(String.format("deleted VmHostBackupFile[uuid:%s] from resource[uuid:%s]",
vo.getUuid(), vo.getResourceUuid()));
} else {
whileCompletion.addError(reply.getError());
whileCompletion.allDone();
}
whileCompletion.done();
}
});
}).run(new WhileDoneCompletion(completion) {
@Override
public void done(ErrorCodeList errorCodeList) {
if (!errorCodeList.isEmpty()) {
completion.fail(operr("failed to delete VmHostBackupFile from resource[uuid:%s]", voList.get(0).getResourceUuid())
.withCause(errorCodeList));
return;
}
completion.success();
}
});
}

private void handleDeletionCleanup(CascadeAction action, Completion completion) {
completion.success();
}

@Override
public List<String> getEdgeNames() {
return list(
VmInstanceVO.class.getSimpleName()

// Note: VolumeSnapshotGroupVO has no cascade extension!
// skip: VolumeSnapshotGroupVO.class.getName()
);
}

@Override
public String getCascadeResourceName() {
return NAME;
}

@Override
public CascadeAction createActionForChildResource(CascadeAction action) {
if (CascadeConstant.DELETION_CODES.contains(action.getActionCode())) {
List<VmHostBackupFileVO> ctx = voFromAction(action);
if (ctx != null) {
return action.copy().setParentIssuer(NAME).setParentIssuerContext(ctx);
}
}

return null;
}
}
Loading