diff --git a/header/src/main/java/org/zstack/header/storage/primary/ConvertVolumeEncryptionOnPrimaryStorageMsg.java b/header/src/main/java/org/zstack/header/storage/primary/ConvertVolumeEncryptionOnPrimaryStorageMsg.java index 2c36e56e9eb..6554cf85d9d 100644 --- a/header/src/main/java/org/zstack/header/storage/primary/ConvertVolumeEncryptionOnPrimaryStorageMsg.java +++ b/header/src/main/java/org/zstack/header/storage/primary/ConvertVolumeEncryptionOnPrimaryStorageMsg.java @@ -54,7 +54,6 @@ public static class VolumeEncryptionConversionItem implements Serializable { private String resourceUuid; private String resourceType; private String sourceInstallPath; - private String sourceTrashInstallPath; private String targetInstallPath; private String targetBackingInstallPath; @@ -82,14 +81,6 @@ public void setSourceInstallPath(String sourceInstallPath) { this.sourceInstallPath = sourceInstallPath; } - public String getSourceTrashInstallPath() { - return sourceTrashInstallPath; - } - - public void setSourceTrashInstallPath(String sourceTrashInstallPath) { - this.sourceTrashInstallPath = sourceTrashInstallPath; - } - public String getTargetInstallPath() { return targetInstallPath; } diff --git a/header/src/main/java/org/zstack/header/storage/primary/RollbackVolumeEncryptionOnPrimaryStorageMsg.java b/header/src/main/java/org/zstack/header/storage/primary/RollbackVolumeEncryptionOnPrimaryStorageMsg.java new file mode 100644 index 00000000000..557c8b9e6a8 --- /dev/null +++ b/header/src/main/java/org/zstack/header/storage/primary/RollbackVolumeEncryptionOnPrimaryStorageMsg.java @@ -0,0 +1,42 @@ +package org.zstack.header.storage.primary; + +import org.zstack.header.message.NeedReplyMessage; +import org.zstack.header.volume.VolumeInventory; + +import java.util.ArrayList; +import java.util.List; + +public class RollbackVolumeEncryptionOnPrimaryStorageMsg extends NeedReplyMessage implements PrimaryStorageMessage { + private String hostUuid; + private VolumeInventory volume; + private List items = new ArrayList<>(); + + @Override + public String getPrimaryStorageUuid() { + return volume.getPrimaryStorageUuid(); + } + + public String getHostUuid() { + return hostUuid; + } + + public void setHostUuid(String hostUuid) { + this.hostUuid = hostUuid; + } + + public VolumeInventory getVolume() { + return volume; + } + + public void setVolume(VolumeInventory volume) { + this.volume = volume; + } + + public List getItems() { + return items; + } + + public void setItems(List items) { + this.items = items; + } +} diff --git a/header/src/main/java/org/zstack/header/storage/primary/RollbackVolumeEncryptionOnPrimaryStorageReply.java b/header/src/main/java/org/zstack/header/storage/primary/RollbackVolumeEncryptionOnPrimaryStorageReply.java new file mode 100644 index 00000000000..9c8e2684585 --- /dev/null +++ b/header/src/main/java/org/zstack/header/storage/primary/RollbackVolumeEncryptionOnPrimaryStorageReply.java @@ -0,0 +1,6 @@ +package org.zstack.header.storage.primary; + +import org.zstack.header.message.MessageReply; + +public class RollbackVolumeEncryptionOnPrimaryStorageReply extends MessageReply { +} diff --git a/plugin/ceph/src/main/java/org/zstack/storage/ceph/primary/CephPrimaryStorageBase.java b/plugin/ceph/src/main/java/org/zstack/storage/ceph/primary/CephPrimaryStorageBase.java index e6eb650e1a4..00ae2569112 100755 --- a/plugin/ceph/src/main/java/org/zstack/storage/ceph/primary/CephPrimaryStorageBase.java +++ b/plugin/ceph/src/main/java/org/zstack/storage/ceph/primary/CephPrimaryStorageBase.java @@ -459,7 +459,6 @@ public static class KVMHostLuksConvertCmd implements Serializable { public String encryptedDek; public String installPath; public String targetInstallPath; - public String sourceTrashInstallPath; public boolean targetEncrypted; public Long virtualSize; } @@ -490,17 +489,8 @@ public static class KVMHostLuksRsp extends KVMAgentCommands.AgentResponse { public Long actualSize; } - public static class ReplaceLuksRbdCmd extends AgentCommand { - public String installPath; + public static class RollbackLuksRbdCmd extends AgentCommand { public String targetInstallPath; - public String temporaryInstallPath; - public String sourceTrashInstallPath; - public boolean deleteSourceTrash; - } - - public static class ReplaceLuksRbdRsp extends AgentResponse { - public Long size; - public Long actualSize; } public static class SwapInPlaceLuksRbdCmd extends AgentCommand { @@ -1454,7 +1444,7 @@ public int compareTo(SnapInfo snapInfo) { public static final String KVM_HOST_LUKS_ENCRYPT_IN_PLACE_PATH = "/ceph/primarystorage/kvmhost/encryptinplace"; public static final String KVM_HOST_LUKS_RESIZE_PATH = "/ceph/primarystorage/kvmhost/luksresize"; public static final String KVM_HOST_LUKS_CONVERT_PATH = "/ceph/primarystorage/kvmhost/luksconvert"; - public static final String LUKS_REPLACE_VOLUME_PATH = "/ceph/primarystorage/volume/luksreplace"; + public static final String LUKS_ROLLBACK_CONVERT_PATH = "/ceph/primarystorage/volume/luksconvert/rollback"; public static final String LUKS_SWAP_IN_PLACE_PATH = "/ceph/primarystorage/volume/luksswapinplace"; public static final String KVM_HOST_IMAGESTORE_ENCRYPTED_DOWNLOAD_PATH = "/ceph/primarystorage/kvmhost/imagestore/encrypteddownload"; public static final String FLATTEN_PATH = "/ceph/primarystorage/volume/flatten"; @@ -3629,12 +3619,83 @@ protected void handle(ConvertVolumeEncryptionOnPrimaryStorageMsg msg) { KVMHostLuksConvertCmd kcmd = new KVMHostLuksConvertCmd(); kcmd.psUuid = self.getUuid(); kcmd.installPath = item.getSourceInstallPath(); - kcmd.targetInstallPath = makeTemporaryLuksRbdInstallPath(item.getTargetInstallPath(), "converting"); - kcmd.sourceTrashInstallPath = item.getSourceTrashInstallPath(); + kcmd.targetInstallPath = item.getTargetInstallPath(); kcmd.targetEncrypted = msg.isTargetEncrypted(); kcmd.virtualSize = msg.getVolume().getSize(); kcmd.encryptedDek = volumeEncryptedSecretHelper.materializeAndSealVolumeDekForHost(hostUuid, msg.getVolume().getUuid()); + CheckIsBitsExistingCmd checkTargetCmd = new CheckIsBitsExistingCmd(); + checkTargetCmd.setInstallPath(item.getTargetInstallPath()); + httpCall(CHECK_BITS_PATH, checkTargetCmd, CheckIsBitsExistingRsp.class, + new ReturnValueCompletion(msg) { + @Override + public void success(CheckIsBitsExistingRsp returnValue) { + if (returnValue.isExisting()) { + reply.setError(operr("RBD LUKS conversion target already exists: %s", item.getTargetInstallPath())); + bus.reply(msg, reply); + return; + } + convertVolumeEncryptionOnKvmHost(msg, reply, kcmd, hostUuid); + } + + @Override + public void fail(ErrorCode errorCode) { + reply.setError(errorCode); + bus.reply(msg, reply); + } + }); + } + + @Override + protected void handle(RollbackVolumeEncryptionOnPrimaryStorageMsg msg) { + RollbackVolumeEncryptionOnPrimaryStorageReply reply = new RollbackVolumeEncryptionOnPrimaryStorageReply(); + if (msg.getItems() == null || msg.getItems().size() != 1) { + reply.setError(operr( + "ceph rollback volume encryption conversion requires volume snapshots to be merged; volume[uuid:%s] has %s rollback items", + msg.getVolume().getUuid(), msg.getItems() == null ? 0 : msg.getItems().size())); + bus.reply(msg, reply); + return; + } + + ConvertVolumeEncryptionOnPrimaryStorageMsg.VolumeEncryptionConversionItem item = msg.getItems().get(0); + if (!VolumeVO.class.getSimpleName().equals(item.getResourceType())) { + reply.setError(operr( + "ceph rollback volume encryption conversion only supports the active volume, but got resource[type:%s, uuid:%s]", + item.getResourceType(), item.getResourceUuid())); + bus.reply(msg, reply); + return; + } + + if (StringUtils.isBlank(item.getTargetInstallPath()) || item.getTargetInstallPath().contains("@")) { + reply.setError(operr( + "ceph rollback volume encryption conversion requires an active RBD target path, but got target path[%s]", + item.getTargetInstallPath())); + bus.reply(msg, reply); + return; + } + + RollbackLuksRbdCmd cmd = new RollbackLuksRbdCmd(); + cmd.targetInstallPath = item.getTargetInstallPath(); + httpCall(LUKS_ROLLBACK_CONVERT_PATH, cmd, AgentResponse.class, + new ReturnValueCompletion(msg) { + @Override + public void success(AgentResponse returnValue) { + bus.reply(msg, reply); + } + + @Override + public void fail(ErrorCode errorCode) { + reply.setError(errorCode); + bus.reply(msg, reply); + } + }); + } + + private void convertVolumeEncryptionOnKvmHost(ConvertVolumeEncryptionOnPrimaryStorageMsg msg, + ConvertVolumeEncryptionOnPrimaryStorageReply reply, + KVMHostLuksConvertCmd kcmd, + String hostUuid) { + ConvertVolumeEncryptionOnPrimaryStorageMsg.VolumeEncryptionConversionItem item = msg.getItems().get(0); httpCallToKvmHost(hostUuid, KVM_HOST_LUKS_CONVERT_PATH, kcmd, KVMHostLuksRsp.class, new ReturnValueCompletion(msg) { @Override @@ -3646,16 +3707,16 @@ public void fail(ErrorCode err) { @Override public void success(KVMHostLuksRsp ret) { - ReplaceLuksRbdCmd rcmd = new ReplaceLuksRbdCmd(); - rcmd.installPath = item.getSourceInstallPath(); - rcmd.targetInstallPath = item.getTargetInstallPath(); - rcmd.temporaryInstallPath = kcmd.targetInstallPath; - rcmd.sourceTrashInstallPath = item.getSourceTrashInstallPath(); - rcmd.deleteSourceTrash = false; - httpCall(LUKS_REPLACE_VOLUME_PATH, rcmd, ReplaceLuksRbdRsp.class, - new ReturnValueCompletion(msg) { + if (ret.actualSize != null) { + reply.setActualSizes(Collections.singletonMap(msg.getVolume().getUuid(), ret.actualSize)); + bus.reply(msg, reply); + return; + } + + syncVolumeSize(msg.getVolume().getUuid(), item.getTargetInstallPath(), + new ReturnValueCompletion(msg) { @Override - public void success(ReplaceLuksRbdRsp rsp) { + public void success(GetVolumeSizeRsp rsp) { if (rsp.actualSize != null) { reply.setActualSizes(Collections.singletonMap(msg.getVolume().getUuid(), rsp.actualSize)); } @@ -3664,8 +3725,8 @@ public void success(ReplaceLuksRbdRsp rsp) { @Override public void fail(ErrorCode errorCode) { - deleteRbdBitsBestEffort(kcmd.targetInstallPath); - reply.setError(errorCode); + logger.warn(String.format("failed to sync converted RBD volume[uuid:%s, installPath:%s] size: %s", + msg.getVolume().getUuid(), item.getTargetInstallPath(), errorCode.getReadableDetails())); bus.reply(msg, reply); } }); diff --git a/plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageBase.java b/plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageBase.java index 5e4831fc1de..a79b0172a24 100755 --- a/plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageBase.java +++ b/plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageBase.java @@ -912,6 +912,8 @@ public void handleLocalMessage(Message msg) { handle((EncryptVolumeBitsOnPrimaryStorageMsg) msg); } else if (msg instanceof ConvertVolumeEncryptionOnPrimaryStorageMsg) { handle((ConvertVolumeEncryptionOnPrimaryStorageMsg) msg); + } else if (msg instanceof RollbackVolumeEncryptionOnPrimaryStorageMsg) { + handle((RollbackVolumeEncryptionOnPrimaryStorageMsg) msg); } else { super.handleLocalMessage(msg); } @@ -959,6 +961,48 @@ public void fail(ErrorCode errorCode) { }); } + protected void handle(RollbackVolumeEncryptionOnPrimaryStorageMsg msg) { + RollbackVolumeEncryptionOnPrimaryStorageReply reply = new RollbackVolumeEncryptionOnPrimaryStorageReply(); + if (msg.getVolume() == null || StringUtils.isBlank(msg.getVolume().getUuid())) { + reply.setError(operr("rollback volume encryption on local primary storage[uuid:%s] requires volume with non-blank uuid", + self.getUuid())); + bus.reply(msg, reply); + return; + } + + String hostUuid = StringUtils.isNotBlank(msg.getHostUuid()) ? + msg.getHostUuid() : null; + if (StringUtils.isBlank(hostUuid)) { + try { + hostUuid = getHostUuidByResourceUuid(msg.getVolume().getUuid()); + } catch (OperationFailureException e) { + reply.setError(e.getErrorCode()); + bus.reply(msg, reply); + return; + } + } + if (StringUtils.isBlank(hostUuid)) { + reply.setError(operr("cannot determine host for rolling back volume[uuid:%s] encryption conversion on local primary storage[uuid:%s]", + msg.getVolume().getUuid(), self.getUuid())); + bus.reply(msg, reply); + return; + } + + LocalStorageHypervisorBackend bkd = getHypervisorBackendFactoryByHostUuid(hostUuid).getHypervisorBackend(self); + bkd.handle(msg, hostUuid, new ReturnValueCompletion(msg) { + @Override + public void success(RollbackVolumeEncryptionOnPrimaryStorageReply returnValue) { + bus.reply(msg, returnValue); + } + + @Override + public void fail(ErrorCode errorCode) { + reply.setError(errorCode); + bus.reply(msg, reply); + } + }); + } + private void handle(EncryptVolumeBitsOnPrimaryStorageMsg msg) { if (StringUtils.isBlank(msg.getHostUuid()) || StringUtils.isBlank(msg.getVolumeUuid())) { EncryptVolumeBitsOnPrimaryStorageReply reply = new EncryptVolumeBitsOnPrimaryStorageReply(); diff --git a/plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageHypervisorBackend.java b/plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageHypervisorBackend.java index b2d5816d860..4d8729e7543 100755 --- a/plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageHypervisorBackend.java +++ b/plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageHypervisorBackend.java @@ -139,4 +139,6 @@ abstract void createEmptyVolumeWithBackingFile(VolumeInventory volume, String ho abstract void handle(EncryptVolumeBitsOnPrimaryStorageMsg msg, ReturnValueCompletion completion); abstract void handle(ConvertVolumeEncryptionOnPrimaryStorageMsg msg, String hostUuid, ReturnValueCompletion completion); + + abstract void handle(RollbackVolumeEncryptionOnPrimaryStorageMsg msg, String hostUuid, ReturnValueCompletion completion); } diff --git a/plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageKvmBackend.java b/plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageKvmBackend.java index e39cb78e18a..3e37a3aa402 100755 --- a/plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageKvmBackend.java +++ b/plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageKvmBackend.java @@ -307,6 +307,14 @@ public static class ConvertVolumeEncryptionRsp extends AgentResponse { public Map actualSizes; } + public static class RollbackVolumeEncryptionCmd extends AgentCommand implements Serializable { + public String volumeUuid; + public List items; + } + + public static class RollbackVolumeEncryptionRsp extends AgentResponse { + } + public static class GetPhysicalCapacityCmd extends AgentCommand { private String hostUuid; @@ -1097,6 +1105,7 @@ public static class PrefixRebaseBackingFilesRsp extends LocalStorageKvmBackend.A public static final String PREFIX_REBASE_BACKING_FILES_PATH = "/localstorage/snapshot/prefixrebasebackingfiles"; public static final String ENCRYPT_VOLUME_BITS_PATH = "/localstorage/volume/encryptinplace"; public static final String CONVERT_VOLUME_ENCRYPTION_PATH = "/localstorage/volume/convertencryption"; + public static final String ROLLBACK_VOLUME_ENCRYPTION_PATH = "/localstorage/volume/convertencryption/rollback"; public LocalStorageKvmBackend() { } @@ -4183,6 +4192,28 @@ public void fail(ErrorCode errorCode) { }); } + @Override + void handle(RollbackVolumeEncryptionOnPrimaryStorageMsg msg, String hostUuid, + ReturnValueCompletion completion) { + RollbackVolumeEncryptionCmd cmd = new RollbackVolumeEncryptionCmd(); + cmd.volumeUuid = msg.getVolume().getUuid(); + cmd.items = msg.getItems(); + + httpCall(ROLLBACK_VOLUME_ENCRYPTION_PATH, hostUuid, cmd, RollbackVolumeEncryptionRsp.class, + new ReturnValueCompletion(completion) { + @Override + public void success(RollbackVolumeEncryptionRsp rsp) { + completion.success(new RollbackVolumeEncryptionOnPrimaryStorageReply()); + } + + @Override + public void fail(ErrorCode errorCode) { + completion.fail(operr("failed to rollback volume[uuid:%s] encryption conversion on local storage host[uuid:%s]: %s", + msg.getVolume().getUuid(), hostUuid, errorCode)); + } + }); + } + private String prepareVolumeEncryptedDek(String hostUuid, VolumeInventory volume, boolean required) { if (!required) { return null; diff --git a/plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorage.java b/plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorage.java index 04b92877dd4..8b691c047bd 100755 --- a/plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorage.java +++ b/plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorage.java @@ -140,6 +140,8 @@ protected void handleLocalMessage(Message msg) { handle((EncryptVolumeBitsOnPrimaryStorageMsg) msg); } else if (msg instanceof ConvertVolumeEncryptionOnPrimaryStorageMsg) { handle((ConvertVolumeEncryptionOnPrimaryStorageMsg) msg); + } else if (msg instanceof RollbackVolumeEncryptionOnPrimaryStorageMsg) { + handle((RollbackVolumeEncryptionOnPrimaryStorageMsg) msg); } else { super.handleLocalMessage(msg); } @@ -169,6 +171,30 @@ public void fail(ErrorCode errorCode) { }); } + protected void handle(RollbackVolumeEncryptionOnPrimaryStorageMsg msg) { + RollbackVolumeEncryptionOnPrimaryStorageReply reply = new RollbackVolumeEncryptionOnPrimaryStorageReply(); + NfsPrimaryStorageBackend backend = getUsableBackend(); + if (backend == null) { + reply.setError(operr("the NFS primary storage[uuid:%s, name:%s] cannot find any usable host to rollback volume[uuid:%s] encryption conversion", + self.getUuid(), self.getName(), msg.getVolume().getUuid())); + bus.reply(msg, reply); + return; + } + + backend.handle(getSelfInventory(), msg, new ReturnValueCompletion(msg) { + @Override + public void success(RollbackVolumeEncryptionOnPrimaryStorageReply returnValue) { + bus.reply(msg, returnValue); + } + + @Override + public void fail(ErrorCode errorCode) { + reply.setError(errorCode); + bus.reply(msg, reply); + } + }); + } + private void handle(EncryptVolumeBitsOnPrimaryStorageMsg msg) { NfsPrimaryStorageBackend backend = getUsableBackend(); if (backend == null) { diff --git a/plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorageBackend.java b/plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorageBackend.java index f769722cfa2..948291ad67a 100755 --- a/plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorageBackend.java +++ b/plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorageBackend.java @@ -56,6 +56,8 @@ public interface NfsPrimaryStorageBackend { void handle(PrimaryStorageInventory inv, ConvertVolumeEncryptionOnPrimaryStorageMsg msg, ReturnValueCompletion completion); + void handle(PrimaryStorageInventory inv, RollbackVolumeEncryptionOnPrimaryStorageMsg msg, ReturnValueCompletion completion); + void getPhysicalCapacity(PrimaryStorageInventory inv, ReturnValueCompletion completion); void checkIsBitsExisting(PrimaryStorageInventory inv, String installPath, ReturnValueCompletion completion); diff --git a/plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorageKVMBackend.java b/plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorageKVMBackend.java index abb511dbd9c..47799d4b1ee 100755 --- a/plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorageKVMBackend.java +++ b/plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorageKVMBackend.java @@ -140,6 +140,7 @@ public class NfsPrimaryStorageKVMBackend implements NfsPrimaryStorageBackend, public static final String CREATE_VOLUME_FROM_TEMPLATE_PATH = "/nfsprimarystorage/sftp/createvolumefromtemplate"; public static final String ENCRYPT_VOLUME_BITS_PATH = "/nfsprimarystorage/volume/encryptinplace"; public static final String CONVERT_VOLUME_ENCRYPTION_PATH = "/nfsprimarystorage/volume/convertencryption"; + public static final String ROLLBACK_VOLUME_ENCRYPTION_PATH = "/nfsprimarystorage/volume/convertencryption/rollback"; public static final String GET_QCOW2_HASH_VALUE_PATH = "/nfsprimarystorage/getqcow2hash"; public static final String WRITE_VM_METADATA_PATH = "/nfsprimarystorage/vm/metadata/write"; @@ -2387,6 +2388,40 @@ public void run(MessageReply reply) { }); } + @Override + public void handle(PrimaryStorageInventory inv, RollbackVolumeEncryptionOnPrimaryStorageMsg msg, + ReturnValueCompletion completion) { + HostInventory host = nfsFactory.getConnectedHostForOperation(inv).get(0); + RollbackVolumeEncryptionCmd cmd = new RollbackVolumeEncryptionCmd(); + cmd.setUuid(inv.getUuid()); + cmd.volumeUuid = msg.getVolume().getUuid(); + cmd.items = msg.getItems(); + + KVMHostAsyncHttpCallMsg hmsg = new KVMHostAsyncHttpCallMsg(); + hmsg.setCommand(cmd); + hmsg.setPath(ROLLBACK_VOLUME_ENCRYPTION_PATH); + hmsg.setHostUuid(host.getUuid()); + bus.makeTargetServiceIdByResourceUuid(hmsg, HostConstant.SERVICE_ID, host.getUuid()); + bus.send(hmsg, new CloudBusCallBack(completion) { + @Override + public void run(MessageReply reply) { + if (!reply.isSuccess()) { + completion.fail(reply.getError()); + return; + } + + RollbackVolumeEncryptionRsp rsp = ((KVMHostAsyncHttpCallReply) reply).toResponse(RollbackVolumeEncryptionRsp.class); + if (!rsp.isSuccess()) { + completion.fail(operr("failed to rollback volume[uuid:%s] encryption conversion on NFS primary storage[uuid:%s] via host[uuid:%s]: %s", + msg.getVolume().getUuid(), inv.getUuid(), host.getUuid(), rsp.getError())); + return; + } + + completion.success(new RollbackVolumeEncryptionOnPrimaryStorageReply()); + } + }); + } + private String prepareVolumeEncryptedDek(String hostUuid, VolumeInventory volume, boolean required) { if (!required) { return null; diff --git a/plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorageKVMBackendCommands.java b/plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorageKVMBackendCommands.java index 522b4b883f2..8b124ad7fc5 100755 --- a/plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorageKVMBackendCommands.java +++ b/plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorageKVMBackendCommands.java @@ -549,6 +549,14 @@ public static class ConvertVolumeEncryptionRsp extends NfsPrimaryStorageAgentRes public Map actualSizes; } + public static class RollbackVolumeEncryptionCmd extends NfsPrimaryStorageAgentCommand implements Serializable { + public String volumeUuid; + public List items; + } + + public static class RollbackVolumeEncryptionRsp extends NfsPrimaryStorageAgentResponse { + } + public static class DeleteCmd extends NfsPrimaryStorageAgentCommand { private boolean folder; private String installPath; diff --git a/storage/src/main/java/org/zstack/storage/primary/PrimaryStorageBase.java b/storage/src/main/java/org/zstack/storage/primary/PrimaryStorageBase.java index e94bf01ec58..f8c3a128dc5 100755 --- a/storage/src/main/java/org/zstack/storage/primary/PrimaryStorageBase.java +++ b/storage/src/main/java/org/zstack/storage/primary/PrimaryStorageBase.java @@ -188,6 +188,13 @@ protected void handle(ConvertVolumeEncryptionOnPrimaryStorageMsg msg) { bus.reply(msg, reply); } + protected void handle(RollbackVolumeEncryptionOnPrimaryStorageMsg msg) { + RollbackVolumeEncryptionOnPrimaryStorageReply reply = new RollbackVolumeEncryptionOnPrimaryStorageReply(); + reply.setError(operr("primary storage[type:%s, uuid:%s] does not support rollback volume encryption conversion", + self.getType(), self.getUuid())); + bus.reply(msg, reply); + } + public PrimaryStorageBase(PrimaryStorageVO self) { this.self = self; } @@ -431,6 +438,8 @@ protected void handleLocalMessage(Message msg) { handle((GetVolumeSnapshotEncryptedOnPrimaryStorageMsg) msg); } else if (msg instanceof ConvertVolumeEncryptionOnPrimaryStorageMsg) { handle((ConvertVolumeEncryptionOnPrimaryStorageMsg) msg); + } else if (msg instanceof RollbackVolumeEncryptionOnPrimaryStorageMsg) { + handle((RollbackVolumeEncryptionOnPrimaryStorageMsg) msg); } else if (msg instanceof DeleteVolumeChainOnPrimaryStorageMsg) { handle((DeleteVolumeChainOnPrimaryStorageMsg) msg); } else if (msg instanceof CleanUpStorageTrashOnPrimaryStorageMsg) { 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 c364e790fe9..2ae6b4ac6d9 100755 --- a/storage/src/main/java/org/zstack/storage/volume/VolumeBase.java +++ b/storage/src/main/java/org/zstack/storage/volume/VolumeBase.java @@ -3308,7 +3308,6 @@ public String getName() { private static class VolumeEncryptionConversionContext { List snapshots; Map oldAndNewInstallPaths; - Map oldAndTrashInstallPaths; VolumeInventory oldVolumeInventory; List oldSnapshotInventories; List items; @@ -3365,7 +3364,7 @@ public void rollback(FlowRollback trigger, Map data) { @Override public void run(FlowTrigger trigger, Map data) { - conversionContext.set(buildVolumeEncryptionConversionContext()); + conversionContext.set(buildVolumeEncryptionConversionContext(targetEncrypted)); trigger.next(); } }); @@ -3396,8 +3395,7 @@ public void run(MessageReply reply) { @Override public void rollback(FlowRollback trigger, Map data) { - deleteConvertedVolumeEncryptionBits(conversionContext.get().items); - trigger.rollback(); + rollbackVolumeEncryptionOnPrimaryStorage(conversionContext.get().items, trigger); } }); @@ -3409,7 +3407,16 @@ public void run(FlowTrigger trigger, Map data) { VolumeEncryptionConversionContext ctx = conversionContext.get(); updateVolumeEncryptionConversionInDb(targetEncrypted, ctx.snapshots, ctx.oldAndNewInstallPaths, (Map) data.get("actualSizes")); - refreshVO(); + try { + refreshVO(); + } catch (RuntimeException e) { + logger.warn(String.format("failed to refresh volume[uuid:%s] after encryption conversion DB update: %s", + self.getUuid(), e.getMessage()), e); + VolumeVO latest = dbf.findByUuid(self.getUuid(), VolumeVO.class); + if (latest != null) { + self = latest; + } + } trigger.next(); } @@ -3434,8 +3441,14 @@ public void run(FlowTrigger trigger, Map data) { return; } - volumeEncryptedSecretHelper.deleteSecretOnHostBestEffort( - sourceSecretHostUuid, sourceSecretVmUuid, self.getUuid(), sourceKeyVersion); + try { + volumeEncryptedSecretHelper.deleteSecretOnHostBestEffort( + sourceSecretHostUuid, sourceSecretVmUuid, self.getUuid(), sourceKeyVersion); + VolumeSystemTags.VOLUME_LIBVIRT_SECRET_HOST.delete(self.getUuid(), VolumeVO.class); + } catch (RuntimeException e) { + logger.warn(String.format("failed to cleanup old libvirt secret for volume[uuid:%s] after encryption conversion: %s", + self.getUuid(), e.getMessage()), e); + } trigger.next(); } }); @@ -3450,9 +3463,14 @@ public void run(FlowTrigger trigger, Map data) { return; } - volumeEncryptedResourceKeyBackend.detachKeyProviderFromVolume(self.getUuid()); - for (VolumeSnapshotVO snapshot : conversionContext.get().snapshots) { - volumeEncryptedResourceKeyBackend.detachKeyProviderFromSnapshot(snapshot.getUuid()); + try { + volumeEncryptedResourceKeyBackend.detachKeyProviderFromVolume(self.getUuid()); + for (VolumeSnapshotVO snapshot : conversionContext.get().snapshots) { + volumeEncryptedResourceKeyBackend.detachKeyProviderFromSnapshot(snapshot.getUuid()); + } + } catch (RuntimeException e) { + logger.warn(String.format("failed to cleanup plain resource key refs for volume[uuid:%s] after encryption conversion: %s", + self.getUuid(), e.getMessage()), e); } trigger.next(); } @@ -3463,10 +3481,15 @@ public void run(FlowTrigger trigger, Map data) { @Override public void run(FlowTrigger trigger, Map data) { - VolumeEncryptionConversionContext ctx = conversionContext.get(); - trash.createTrash(TrashType.ConvertVolumeEncryption, false, ctx.oldVolumeInventory); - for (VolumeSnapshotInventory snapshot : ctx.oldSnapshotInventories) { - trash.createTrash(TrashType.ConvertVolumeSnapshotEncryption, false, snapshot); + try { + VolumeEncryptionConversionContext ctx = conversionContext.get(); + trash.createTrash(TrashType.ConvertVolumeEncryption, false, ctx.oldVolumeInventory); + for (VolumeSnapshotInventory snapshot : ctx.oldSnapshotInventories) { + trash.createTrash(TrashType.ConvertVolumeSnapshotEncryption, false, snapshot); + } + } catch (RuntimeException e) { + logger.warn(String.format("failed to record old volume bits in trash for volume[uuid:%s] after encryption conversion: %s", + self.getUuid(), e.getMessage()), e); } trigger.next(); } @@ -3485,7 +3508,7 @@ public void handle(ErrorCode errCode, Map data) { }).start(); } - private VolumeEncryptionConversionContext buildVolumeEncryptionConversionContext() { + private VolumeEncryptionConversionContext buildVolumeEncryptionConversionContext(boolean targetEncrypted) { VolumeEncryptionConversionContext ctx = new VolumeEncryptionConversionContext(); ctx.snapshots = Q.New(VolumeSnapshotVO.class) .eq(VolumeSnapshotVO_.volumeUuid, self.getUuid()) @@ -3494,20 +3517,13 @@ private VolumeEncryptionConversionContext buildVolumeEncryptionConversionContext .comparingInt(VolumeSnapshotVO::getDistance) .thenComparing(VolumeSnapshotVO::getUuid)); - ctx.oldAndNewInstallPaths = buildEncryptionConversionInstallPaths(ctx.snapshots); - ctx.oldAndTrashInstallPaths = buildEncryptionConversionSourceTrashInstallPaths(ctx.snapshots); + ctx.oldAndNewInstallPaths = buildEncryptionConversionInstallPaths(ctx.snapshots, targetEncrypted, Platform.getUuid()); ctx.oldVolumeInventory = VolumeInventory.valueOf(self); - ctx.oldVolumeInventory.setInstallPath(ctx.oldAndTrashInstallPaths.get(self.getInstallPath())); ctx.oldSnapshotInventories = ctx.snapshots.stream() .filter(snapshot -> StringUtils.isNotBlank(snapshot.getPrimaryStorageInstallPath())) - .filter(snapshot -> StringUtils.isNotBlank(ctx.oldAndTrashInstallPaths.get(snapshot.getPrimaryStorageInstallPath()))) - .map(snapshot -> { - VolumeSnapshotInventory inv = VolumeSnapshotInventory.valueOf(snapshot); - inv.setPrimaryStorageInstallPath(ctx.oldAndTrashInstallPaths.get(snapshot.getPrimaryStorageInstallPath())); - return inv; - }) + .map(VolumeSnapshotInventory::valueOf) .collect(Collectors.toList()); - ctx.items = buildEncryptionConversionItems(ctx.snapshots, ctx.oldAndNewInstallPaths, ctx.oldAndTrashInstallPaths); + ctx.items = buildEncryptionConversionItems(ctx.snapshots, ctx.oldAndNewInstallPaths); return ctx; } @@ -3573,37 +3589,25 @@ private boolean ensureVolumeKeyBindingIfNeeded(boolean targetEncrypted) { return true; } - private Map buildEncryptionConversionInstallPaths(List snapshots) { + private Map buildEncryptionConversionInstallPaths(List snapshots, + boolean targetEncrypted, + String conversionUuid) { Map paths = new LinkedHashMap<>(); for (VolumeSnapshotVO snapshot : snapshots) { if (StringUtils.isNotBlank(snapshot.getPrimaryStorageInstallPath())) { paths.put(snapshot.getPrimaryStorageInstallPath(), - makeConvertedSnapshotInstallPath(snapshot.getPrimaryStorageInstallPath(), snapshot.getUuid())); + makeConvertedSnapshotInstallPath(snapshot.getPrimaryStorageInstallPath(), snapshot.getUuid(), + targetEncrypted, conversionUuid)); } } - paths.put(self.getInstallPath(), makeConvertedVolumeInstallPath(self.getInstallPath(), self.getUuid())); - return paths; - } - - private Map buildEncryptionConversionSourceTrashInstallPaths(List snapshots) { - Map paths = new LinkedHashMap<>(); - Set trashPaths = new HashSet<>(); - for (VolumeSnapshotVO snapshot : snapshots) { - if (StringUtils.isNotBlank(snapshot.getPrimaryStorageInstallPath())) { - String sourceTrashPath = makeSourceSnapshotTrashInstallPath(snapshot.getPrimaryStorageInstallPath(), snapshot.getUuid(), - snapshot.isEncrypted(), trashPaths); - if (StringUtils.isNotBlank(sourceTrashPath)) { - paths.put(snapshot.getPrimaryStorageInstallPath(), sourceTrashPath); - } - } - } - paths.put(self.getInstallPath(), makeSourceTrashInstallPath(self.getInstallPath(), self.getUuid(), self.isEncrypted(), trashPaths)); + paths.put(self.getInstallPath(), makeConvertedVolumeInstallPath(self.getInstallPath(), self.getUuid(), + targetEncrypted, conversionUuid)); + validateEncryptionConversionTargetPaths(paths); return paths; } private List buildEncryptionConversionItems( - List snapshots, Map oldAndNewInstallPaths, - Map oldAndTrashInstallPaths) { + List snapshots, Map oldAndNewInstallPaths) { Map snapshotMap = snapshots.stream() .collect(Collectors.toMap(VolumeSnapshotVO::getUuid, it -> it)); List items = new ArrayList<>(); @@ -3618,7 +3622,7 @@ private List oldAndNewInstallPaths) { + Set targets = new HashSet<>(); + for (Map.Entry entry : oldAndNewInstallPaths.entrySet()) { + if (StringUtils.isBlank(entry.getValue())) { + throw new OperationFailureException(operr("failed to build converted install path for source path[%s]", + entry.getKey())); + } + if (entry.getKey().equals(entry.getValue())) { + throw new OperationFailureException(operr("converted install path must differ from source path[%s]", + entry.getKey())); + } + if (!targets.add(entry.getValue())) { + throw new OperationFailureException(operr("duplicate converted install path[%s]", entry.getValue())); + } } - return makeSiblingInstallPath(installPath, String.format("%s.qcow2", resourceUuid)); } private String makeSiblingInstallPath(String installPath, String fileName) { @@ -3701,27 +3722,6 @@ private String makeCephVolumeInstallPath(String installPath, String resourceUuid return String.format("%s%s/%s", prefix, path.substring(0, poolEnd), resourceUuid); } - private String makeSourceSnapshotTrashInstallPath(String installPath, String resourceUuid, boolean sourceEncrypted, Set trashPaths) { - if (isCephInstallPath(installPath)) { - return null; - } - return makeSourceTrashInstallPath(installPath, resourceUuid, sourceEncrypted, trashPaths); - } - - private String makeSourceTrashInstallPath(String installPath, String resourceUuid, boolean sourceEncrypted, Set trashPaths) { - String trashPath; - do { - String fileName = String.format("%s.trash.%s.%s.qcow2", - resourceUuid, sourceEncrypted ? "encrypted" : "plain", Platform.getUuid().substring(0, 6)); - if (isCephInstallPath(installPath)) { - trashPath = makeCephVolumeInstallPath(installPath, fileName); - } else { - trashPath = makeSiblingInstallPath(installPath, fileName); - } - } while (!trashPaths.add(trashPath)); - return trashPath; - } - private boolean isCephInstallPath(String installPath) { return installPath != null && installPath.startsWith("ceph://"); } @@ -3730,53 +3730,56 @@ private boolean isSharedBlockInstallPath(String installPath) { return installPath != null && installPath.startsWith("sharedblock://"); } - private void deleteConvertedVolumeEncryptionBits(List items) { - Set sourceInstallPaths = items.stream() - .map(ConvertVolumeEncryptionOnPrimaryStorageMsg.VolumeEncryptionConversionItem::getSourceInstallPath) - .collect(Collectors.toSet()); - for (ConvertVolumeEncryptionOnPrimaryStorageMsg.VolumeEncryptionConversionItem item : items) { - if (sourceInstallPaths.contains(item.getTargetInstallPath())) { - continue; + private void rollbackVolumeEncryptionOnPrimaryStorage(List items, + FlowRollback trigger) { + RollbackVolumeEncryptionOnPrimaryStorageMsg rmsg = new RollbackVolumeEncryptionOnPrimaryStorageMsg(); + rmsg.setVolume(VolumeInventory.valueOf(self)); + rmsg.setItems(items); + bus.makeTargetServiceIdByResourceUuid(rmsg, PrimaryStorageConstant.SERVICE_ID, self.getPrimaryStorageUuid()); + bus.send(rmsg, new CloudBusCallBack(trigger) { + @Override + public void run(MessageReply reply) { + if (!reply.isSuccess()) { + logger.warn(String.format("failed to rollback volume[uuid:%s] encryption conversion on primary storage[uuid:%s]: %s", + self.getUuid(), self.getPrimaryStorageUuid(), reply.getError().getReadableDetails())); + } + trigger.rollback(); } - DeleteVolumeBitsOnPrimaryStorageMsg dmsg = new DeleteVolumeBitsOnPrimaryStorageMsg(); - dmsg.setPrimaryStorageUuid(self.getPrimaryStorageUuid()); - dmsg.setInstallPath(item.getTargetInstallPath()); - dmsg.setBitsUuid(item.getResourceUuid()); - dmsg.setBitsType(item.getResourceType()); - dmsg.setHypervisorType(VolumeFormat.getMasterHypervisorTypeByVolumeFormat(self.getFormat()).toString()); - bus.makeTargetServiceIdByResourceUuid(dmsg, PrimaryStorageConstant.SERVICE_ID, self.getPrimaryStorageUuid()); - bus.send(dmsg); - } + }); } - @Transactional private void updateVolumeEncryptionConversionInDb(boolean targetEncrypted, List snapshots, Map oldAndNewInstallPaths, Map actualSizes) { - VolumeSnapshotReferenceUtils.handleVolumeInstallUrlChange(self.getUuid(), oldAndNewInstallPaths); - - UpdateQuery q = SQL.New(VolumeVO.class) - .eq(VolumeVO_.uuid, self.getUuid()) - .set(VolumeVO_.installPath, oldAndNewInstallPaths.get(self.getInstallPath())) - .set(VolumeVO_.encrypted, targetEncrypted); - if (actualSizes != null && actualSizes.get(self.getUuid()) != null) { - q.set(VolumeVO_.actualSize, actualSizes.get(self.getUuid())); - } - q.update(); + new SQLBatch() { + @Override + protected void scripts() { + VolumeSnapshotReferenceUtils.handleVolumeInstallUrlChange(self.getUuid(), oldAndNewInstallPaths); + + UpdateQuery q = SQL.New(VolumeVO.class) + .eq(VolumeVO_.uuid, self.getUuid()) + .set(VolumeVO_.installPath, oldAndNewInstallPaths.get(self.getInstallPath())) + .set(VolumeVO_.encrypted, targetEncrypted); + if (actualSizes != null && actualSizes.get(self.getUuid()) != null) { + q.set(VolumeVO_.actualSize, actualSizes.get(self.getUuid())); + } + q.update(); - Map convertedSnapshotInstallPaths = new LinkedHashMap<>(); - for (VolumeSnapshotVO snapshot : snapshots) { - String newPath = oldAndNewInstallPaths.get(snapshot.getPrimaryStorageInstallPath()); - if (StringUtils.isBlank(newPath)) { - continue; - } - convertedSnapshotInstallPaths.put(snapshot.getUuid(), newPath); - } + Map convertedSnapshotInstallPaths = new LinkedHashMap<>(); + for (VolumeSnapshotVO snapshot : snapshots) { + String newPath = oldAndNewInstallPaths.get(snapshot.getPrimaryStorageInstallPath()); + if (StringUtils.isBlank(newPath)) { + continue; + } + convertedSnapshotInstallPaths.put(snapshot.getUuid(), newPath); + } - updateConvertedSnapshotPaths(convertedSnapshotInstallPaths, targetEncrypted); - if (targetEncrypted && !convertedSnapshotInstallPaths.isEmpty()) { - volumeEncryptedResourceKeyBackend.copyVolumeKeyRefToSnapshots(self.getUuid(), convertedSnapshotInstallPaths.keySet()); - } + updateConvertedSnapshotPaths(convertedSnapshotInstallPaths, targetEncrypted); + if (targetEncrypted && !convertedSnapshotInstallPaths.isEmpty()) { + volumeEncryptedResourceKeyBackend.copyVolumeKeyRefToSnapshots(self.getUuid(), convertedSnapshotInstallPaths.keySet()); + } + } + }.execute(); } private void updateConvertedSnapshotPaths(Map snapshotInstallPaths, boolean targetEncrypted) { diff --git a/testlib/src/main/java/org/zstack/testlib/CephPrimaryStorageSpec.groovy b/testlib/src/main/java/org/zstack/testlib/CephPrimaryStorageSpec.groovy index 5e711d6819d..3534ad26914 100755 --- a/testlib/src/main/java/org/zstack/testlib/CephPrimaryStorageSpec.groovy +++ b/testlib/src/main/java/org/zstack/testlib/CephPrimaryStorageSpec.groovy @@ -262,47 +262,6 @@ class CephPrimaryStorageSpec extends PrimaryStorageSpec { return rsp } - simulator(CephPrimaryStorageBase.LUKS_REPLACE_VOLUME_PATH) { HttpEntity e, EnvSpec spec -> - return new CephPrimaryStorageBase.ReplaceLuksRbdRsp() - } - - VFS.vfsHook(CephPrimaryStorageBase.LUKS_REPLACE_VOLUME_PATH, espec) { CephPrimaryStorageBase.ReplaceLuksRbdRsp rsp, HttpEntity e, EnvSpec spec -> - def cmd = JSONObjectUtil.toObject(e.body, CephPrimaryStorageBase.ReplaceLuksRbdCmd.class) - VFS vfs = vfs(cmd, spec) - String srcPath = cephPathToVFSPath(cmd.installPath) - String tmpPath = cephPathToVFSPath(cmd.temporaryInstallPath) - String targetPath = cephPathToVFSPath(cmd.targetInstallPath ?: cmd.installPath) - boolean deleteSourceTrash = cmd.deleteSourceTrash || !cmd.sourceTrashInstallPath - String trashPath = cephPathToVFSPath(cmd.sourceTrashInstallPath ?: "${srcPath}-trash-${Platform.uuid.substring(0, 8)}") - - [srcPath, tmpPath, targetPath, trashPath].each { - vfs.Assert(!it.contains("@"), "RBD LUKS replacement only supports active image paths[${it}]") - } - vfs.Assert(vfs.isFile(srcPath), "cannot find the source file[${cmd.installPath}]") - CephRaw src = vfs.getFile(srcPath, true) - if (!vfs.exists(tmpPath)) { - CephRaw tmp = vfs.createCephRaw(tmpPath, src.virtualSize) - tmp.actualSize = src.actualSize - tmp.update() - } - vfs.Assert(vfs.isFile(tmpPath), "cannot find the temporary file[${cmd.temporaryInstallPath}]") - vfs.Assert(!vfs.exists(trashPath), "trash file[${trashPath}] already exists") - if (targetPath != srcPath) { - vfs.Assert(!vfs.exists(targetPath), "target file[${targetPath}] already exists") - } - - src.move(trashPath) - CephRaw tmp = vfs.getFile(tmpPath, true) - CephRaw target = tmp.move(targetPath) - if (deleteSourceTrash && vfs.exists(trashPath)) { - vfs.delete(trashPath) - } - - rsp.size = target.virtualSize - rsp.actualSize = target.actualSize - return rsp - } - simulator(CephPrimaryStorageBase.LUKS_SWAP_IN_PLACE_PATH) { HttpEntity e, EnvSpec spec -> return new CephPrimaryStorageBase.AgentResponse() } @@ -664,15 +623,13 @@ class CephPrimaryStorageSpec extends PrimaryStorageSpec { } simulator(CephPrimaryStorageBase.CHECK_BITS_PATH) { HttpEntity e, EnvSpec spec -> - CephPrimaryStorageBase.CheckIsBitsExistingRsp rsp = new CephPrimaryStorageBase.CheckIsBitsExistingRsp() - rsp.setExisting(true) - return rsp + return new CephPrimaryStorageBase.CheckIsBitsExistingRsp() } VFS.vfsHook(CephPrimaryStorageBase.CHECK_BITS_PATH, espec) { rsp, HttpEntity e, EnvSpec spec -> def cmd = JSONObjectUtil.toObject(e.body, CephPrimaryStorageBase.CheckIsBitsExistingCmd.class) VFS vfs = vfs(cmd, spec) - vfs.Assert(vfs.isFile(cephPathToVFSPath(cmd.installPath)), "cannot find ${cmd.installPath}") + rsp.existing = vfs.exists(cephPathToVFSPath(cmd.installPath)) return rsp } diff --git a/testlib/src/main/java/org/zstack/testlib/LocalStorageSpec.groovy b/testlib/src/main/java/org/zstack/testlib/LocalStorageSpec.groovy index 3530492f9bb..a0accf71676 100755 --- a/testlib/src/main/java/org/zstack/testlib/LocalStorageSpec.groovy +++ b/testlib/src/main/java/org/zstack/testlib/LocalStorageSpec.groovy @@ -303,6 +303,20 @@ class LocalStorageSpec extends PrimaryStorageSpec { return new LocalStorageKvmBackend.ConvertVolumeEncryptionRsp() } + VFS.vfsHook(LocalStorageKvmBackend.CONVERT_VOLUME_ENCRYPTION_PATH, espec) { rsp, HttpEntity e, EnvSpec spec -> + def cmd = JSONObjectUtil.toObject(e.body, LocalStorageKvmBackend.ConvertVolumeEncryptionCmd.class) + VFS vfs = vfs(e, cmd, spec) + rsp.actualSizes = [:] + cmd.items.each { item -> + Qcow2 source = vfs.getFile(item.sourceInstallPath) + assert source != null : "cannot find source file[${item.sourceInstallPath}]" + Qcow2 target = vfs.createQcow2(item.targetInstallPath, source.actualSize, + source.virtualSize, item.targetBackingInstallPath) + rsp.actualSizes[item.resourceUuid] = target.actualSize + } + return rsp + } + VFS.vfsHook(LocalStorageKvmBackend.CREATE_VOLUME_WITH_BACKING_PATH, espec) { rsp, HttpEntity e, EnvSpec spec -> def cmd = JSONObjectUtil.toObject(e.body, LocalStorageKvmBackend.CreateVolumeWithBackingCmd.class) VFS vfs = vfs(e, cmd, spec) diff --git a/testlib/src/main/java/org/zstack/testlib/NfsPrimaryStorageSpec.groovy b/testlib/src/main/java/org/zstack/testlib/NfsPrimaryStorageSpec.groovy index 32ea00411d1..6fbca40156c 100755 --- a/testlib/src/main/java/org/zstack/testlib/NfsPrimaryStorageSpec.groovy +++ b/testlib/src/main/java/org/zstack/testlib/NfsPrimaryStorageSpec.groovy @@ -161,6 +161,20 @@ class NfsPrimaryStorageSpec extends PrimaryStorageSpec { return new NfsPrimaryStorageKVMBackendCommands.ConvertVolumeEncryptionRsp() } + VFS.vfsHook(NfsPrimaryStorageKVMBackend.CONVERT_VOLUME_ENCRYPTION_PATH, xspec) { rsp, HttpEntity e, EnvSpec spec -> + def cmd = JSONObjectUtil.toObject(e.body, NfsPrimaryStorageKVMBackendCommands.ConvertVolumeEncryptionCmd.class) + VFS vfs = vfs(cmd, spec) + rsp.actualSizes = [:] + cmd.items.each { item -> + Qcow2 source = vfs.getFile(item.sourceInstallPath) + assert source != null : "cannot find source file[${item.sourceInstallPath}]" + Qcow2 target = vfs.createQcow2(item.targetInstallPath, source.actualSize, + source.virtualSize, item.targetBackingInstallPath) + rsp.actualSizes[item.resourceUuid] = target.actualSize + } + return rsp + } + VFS.vfsHook(NfsPrimaryStorageKVMBackend.CREATE_EMPTY_VOLUME_PATH, xspec) { rsp, HttpEntity e, EnvSpec spec -> def cmd = JSONObjectUtil.toObject(e.body, NfsPrimaryStorageKVMBackendCommands.CreateEmptyVolumeCmd.class) VFS vfs = vfs(cmd, spec)