diff --git a/compute/src/main/java/org/zstack/compute/vm/VmInstanceManagerImpl.java b/compute/src/main/java/org/zstack/compute/vm/VmInstanceManagerImpl.java index 418cd0fbd5b..2af241834d7 100755 --- a/compute/src/main/java/org/zstack/compute/vm/VmInstanceManagerImpl.java +++ b/compute/src/main/java/org/zstack/compute/vm/VmInstanceManagerImpl.java @@ -895,7 +895,7 @@ private void handle(APICreateVmNicMsg msg) { FlowChain flowChain = FlowChainBuilder.newSimpleFlowChain(); flowChain.setName(String.format("create-nic-on-l3-network-%s", msg.getL3NetworkUuid())); flowChain.then(new NoRollbackFlow() { - String __name__ = "create-nic-and-presist-to-db"; + String __name__ = "create-nic-and-persist-to-db"; @Override public void run(FlowTrigger trigger, Map data) { diff --git a/conf/db/zsv/V5.0.0__schema.sql b/conf/db/zsv/V5.0.0__schema.sql index 808e3041120..3bdfbdcfaaf 100644 --- a/conf/db/zsv/V5.0.0__schema.sql +++ b/conf/db/zsv/V5.0.0__schema.sql @@ -25,13 +25,12 @@ CREATE TABLE IF NOT EXISTS `zstack`.`VmHostFileVO` ( CREATE TABLE IF NOT EXISTS `zstack`.`VmHostBackupFileVO` ( `uuid` char(32) NOT NULL UNIQUE, - `vmInstanceUuid` char(32) NOT NULL, + `resourceUuid` char(32) NOT NULL, `type` varchar(64) NOT NULL COMMENT 'NvRam, TpmState', `lastOpDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `createDate` timestamp NOT NULL DEFAULT '1999-12-31 23:59:59', PRIMARY KEY (`uuid`), - INDEX `idxVmHostBackupFileVOVmInstanceUuid` (`vmInstanceUuid`), - UNIQUE KEY `ukVmHostBackupFileVO` (`vmInstanceUuid`, `type`) + UNIQUE KEY `ukVmHostBackupFileVO` (`resourceUuid`, `type`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE IF NOT EXISTS `zstack`.`VmHostFileContentVO` ( diff --git a/header/src/main/java/org/zstack/header/storage/snapshot/CreateVolumesSnapshotMsg.java b/header/src/main/java/org/zstack/header/storage/snapshot/CreateVolumesSnapshotMsg.java index 886f697c6df..919e0b9af8e 100755 --- a/header/src/main/java/org/zstack/header/storage/snapshot/CreateVolumesSnapshotMsg.java +++ b/header/src/main/java/org/zstack/header/storage/snapshot/CreateVolumesSnapshotMsg.java @@ -16,6 +16,8 @@ public class CreateVolumesSnapshotMsg extends NeedReplyMessage implements NeedQu private List volumeSnapshotJobs; + private boolean backupHostFileIfNeeded; + public String getAccountUuid() { return accountUuid; } @@ -44,4 +46,12 @@ public ConsistentType getConsistentType() { public void setConsistentType(ConsistentType consistentType) { this.consistentType = consistentType; } + + public boolean isBackupHostFileIfNeeded() { + return backupHostFileIfNeeded; + } + + public void setBackupHostFileIfNeeded(boolean backupHostFileIfNeeded) { + this.backupHostFileIfNeeded = backupHostFileIfNeeded; + } } diff --git a/header/src/main/java/org/zstack/header/storage/snapshot/CreateVolumesSnapshotOverlayInnerMsg.java b/header/src/main/java/org/zstack/header/storage/snapshot/CreateVolumesSnapshotOverlayInnerMsg.java index 9e52d36b0d3..a9b4f6bdc62 100755 --- a/header/src/main/java/org/zstack/header/storage/snapshot/CreateVolumesSnapshotOverlayInnerMsg.java +++ b/header/src/main/java/org/zstack/header/storage/snapshot/CreateVolumesSnapshotOverlayInnerMsg.java @@ -19,6 +19,8 @@ public class CreateVolumesSnapshotOverlayInnerMsg extends NeedReplyMessage imple private List lockedVolumeUuids; + private boolean backupHostFileIfNeeded; + public List getLockedVmInstanceUuids() { return lockedVmInstanceUuids; } @@ -63,4 +65,12 @@ public ConsistentType getConsistentType() { public void setConsistentType(ConsistentType consistentType) { this.consistentType = consistentType; } + + public boolean isBackupHostFileIfNeeded() { + return backupHostFileIfNeeded; + } + + public void setBackupHostFileIfNeeded(boolean backupHostFileIfNeeded) { + this.backupHostFileIfNeeded = backupHostFileIfNeeded; + } } diff --git a/header/src/main/java/org/zstack/header/storage/snapshot/CreateVolumesSnapshotOverlayInnerReply.java b/header/src/main/java/org/zstack/header/storage/snapshot/CreateVolumesSnapshotOverlayInnerReply.java index a488079afc2..cd816f6014c 100644 --- a/header/src/main/java/org/zstack/header/storage/snapshot/CreateVolumesSnapshotOverlayInnerReply.java +++ b/header/src/main/java/org/zstack/header/storage/snapshot/CreateVolumesSnapshotOverlayInnerReply.java @@ -9,6 +9,7 @@ */ public class CreateVolumesSnapshotOverlayInnerReply extends MessageReply { private List inventories; + private List hostBackupFileUuidList; public List getInventories() { return inventories; @@ -17,4 +18,12 @@ public List getInventories() { public void setInventories(List inventories) { this.inventories = inventories; } + + public List getHostBackupFileUuidList() { + return hostBackupFileUuidList; + } + + public void setHostBackupFileUuidList(List hostBackupFileUuidList) { + this.hostBackupFileUuidList = hostBackupFileUuidList; + } } diff --git a/header/src/main/java/org/zstack/header/storage/snapshot/CreateVolumesSnapshotReply.java b/header/src/main/java/org/zstack/header/storage/snapshot/CreateVolumesSnapshotReply.java index 41e1b0270f9..ac510f260b7 100755 --- a/header/src/main/java/org/zstack/header/storage/snapshot/CreateVolumesSnapshotReply.java +++ b/header/src/main/java/org/zstack/header/storage/snapshot/CreateVolumesSnapshotReply.java @@ -9,6 +9,7 @@ */ public class CreateVolumesSnapshotReply extends MessageReply { private List inventories; + private List hostBackupFileUuidList; public List getInventories() { return inventories; @@ -17,4 +18,12 @@ public List getInventories() { public void setInventories(List inventories) { this.inventories = inventories; } + + public List getHostBackupFileUuidList() { + return hostBackupFileUuidList; + } + + public void setHostBackupFileUuidList(List hostBackupFileUuidList) { + this.hostBackupFileUuidList = hostBackupFileUuidList; + } } diff --git a/header/src/main/java/org/zstack/header/vm/additions/VmHostBackupFileVO.java b/header/src/main/java/org/zstack/header/vm/additions/VmHostBackupFileVO.java index 94662c612e0..383da0844a0 100644 --- a/header/src/main/java/org/zstack/header/vm/additions/VmHostBackupFileVO.java +++ b/header/src/main/java/org/zstack/header/vm/additions/VmHostBackupFileVO.java @@ -1,6 +1,5 @@ package org.zstack.header.vm.additions; -import org.zstack.header.vm.VmInstanceEO; import org.zstack.header.vo.EntityGraph; import org.zstack.header.vo.ForeignKey; import org.zstack.header.vo.ResourceVO; @@ -21,13 +20,12 @@ @Table @EntityGraph( friends = { - @EntityGraph.Neighbour(type = VmInstanceEO.class, myField = "vmInstanceUuid", targetField = "uuid"), + @EntityGraph.Neighbour(type = ResourceVO.class, myField = "resourceUuid", targetField = "uuid"), } ) public class VmHostBackupFileVO extends ResourceVO { @Column - @ForeignKey(parentEntityClass = VmInstanceEO.class, onDeleteAction = ForeignKey.ReferenceOption.CASCADE) - private String vmInstanceUuid; + private String resourceUuid; @Column @Enumerated(EnumType.STRING) private VmHostFileType type; @@ -36,12 +34,12 @@ public class VmHostBackupFileVO extends ResourceVO { @Column private Timestamp lastOpDate; - public String getVmInstanceUuid() { - return vmInstanceUuid; + public String getResourceUuid() { + return resourceUuid; } - public void setVmInstanceUuid(String vmInstanceUuid) { - this.vmInstanceUuid = vmInstanceUuid; + public void setResourceUuid(String resourceUuid) { + this.resourceUuid = resourceUuid; } public VmHostFileType getType() { @@ -71,7 +69,7 @@ public void setLastOpDate(Timestamp lastOpDate) { @Override public String toString() { return "VmHostBackupFileVO{" + - "vmInstanceUuid='" + vmInstanceUuid + '\'' + + "resourceUuid='" + resourceUuid + '\'' + ", type=" + type + ", createDate=" + createDate + ", lastOpDate=" + lastOpDate + diff --git a/header/src/main/java/org/zstack/header/vm/additions/VmHostBackupFileVO_.java b/header/src/main/java/org/zstack/header/vm/additions/VmHostBackupFileVO_.java index e45e804f381..355fb0661f9 100644 --- a/header/src/main/java/org/zstack/header/vm/additions/VmHostBackupFileVO_.java +++ b/header/src/main/java/org/zstack/header/vm/additions/VmHostBackupFileVO_.java @@ -8,7 +8,7 @@ @StaticMetamodel(VmHostBackupFileVO.class) public class VmHostBackupFileVO_ extends ResourceVO_ { - public static volatile SingularAttribute vmInstanceUuid; + public static volatile SingularAttribute resourceUuid; public static volatile SingularAttribute type; public static volatile SingularAttribute createDate; public static volatile SingularAttribute lastOpDate; diff --git a/plugin/kvm/src/main/java/org/zstack/kvm/efi/BackupVmHostFileMsg.java b/plugin/kvm/src/main/java/org/zstack/kvm/efi/BackupVmHostFileMsg.java new file mode 100644 index 00000000000..1eafe523e1c --- /dev/null +++ b/plugin/kvm/src/main/java/org/zstack/kvm/efi/BackupVmHostFileMsg.java @@ -0,0 +1,35 @@ +package org.zstack.kvm.efi; + +import org.zstack.header.message.NeedReplyMessage; + +import java.util.List; + +public class BackupVmHostFileMsg extends NeedReplyMessage { + private String vmUuid; + private String hostUuid; + private List toResourceUuidList; + + public String getVmUuid() { + return vmUuid; + } + + public void setVmUuid(String vmUuid) { + this.vmUuid = vmUuid; + } + + public String getHostUuid() { + return hostUuid; + } + + public void setHostUuid(String hostUuid) { + this.hostUuid = hostUuid; + } + + public List getToResourceUuidList() { + return toResourceUuidList; + } + + public void setToResourceUuidList(List toResourceUuidList) { + this.toResourceUuidList = toResourceUuidList; + } +} diff --git a/plugin/kvm/src/main/java/org/zstack/kvm/efi/BackupVmHostFileReply.java b/plugin/kvm/src/main/java/org/zstack/kvm/efi/BackupVmHostFileReply.java new file mode 100644 index 00000000000..a4d1c481c92 --- /dev/null +++ b/plugin/kvm/src/main/java/org/zstack/kvm/efi/BackupVmHostFileReply.java @@ -0,0 +1,17 @@ +package org.zstack.kvm.efi; + +import org.zstack.header.message.MessageReply; + +import java.util.List; + +public class BackupVmHostFileReply extends MessageReply { + private List backupFileUuidList; + + public List getBackupFileUuidList() { + return backupFileUuidList; + } + + public void setBackupFileUuidList(List backupFileUuidList) { + this.backupFileUuidList = backupFileUuidList; + } +} diff --git a/plugin/kvm/src/main/java/org/zstack/kvm/efi/KvmSecureBootExtensions.java b/plugin/kvm/src/main/java/org/zstack/kvm/efi/KvmSecureBootExtensions.java index a2d815d96a1..69a977847ec 100644 --- a/plugin/kvm/src/main/java/org/zstack/kvm/efi/KvmSecureBootExtensions.java +++ b/plugin/kvm/src/main/java/org/zstack/kvm/efi/KvmSecureBootExtensions.java @@ -556,7 +556,7 @@ public boolean skip(Map data) { public void run(FlowTrigger trigger, Map data) { context.vmBackupFileVO = Q.New(VmHostBackupFileVO.class) .eq(VmHostBackupFileVO_.type, context.type) - .eq(VmHostBackupFileVO_.vmInstanceUuid, context.vmUuid) + .eq(VmHostBackupFileVO_.resourceUuid, context.vmUuid) .orderByDesc(VmHostBackupFileVO_.lastOpDate) .limit(1) .find(); @@ -835,7 +835,7 @@ public void afterDestroyVm(VmInstanceInventory inv) { .eq(VmHostFileVO_.vmInstanceUuid, vmUuid) .delete(); SQL.New(VmHostBackupFileVO.class) - .eq(VmHostBackupFileVO_.vmInstanceUuid, vmUuid) + .eq(VmHostBackupFileVO_.resourceUuid, vmUuid) .delete(); } diff --git a/plugin/kvm/src/main/java/org/zstack/kvm/efi/KvmSecureBootManager.java b/plugin/kvm/src/main/java/org/zstack/kvm/efi/KvmSecureBootManager.java index 1febc137e09..56a80935d37 100644 --- a/plugin/kvm/src/main/java/org/zstack/kvm/efi/KvmSecureBootManager.java +++ b/plugin/kvm/src/main/java/org/zstack/kvm/efi/KvmSecureBootManager.java @@ -7,6 +7,7 @@ import org.zstack.core.cloudbus.CloudBus; import org.zstack.core.cloudbus.EventCallback; import org.zstack.core.cloudbus.EventFacadeImpl; +import org.zstack.core.cloudbus.MessageSafe; import org.zstack.core.db.Q; import org.zstack.core.db.SQLBatch; import org.zstack.core.workflow.SimpleFlowChain; @@ -143,9 +144,12 @@ public String getId() { } @Override + @MessageSafe public void handleMessage(Message msg) { if (msg instanceof CloneVmHostFileMsg) { handle((CloneVmHostFileMsg) msg); + } else if (msg instanceof BackupVmHostFileMsg) { + handle((BackupVmHostFileMsg) msg); } else { bus.dealWithUnknownMessage(msg); } @@ -287,7 +291,7 @@ public void run(FlowTrigger trigger, Map data) { } context.backupFiles.addAll(Q.New(VmHostBackupFileVO.class) - .eq(VmHostBackupFileVO_.vmInstanceUuid, msg.getSrcVmUuid()) + .eq(VmHostBackupFileVO_.resourceUuid, msg.getSrcVmUuid()) .in(VmHostFileVO_.type, missingTypes) .list()); trigger.next(); @@ -306,65 +310,7 @@ public void run(FlowTrigger trigger, Map data) { List filesAfterSyncing = Q.New(VmHostFileVO.class) .in(VmHostFileVO_.uuid, uuidList) .list(); - uuidList.addAll(transform(context.backupFiles, VmHostBackupFileVO::getUuid)); - List contents = Q.New(VmHostFileContentVO.class) - .in(VmHostFileContentVO_.uuid, uuidList) - .list(); - - List filesNeedPersists = new ArrayList<>(); - List contentsNeedPersists = new ArrayList<>(); - - Timestamp now = Timestamp.from(Instant.now()); - for (String vmUuid : msg.getDstVmUuidList()) { - for (String uuid : uuidList) { - VmHostFileContentVO srcContent = findOneOrNull(contents, - item -> item.getUuid().equals(uuid)); - if (srcContent == null) { - continue; - } - - VmHostFileVO vmHostFile = findOneOrNull(filesAfterSyncing, - item -> item.getUuid().equals(uuid)); - VmHostBackupFileVO vmHostBackupFile = vmHostFile == null ? - findOneOrNull(context.backupFiles, item -> item.getUuid().equals(uuid)) : null; - DebugUtils.Assert(vmHostFile != null || vmHostBackupFile != null, - "vmHostFile or vmHostBackupFile cannot be null"); - - VmHostBackupFileVO file = new VmHostBackupFileVO(); - file.setUuid(Platform.getUuid()); - file.setVmInstanceUuid(vmUuid); - file.setType(vmHostFile == null ? vmHostBackupFile.getType() : vmHostFile.getType()); - file.setCreateDate(now); - file.setLastOpDate(now); - filesNeedPersists.add(file); - - VmHostFileContentVO content = new VmHostFileContentVO(); - content.setUuid(file.getUuid()); - content.setContent(srcContent.getContent()); - content.setFormat(srcContent.getFormat()); - content.setCreateDate(now); - content.setLastOpDate(now); - contentsNeedPersists.add(content); - } - } - - if (logger.isTraceEnabled()) { - logger.trace(String.format("persist VmHostFileContentVO [uuid=%s]", - transform(contentsNeedPersists, VmHostFileContentVO::getUuid))); - } - - new SQLBatch() { - @Override - protected void scripts() { - if (!filesNeedPersists.isEmpty()) { - databaseFacade.persistCollection(filesNeedPersists); - } - if (!contentsNeedPersists.isEmpty()) { - databaseFacade.persistCollection(contentsNeedPersists); - } - } - }.execute(); - + backupVmHostFile(filesAfterSyncing, context.backupFiles, msg.getDstVmUuidList()); trigger.next(); } }).done(new FlowDoneHandler(msg) { @@ -380,4 +326,95 @@ public void handle(ErrorCode errCode, Map data) { } }).start(); } + + private void handle(BackupVmHostFileMsg msg) { + BackupVmHostFileReply reply = new BackupVmHostFileReply(); + List filesNeedPersists = backupVmHostFile( + msg.getVmUuid(), msg.getHostUuid(), msg.getToResourceUuidList()); + reply.setBackupFileUuidList(transform(filesNeedPersists, VmHostBackupFileVO::getUuid)); + bus.reply(msg, reply); + } + + private List backupVmHostFile(String fromVmUuid, String hostUuid, List toResourceList) { + List hostFiles = Q.New(VmHostFileVO.class) + .eq(VmHostFileVO_.vmInstanceUuid, fromVmUuid) + .eq(VmHostFileVO_.hostUuid, hostUuid) + .list(); + + if (hostFiles.isEmpty()) { + return new ArrayList<>(); + } + return backupVmHostFile(hostFiles, new ArrayList<>(), toResourceList); + } + + private List backupVmHostFile(List fileList, List backupFiles, List toResourceList) { + List uuidList = transform(fileList, VmHostFileVO::getUuid); + uuidList.addAll(transform(backupFiles, VmHostBackupFileVO::getUuid)); + List contents = Q.New(VmHostFileContentVO.class) + .in(VmHostFileContentVO_.uuid, uuidList) + .list(); + + List filesNeedPersists = new ArrayList<>(); + List contentsNeedPersists = new ArrayList<>(); + + Timestamp now = Timestamp.from(Instant.now()); + for (String resourceUuid : toResourceList) { + for (String uuid : uuidList) { + VmHostFileContentVO srcContent = findOneOrNull(contents, + item -> item.getUuid().equals(uuid)); + if (srcContent == null) { + continue; + } + + VmHostFileVO vmHostFile = findOneOrNull(fileList, + item -> item.getUuid().equals(uuid)); + VmHostBackupFileVO vmHostBackupFile = vmHostFile == null ? + findOneOrNull(backupFiles, item -> item.getUuid().equals(uuid)) : null; + DebugUtils.Assert(vmHostFile != null || vmHostBackupFile != null, + "vmHostFile or vmHostBackupFile cannot be null"); + + VmHostBackupFileVO file = new VmHostBackupFileVO(); + file.setUuid(Platform.getUuid()); + file.setResourceUuid(resourceUuid); + file.setType(vmHostFile == null ? vmHostBackupFile.getType() : vmHostFile.getType()); + file.setCreateDate(now); + file.setLastOpDate(now); + filesNeedPersists.add(file); + + VmHostFileContentVO content = new VmHostFileContentVO(); + content.setUuid(file.getUuid()); + content.setContent(srcContent.getContent()); + content.setFormat(srcContent.getFormat()); + content.setCreateDate(now); + content.setLastOpDate(now); + contentsNeedPersists.add(content); + } + } + + if (logger.isTraceEnabled()) { + logger.trace(String.format("persist VmHostFileContentVO [uuid=%s]", + transform(contentsNeedPersists, VmHostFileContentVO::getUuid))); + } + + new SQLBatch() { + @Override + protected void scripts() { + for (VmHostBackupFileVO backupFile : filesNeedPersists) { + // resourceUuid + type must be unique in DB + sql(VmHostBackupFileVO.class) + .eq(VmHostBackupFileVO_.resourceUuid, backupFile.getResourceUuid()) + .eq(VmHostBackupFileVO_.type, backupFile.getType()) + .delete(); + } + + if (!filesNeedPersists.isEmpty()) { + databaseFacade.persistCollection(filesNeedPersists); + } + if (!contentsNeedPersists.isEmpty()) { + databaseFacade.persistCollection(contentsNeedPersists); + } + } + }.execute(); + return filesNeedPersists; + } } diff --git a/storage/src/main/java/org/zstack/storage/snapshot/group/VolumeSnapshotGroupBase.java b/storage/src/main/java/org/zstack/storage/snapshot/group/VolumeSnapshotGroupBase.java index dbc130475e5..07881c552ed 100644 --- a/storage/src/main/java/org/zstack/storage/snapshot/group/VolumeSnapshotGroupBase.java +++ b/storage/src/main/java/org/zstack/storage/snapshot/group/VolumeSnapshotGroupBase.java @@ -31,6 +31,8 @@ import org.zstack.header.storage.snapshot.group.*; import org.zstack.header.vm.RestoreVmInstanceMsg; import org.zstack.header.vm.VmInstanceConstant; +import org.zstack.header.vm.additions.VmHostBackupFileVO; +import org.zstack.header.vm.additions.VmHostBackupFileVO_; import org.zstack.header.vm.devices.VmInstanceResourceMetadataManager; import org.zstack.header.volume.VolumeType; import org.zstack.header.volume.VolumeVO; @@ -142,6 +144,9 @@ public String getSyncSignature() { public void run(SyncTaskChain chain) { APIUngroupVolumeSnapshotGroupEvent evt = new APIUngroupVolumeSnapshotGroupEvent(msg.getId()); dbf.remove(self); + SQL.New(VmHostBackupFileVO.class) + .eq(VmHostBackupFileVO_.resourceUuid, msg.getUuid()) + .delete(); bus.publish(evt); chain.next(); } @@ -209,28 +214,43 @@ private void handle(DeleteVolumeSnapshotGroupInnerMsg msg) { logger.debug(String.format("skip snapshots not belong to origin vm[uuid:%s]", self.getVmInstanceUuid())); } - new While<>(snapshots).all((snapshot, compl) -> { - DeleteVolumeSnapshotMsg rmsg = new DeleteVolumeSnapshotMsg(); - rmsg.setSnapshotUuid(snapshot.getUuid()); - rmsg.setVolumeUuid(snapshot.getVolumeUuid()); - rmsg.setTreeUuid(snapshot.getTreeUuid()); - rmsg.setDeletionMode(msg.getDeletionMode()); - rmsg.setScope(msg.getScope()); - rmsg.setDirection(msg.getDirection()); - bus.makeTargetServiceIdByResourceUuid(rmsg, VolumeSnapshotConstant.SERVICE_ID, getResourceIdToRouteMsg(snapshot)); - bus.send(rmsg, new CloudBusCallBack(compl) { - @Override - public void run(MessageReply r) { - reply.addResult(new DeleteSnapshotGroupResult(rmsg.getSnapshotUuid(), rmsg.getVolumeUuid(), r.getError())); - compl.done(); - } - }); - }).run(new WhileDoneCompletion(msg) { - @Override - public void done(ErrorCodeList errorCodeList) { + SimpleFlowChain.of("delete-volume-snapshot-group") + .then("delete-volume-snapshots", (trigger) -> + new While<>(snapshots).step((snapshot, compl) -> { + DeleteVolumeSnapshotMsg rmsg = new DeleteVolumeSnapshotMsg(); + rmsg.setSnapshotUuid(snapshot.getUuid()); + rmsg.setVolumeUuid(snapshot.getVolumeUuid()); + rmsg.setTreeUuid(snapshot.getTreeUuid()); + rmsg.setDeletionMode(msg.getDeletionMode()); + rmsg.setScope(msg.getScope()); + rmsg.setDirection(msg.getDirection()); + bus.makeTargetServiceIdByResourceUuid(rmsg, VolumeSnapshotConstant.SERVICE_ID, getResourceIdToRouteMsg(snapshot)); + bus.send(rmsg, new CloudBusCallBack(compl) { + @Override + public void run(MessageReply r) { + reply.addResult(new DeleteSnapshotGroupResult(rmsg.getSnapshotUuid(), rmsg.getVolumeUuid(), r.getError())); + compl.done(); + } + }); + }, 5).run(new WhileDoneCompletion(msg) { + @Override + public void done(ErrorCodeList errorCodeList) { + trigger.next(); + } + })) + .then("delete-vm-host-backup-files", trigger -> { + SQL.New(VmHostBackupFileVO.class) + .eq(VmHostBackupFileVO_.resourceUuid, self.getUuid()) + .delete(); + trigger.next(); + }) + .propagateExceptionTo(msg) + .done(() -> bus.reply(msg, reply)) + .error(errorCode -> { + reply.setError(errorCode); bus.reply(msg, reply); - } - }); + }) + .start(); } private void handle(APIRevertVmFromSnapshotGroupMsg msg) { diff --git a/storage/src/main/java/org/zstack/storage/volume/VolumeBase.java b/storage/src/main/java/org/zstack/storage/volume/VolumeBase.java index e0ef4a6141a..f0cd6d4a511 100755 --- a/storage/src/main/java/org/zstack/storage/volume/VolumeBase.java +++ b/storage/src/main/java/org/zstack/storage/volume/VolumeBase.java @@ -41,6 +41,8 @@ import org.zstack.header.tag.SystemTagVO; import org.zstack.header.tag.SystemTagVO_; import org.zstack.header.vm.*; +import org.zstack.header.vm.additions.VmHostBackupFileVO; +import org.zstack.header.vm.additions.VmHostBackupFileVO_; import org.zstack.header.vm.devices.VmInstanceResourceMetadataManager; import org.zstack.header.volume.*; import org.zstack.header.volume.VolumeConstant.Capability; @@ -62,6 +64,7 @@ import javax.persistence.TypedQuery; import java.util.*; +import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; import static org.zstack.core.Platform.*; @@ -3084,6 +3087,7 @@ private void createSnapshotGroup(CreateVolumeSnapshotGroupMessage msg, ReturnVal CreateVolumesSnapshotMsg cmsg = new CreateVolumesSnapshotMsg(); List volumesSnapshotsJobs = new ArrayList<>(); cmsg.setAccountUuid(msg.getSession().getAccountUuid()); + cmsg.setBackupHostFileIfNeeded(true); VmInstanceInventory vm = msg.getVmInstance(); Map vols = vm.getAllVolumes().stream() @@ -3104,55 +3108,92 @@ private void createSnapshotGroup(CreateVolumeSnapshotGroupMessage msg, ReturnVal cmsg.setVolumeSnapshotJobs(volumesSnapshotsJobs); cmsg.setConsistentType(msg.getConsistentType()); - bus.makeTargetServiceIdByResourceUuid(cmsg, VolumeConstant.SERVICE_ID, msg.getRootVolumeUuid()); - bus.send(cmsg, new CloudBusCallBack(completion) { - @Override - public void run(MessageReply reply) { - if (!reply.isSuccess()) { - completion.fail(reply.getError()); - return; - } + List inventories = new ArrayList<>(); + List hostBackupFileUuidList = new ArrayList<>(); + AtomicReference groupRef = new AtomicReference<>(null); + String resourceUuid = msg.getResourceUuid() == null ? getUuid() : msg.getResourceUuid(); + SimpleFlowChain.of("create-snapshot-group") + .then("handle-create-snapshot-group-message", trigger -> { + bus.makeTargetServiceIdByResourceUuid(cmsg, VolumeConstant.SERVICE_ID, msg.getRootVolumeUuid()); + bus.send(cmsg, new CloudBusCallBack(completion) { + @Override + public void run(MessageReply reply) { + if (!reply.isSuccess()) { + trigger.fail(reply.getError()); + return; + } + CreateVolumesSnapshotReply r = reply.castReply(); + inventories.addAll(r.getInventories()); + if (!CollectionUtils.isEmpty(r.getHostBackupFileUuidList())) { + hostBackupFileUuidList.addAll(r.getHostBackupFileUuidList()); + } + trigger.next(); + } + }); + }) + .then(Flow.of("persist-snapshot-group") + .handle(trigger -> { + List refs = new ArrayList<>(); + VolumeSnapshotGroupVO group = new VolumeSnapshotGroupVO(); + group.setUuid(resourceUuid); + group.setSnapshotCount(cmsg.getVolumeSnapshotJobs().size()); + group.setName(msg.getName()); + group.setDescription(msg.getDescription()); + group.setVmInstanceUuid(vm.getUuid()); + group.setAccountUuid(msg.getSession().getAccountUuid()); + for (VolumeSnapshotInventory inv : inventories) { + VolumeSnapshotGroupRefVO ref = new VolumeSnapshotGroupRefVO(); + ref.setVolumeUuid(inv.getVolumeUuid()); + ref.setVolumeName(vols.get(inv.getVolumeUuid()).getName()); + ref.setVolumeType(inv.getVolumeType()); + ref.setVolumeSnapshotGroupUuid(group.getUuid()); + ref.setVolumeSnapshotUuid(inv.getUuid()); + ref.setVolumeSnapshotName(inv.getName()); + ref.setVolumeSnapshotInstallPath(inv.getPrimaryStorageInstallPath()); + ref.setDeviceId(vols.get(inv.getVolumeUuid()).getDeviceId()); + ref.setVolumeLastAttachDate(vols.get(inv.getVolumeUuid()).getLastAttachDate()); + refs.add(ref); + } + + new SQLBatch() { + @Override + protected void scripts() { + databaseFacade.persist(group); + databaseFacade.persistCollection(refs); + } + }.execute(); - CreateVolumesSnapshotReply r = reply.castReply(); - VolumeSnapshotGroupVO group = createGroup(r); + groupRef.set(group); + trigger.next(); + }) + .rollback(trigger -> { + SQL.New(VolumeSnapshotGroupVO.class) + .eq(VolumeSnapshotGroupVO_.uuid, resourceUuid) + .delete(); + // VolumeSnapshotGroupRefVO delete in cascade + trigger.rollback(); + }) + .build()) + .then(Flow.of("persist-vm-host-backup-file") + .handle(trigger -> { + if (!CollectionUtils.isEmpty(hostBackupFileUuidList)) { + SQL.New(VmHostBackupFileVO.class) + .in(VmHostBackupFileVO_.uuid, hostBackupFileUuidList) + .set(VmHostBackupFileVO_.resourceUuid, resourceUuid) + .update(); + } + trigger.next(); + }) + .build()) + .propagateExceptionTo(completion) + .done(() -> { logger.debug(String.format("created volume snapshot group[uuid:%s] for vm[uuid:%s]", - group.getUuid(), vm.getUuid())); - completion.success(VolumeSnapshotGroupInventory.valueOf(dbf.reload(group))); - } - - private VolumeSnapshotGroupVO createGroup(CreateVolumesSnapshotReply r) { - List refs = new ArrayList<>(); - VolumeSnapshotGroupVO group = new VolumeSnapshotGroupVO(); - if (msg.getResourceUuid() != null) { - group.setUuid(msg.getResourceUuid()); - } else { - group.setUuid(getUuid()); - } - group.setSnapshotCount(cmsg.getVolumeSnapshotJobs().size()); - group.setName(msg.getName()); - group.setDescription(msg.getDescription()); - group.setVmInstanceUuid(vm.getUuid()); - group.setAccountUuid(msg.getSession().getAccountUuid()); - for (VolumeSnapshotInventory inv : r.getInventories()) { - VolumeSnapshotGroupRefVO ref = new VolumeSnapshotGroupRefVO(); - ref.setVolumeUuid(inv.getVolumeUuid()); - ref.setVolumeName(vols.get(inv.getVolumeUuid()).getName()); - ref.setVolumeType(inv.getVolumeType()); - ref.setVolumeSnapshotGroupUuid(group.getUuid()); - ref.setVolumeSnapshotUuid(inv.getUuid()); - ref.setVolumeSnapshotName(inv.getName()); - ref.setVolumeSnapshotInstallPath(inv.getPrimaryStorageInstallPath()); - ref.setDeviceId(vols.get(inv.getVolumeUuid()).getDeviceId()); - ref.setVolumeLastAttachDate(vols.get(inv.getVolumeUuid()).getLastAttachDate()); - refs.add(ref); - } - - dbf.persist(group); - dbf.persistCollection(refs); - return group; - } - }); + groupRef.get().getUuid(), vm.getUuid())); + completion.success(VolumeSnapshotGroupInventory.valueOf(dbf.reload(groupRef.get()))); + }) + .error(completion::fail) + .start(); } private void handle(APIFlattenVolumeMsg msg) {