diff --git a/header/src/main/java/org/zstack/header/storage/addon/primary/ExternalPrimaryStorageInventory.java b/header/src/main/java/org/zstack/header/storage/addon/primary/ExternalPrimaryStorageInventory.java index a15ed211307..b170c0529d5 100644 --- a/header/src/main/java/org/zstack/header/storage/addon/primary/ExternalPrimaryStorageInventory.java +++ b/header/src/main/java/org/zstack/header/storage/addon/primary/ExternalPrimaryStorageInventory.java @@ -100,6 +100,7 @@ private static String desensitizeUrl(String url) { return url; } + @Override public String getIdentity() { return identity; } diff --git a/header/src/main/java/org/zstack/header/storage/addon/primary/PrimaryStorageControllerSvc.java b/header/src/main/java/org/zstack/header/storage/addon/primary/PrimaryStorageControllerSvc.java index feb4c5c11c7..e57f490ea91 100644 --- a/header/src/main/java/org/zstack/header/storage/addon/primary/PrimaryStorageControllerSvc.java +++ b/header/src/main/java/org/zstack/header/storage/addon/primary/PrimaryStorageControllerSvc.java @@ -5,6 +5,7 @@ import org.zstack.header.core.ReturnValueCompletion; import org.zstack.header.host.HostInventory; import org.zstack.header.storage.addon.*; +import org.zstack.header.storage.primary.StorageResourceStats; import org.zstack.header.storage.snapshot.VolumeSnapshotStats; import org.zstack.header.volume.VolumeProtocol; import org.zstack.header.volume.VolumeStats; @@ -64,7 +65,7 @@ public interface PrimaryStorageControllerSvc { // support uri or path void stats(String installPath, ReturnValueCompletion comp); - void batchStats(Collection installPath, ReturnValueCompletion> comp); + void batchStats(Collection installPaths, ReturnValueCompletion> comp); void expandVolume(String installPath, long size, ReturnValueCompletion comp); void setVolumeQos(BaseVolumeInfo v, Completion comp); diff --git a/header/src/main/java/org/zstack/header/storage/primary/AskVolumeSnapshotCapabilityMsg.java b/header/src/main/java/org/zstack/header/storage/primary/AskVolumeSnapshotCapabilityMsg.java index eef6a6cca8c..fd8d221eb13 100755 --- a/header/src/main/java/org/zstack/header/storage/primary/AskVolumeSnapshotCapabilityMsg.java +++ b/header/src/main/java/org/zstack/header/storage/primary/AskVolumeSnapshotCapabilityMsg.java @@ -9,6 +9,7 @@ public class AskVolumeSnapshotCapabilityMsg extends NeedReplyMessage implements PrimaryStorageMessage { private String primaryStorageUuid; private VolumeInventory volume; + private String volumeType; @Override public String getPrimaryStorageUuid() { @@ -26,4 +27,12 @@ public VolumeInventory getVolume() { public void setVolume(VolumeInventory volume) { this.volume = volume; } + + public String getVolumeType() { + return volumeType != null ? volumeType : volume == null ? null : volume.getType(); + } + + public void setVolumeType(String volumeType) { + this.volumeType = volumeType; + } } diff --git a/header/src/main/java/org/zstack/header/storage/primary/PrimaryStorageInventory.java b/header/src/main/java/org/zstack/header/storage/primary/PrimaryStorageInventory.java index 643c8709e68..44d153bc717 100755 --- a/header/src/main/java/org/zstack/header/storage/primary/PrimaryStorageInventory.java +++ b/header/src/main/java/org/zstack/header/storage/primary/PrimaryStorageInventory.java @@ -290,6 +290,10 @@ public String getType() { return type; } + public String getIdentity() { + return type; + } + public void setType(String type) { this.type = type; } diff --git a/header/src/main/java/org/zstack/header/storage/primary/StorageResourceStats.java b/header/src/main/java/org/zstack/header/storage/primary/StorageResourceStats.java new file mode 100644 index 00000000000..2d90b5ac9ad --- /dev/null +++ b/header/src/main/java/org/zstack/header/storage/primary/StorageResourceStats.java @@ -0,0 +1,31 @@ +package org.zstack.header.storage.primary; + +public class StorageResourceStats { + protected String installPath; + protected Long actualSize; + protected Long size; + + public String getInstallPath() { + return installPath; + } + + public void setInstallPath(String installPath) { + this.installPath = installPath; + } + + public Long getActualSize() { + return actualSize; + } + + public void setActualSize(Long actualSize) { + this.actualSize = actualSize; + } + + public Long getSize() { + return size; + } + + public void setSize(long size) { + this.size = size; + } +} diff --git a/header/src/main/java/org/zstack/header/storage/primary/VolumeSnapshotCapability.java b/header/src/main/java/org/zstack/header/storage/primary/VolumeSnapshotCapability.java index 79050146db5..08d9c6a073d 100755 --- a/header/src/main/java/org/zstack/header/storage/primary/VolumeSnapshotCapability.java +++ b/header/src/main/java/org/zstack/header/storage/primary/VolumeSnapshotCapability.java @@ -16,6 +16,11 @@ public static enum VolumeSnapshotPlacementType { EXTERNAL, } + public static enum VolumeSnapshotMode { + REDIRECT_ON_WRITE, + COPY_ON_WRITE, + } + private boolean support; /*** @@ -34,6 +39,8 @@ public static enum VolumeSnapshotPlacementType { private VolumeSnapshotArrangementType arrangementType; private VolumeSnapshotPlacementType placementType; + + private VolumeSnapshotMode mode; /*** * If volume snapshot is inner snapshot on volume, it must be set. @@ -66,6 +73,14 @@ public void setPlacementType(VolumeSnapshotPlacementType placementType) { this.placementType = placementType; } + public VolumeSnapshotMode getMode() { + return mode; + } + + public void setMode(VolumeSnapshotMode mode) { + this.mode = mode; + } + public boolean isSupportCreateOnHypervisor() { return supportCreateOnHypervisor; } diff --git a/header/src/main/java/org/zstack/header/storage/snapshot/VolumeSnapshotStats.java b/header/src/main/java/org/zstack/header/storage/snapshot/VolumeSnapshotStats.java index 8b4e3778ef3..4e6e4b860d9 100644 --- a/header/src/main/java/org/zstack/header/storage/snapshot/VolumeSnapshotStats.java +++ b/header/src/main/java/org/zstack/header/storage/snapshot/VolumeSnapshotStats.java @@ -1,22 +1,6 @@ package org.zstack.header.storage.snapshot; -public class VolumeSnapshotStats { - private String installPath; - private long actualSize; +import org.zstack.header.storage.primary.StorageResourceStats; - public String getInstallPath() { - return installPath; - } - - public void setInstallPath(String installPath) { - this.installPath = installPath; - } - - public long getActualSize() { - return actualSize; - } - - public void setActualSize(long actualSize) { - this.actualSize = actualSize; - } +public class VolumeSnapshotStats extends StorageResourceStats { } diff --git a/header/src/main/java/org/zstack/header/volume/BatchSyncVolumeSizeOnPrimaryStorageMsg.java b/header/src/main/java/org/zstack/header/volume/BatchSyncVolumeResourceSizeOnPrimaryStorageMsg.java similarity index 58% rename from header/src/main/java/org/zstack/header/volume/BatchSyncVolumeSizeOnPrimaryStorageMsg.java rename to header/src/main/java/org/zstack/header/volume/BatchSyncVolumeResourceSizeOnPrimaryStorageMsg.java index 4ab12a3b2a8..a21435a170c 100644 --- a/header/src/main/java/org/zstack/header/volume/BatchSyncVolumeSizeOnPrimaryStorageMsg.java +++ b/header/src/main/java/org/zstack/header/volume/BatchSyncVolumeResourceSizeOnPrimaryStorageMsg.java @@ -5,13 +5,17 @@ import java.util.Map; -public class BatchSyncVolumeSizeOnPrimaryStorageMsg extends NeedReplyMessage implements PrimaryStorageMessage { +public class BatchSyncVolumeResourceSizeOnPrimaryStorageMsg extends NeedReplyMessage implements PrimaryStorageMessage { private String hostUuid; private String primaryStorageUuid; private Map volumeUuidInstallPaths; + private Map snapshotUuidInstallPaths; + + private boolean withSnapshot; + public void setHostUuid(String hostUuid) { this.hostUuid = hostUuid; } @@ -35,4 +39,20 @@ public void setVolumeUuidInstallPaths(Map volumeUuidInstallPaths public Map getVolumeUuidInstallPaths() { return volumeUuidInstallPaths; } + + public Map getSnapshotUuidInstallPaths() { + return snapshotUuidInstallPaths; + } + + public void setSnapshotUuidInstallPaths(Map snapshotUuidInstallPaths) { + this.snapshotUuidInstallPaths = snapshotUuidInstallPaths; + } + + public boolean isWithSnapshot() { + return withSnapshot; + } + + public void setWithSnapshot(boolean withSnapshot) { + this.withSnapshot = withSnapshot; + } } diff --git a/header/src/main/java/org/zstack/header/volume/BatchSyncVolumeResourceSizeOnPrimaryStorageReply.java b/header/src/main/java/org/zstack/header/volume/BatchSyncVolumeResourceSizeOnPrimaryStorageReply.java new file mode 100644 index 00000000000..5523012db86 --- /dev/null +++ b/header/src/main/java/org/zstack/header/volume/BatchSyncVolumeResourceSizeOnPrimaryStorageReply.java @@ -0,0 +1,28 @@ +package org.zstack.header.volume; + +import org.zstack.header.message.MessageReply; + +import java.util.HashMap; +import java.util.Map; + +public class BatchSyncVolumeResourceSizeOnPrimaryStorageReply extends MessageReply { + private Map actualSizes = new HashMap<>(); + + private Map snapshotActualSizes = new HashMap<>(); + + public void setActualSizes(Map actualSizes) { + this.actualSizes = actualSizes; + } + + public Map getActualSizes() { + return actualSizes; + } + + public Map getSnapshotActualSizes() { + return snapshotActualSizes; + } + + public void setSnapshotActualSizes(Map snapshotActualSizes) { + this.snapshotActualSizes = snapshotActualSizes; + } +} diff --git a/header/src/main/java/org/zstack/header/volume/BatchSyncVolumeSizeOnPrimaryStorageReply.java b/header/src/main/java/org/zstack/header/volume/BatchSyncVolumeSizeOnPrimaryStorageReply.java deleted file mode 100644 index ee543ba398a..00000000000 --- a/header/src/main/java/org/zstack/header/volume/BatchSyncVolumeSizeOnPrimaryStorageReply.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.zstack.header.volume; - -import org.zstack.header.message.MessageReply; - -import java.util.HashMap; -import java.util.Map; - -public class BatchSyncVolumeSizeOnPrimaryStorageReply extends MessageReply { - private Map actualSizes = new HashMap<>(); - - public void setActualSizes(Map actualSizes) { - this.actualSizes = actualSizes; - } - - public Map getActualSizes() { - return actualSizes; - } -} diff --git a/header/src/main/java/org/zstack/header/volume/VolumeStats.java b/header/src/main/java/org/zstack/header/volume/VolumeStats.java index 2931945e7ed..4cedca7541a 100644 --- a/header/src/main/java/org/zstack/header/volume/VolumeStats.java +++ b/header/src/main/java/org/zstack/header/volume/VolumeStats.java @@ -1,10 +1,9 @@ package org.zstack.header.volume; -public class VolumeStats { - protected String installPath; +import org.zstack.header.storage.primary.StorageResourceStats; + +public class VolumeStats extends StorageResourceStats { protected String format; - protected Long actualSize; - protected Long size; /** * The parent uri of the volume, vendor://pool/path@snapshot or snapshot://uuid */ @@ -29,30 +28,6 @@ public VolumeStats(String installPath, Long actualSize, Long size) { public VolumeStats() { } - public String getInstallPath() { - return installPath; - } - - public void setInstallPath(String installPath) { - this.installPath = installPath; - } - - public Long getActualSize() { - return actualSize; - } - - public void setActualSize(Long actualSize) { - this.actualSize = actualSize; - } - - public Long getSize() { - return size; - } - - public void setSize(long size) { - this.size = size; - } - public void setFormat(String format) { this.format = format; } 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 53816200465..b6c015d0166 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 @@ -3325,11 +3325,12 @@ protected void handle(AskVolumeSnapshotCapabilityMsg msg) { AskVolumeSnapshotCapabilityReply reply = new AskVolumeSnapshotCapabilityReply(); VolumeSnapshotCapability cap = new VolumeSnapshotCapability(); - String volumeType = msg.getVolume().getType(); + String volumeType = msg.getVolumeType(); if (VolumeType.Data.toString().equals(volumeType) || VolumeType.Root.toString().equals(volumeType)) { cap.setSupport(true); cap.setArrangementType(VolumeSnapshotArrangementType.INDIVIDUAL); cap.setPlacementType(VolumeSnapshotCapability.VolumeSnapshotPlacementType.INTERNAL); + cap.setMode(VolumeSnapshotCapability.VolumeSnapshotMode.REDIRECT_ON_WRITE); cap.setVolumePathFromInternalSnapshotRegex("^[^@]+"); } else if (VolumeType.Memory.toString().equals(volumeType)) { cap.setSupport(false); @@ -3397,10 +3398,10 @@ public void fail(ErrorCode errorCode) { } @Override - protected void handle(BatchSyncVolumeSizeOnPrimaryStorageMsg msg) { + protected void handle(BatchSyncVolumeResourceSizeOnPrimaryStorageMsg msg) { inQueue().name(String.format("batch-sync-volume-size-on-primarystorage-%s", self.getUuid())) .asyncBackup(msg) - .run(chain -> BatchSyncVolumeSizeOnPrimaryStorage(msg, new NoErrorCompletion(chain) { + .run(chain -> BatchSyncVolumeResourceSizeOnPrimaryStorage(msg, new NoErrorCompletion(chain) { @Override public void done() { chain.next(); @@ -3419,8 +3420,8 @@ protected void syncVolumeSize(String volumeUuid, String installPath, final Retur httpCall(GET_VOLUME_SIZE_PATH, cmd, GetVolumeSizeRsp.class, completion); } - private void BatchSyncVolumeSizeOnPrimaryStorage(BatchSyncVolumeSizeOnPrimaryStorageMsg msg, NoErrorCompletion completion) { - BatchSyncVolumeSizeOnPrimaryStorageReply reply = new BatchSyncVolumeSizeOnPrimaryStorageReply(); + private void BatchSyncVolumeResourceSizeOnPrimaryStorage(BatchSyncVolumeResourceSizeOnPrimaryStorageMsg msg, NoErrorCompletion completion) { + BatchSyncVolumeResourceSizeOnPrimaryStorageReply reply = new BatchSyncVolumeResourceSizeOnPrimaryStorageReply(); GetBatchVolumeSizeCmd cmd = new GetBatchVolumeSizeCmd(); cmd.volumeUuidInstallPaths = msg.getVolumeUuidInstallPaths(); diff --git a/plugin/expon/src/main/java/org/zstack/expon/ExponStorageController.java b/plugin/expon/src/main/java/org/zstack/expon/ExponStorageController.java index 2f24bc3feaf..a78d4e1c8aa 100644 --- a/plugin/expon/src/main/java/org/zstack/expon/ExponStorageController.java +++ b/plugin/expon/src/main/java/org/zstack/expon/ExponStorageController.java @@ -36,6 +36,7 @@ import org.zstack.header.storage.addon.*; import org.zstack.header.storage.addon.primary.*; import org.zstack.header.storage.primary.ImageCacheInventory; +import org.zstack.header.storage.primary.StorageResourceStats; import org.zstack.header.storage.primary.VolumeSnapshotCapability; import org.zstack.header.storage.snapshot.VolumeSnapshotStats; import org.zstack.header.volume.*; @@ -93,6 +94,7 @@ public class ExponStorageController implements PrimaryStorageControllerSvc, Prim scap.setSupport(true); scap.setArrangementType(VolumeSnapshotCapability.VolumeSnapshotArrangementType.INDIVIDUAL); scap.setPlacementType(VolumeSnapshotCapability.VolumeSnapshotPlacementType.INTERNAL); + scap.setMode(VolumeSnapshotCapability.VolumeSnapshotMode.REDIRECT_ON_WRITE); scap.setSupportCreateOnHypervisor(false); scap.setSupportLazyDelete(true); scap.setVolumePathFromInternalSnapshotRegex("^[^@]+"); @@ -1158,15 +1160,15 @@ public void stats(String installPath, ReturnValueCompletion comp) { } @Override - public void batchStats(Collection installPath, ReturnValueCompletion> comp) { - List stats = installPath.stream().map(it -> { + public void batchStats(Collection installPaths, ReturnValueCompletion> comp) { + List stats = installPaths.stream().map(it -> { VolumeModule vol = apiHelper.getVolume(getVolIdFromPath(it)); VolumeStats s = new VolumeStats(); s.setInstallPath(it); s.setSize(vol.getVolumeSize()); s.setActualSize(vol.getDataSize()); s.setFormat(VolumeConstant.VOLUME_FORMAT_RAW); - return s; + return (StorageResourceStats) s; }).collect(Collectors.toList()); comp.success(stats); } 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 0f57a471769..904bfc15b01 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 @@ -2677,10 +2677,11 @@ protected void handle(AskVolumeSnapshotCapabilityMsg msg) { VolumeSnapshotCapability capability = new VolumeSnapshotCapability(); capability.setSupport(true); - String volumeType = msg.getVolume().getType(); + String volumeType = msg.getVolumeType(); if (VolumeType.Data.toString().equals(volumeType) || VolumeType.Root.toString().equals(volumeType)) { capability.setArrangementType(VolumeSnapshotArrangementType.CHAIN); capability.setPlacementType(VolumeSnapshotCapability.VolumeSnapshotPlacementType.EXTERNAL); + capability.setMode(VolumeSnapshotCapability.VolumeSnapshotMode.REDIRECT_ON_WRITE); } else if (VolumeType.Memory.toString().equals(volumeType)) { capability.setArrangementType(VolumeSnapshotArrangementType.INDIVIDUAL); } else { @@ -2733,17 +2734,17 @@ public void fail(ErrorCode errorCode) { } @Override - protected void handle(BatchSyncVolumeSizeOnPrimaryStorageMsg msg) { + protected void handle(BatchSyncVolumeResourceSizeOnPrimaryStorageMsg msg) { LocalStorageHypervisorFactory f = getHypervisorBackendFactoryByHostUuid(msg.getHostUuid()); LocalStorageHypervisorBackend bkd = f.getHypervisorBackend(self); - bkd.handle(msg, msg.getHostUuid(), new ReturnValueCompletion(msg) { + bkd.handle(msg, msg.getHostUuid(), new ReturnValueCompletion(msg) { @Override - public void success(BatchSyncVolumeSizeOnPrimaryStorageReply returnValue) { + public void success(BatchSyncVolumeResourceSizeOnPrimaryStorageReply returnValue) { bus.reply(msg, returnValue); } @Override public void fail(ErrorCode errorCode) { - BatchSyncVolumeSizeOnPrimaryStorageReply reply = new BatchSyncVolumeSizeOnPrimaryStorageReply(); + BatchSyncVolumeResourceSizeOnPrimaryStorageReply reply = new BatchSyncVolumeResourceSizeOnPrimaryStorageReply(); reply.setError(errorCode); bus.reply(msg, reply); } 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 7760e28de93..b502409aac2 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 @@ -72,7 +72,7 @@ public LocalStorageHypervisorBackend(PrimaryStorageVO self) { abstract void handle(EstimateVolumeTemplateSizeOnPrimaryStorageMsg msg, String huuid, ReturnValueCompletion completion); - abstract void handle(BatchSyncVolumeSizeOnPrimaryStorageMsg msg, String hostUuid, ReturnValueCompletion completion); + abstract void handle(BatchSyncVolumeResourceSizeOnPrimaryStorageMsg msg, String hostUuid, ReturnValueCompletion completion); abstract void handle(CreateImageCacheFromVolumeOnPrimaryStorageMsg msg, 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 ec0b51711ba..aa163ef141f 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 @@ -2495,8 +2495,8 @@ public void fail(ErrorCode errorCode) { } @Override - void handle(BatchSyncVolumeSizeOnPrimaryStorageMsg msg, String hostUuid, ReturnValueCompletion completion) { - final BatchSyncVolumeSizeOnPrimaryStorageReply reply = new BatchSyncVolumeSizeOnPrimaryStorageReply(); + void handle(BatchSyncVolumeResourceSizeOnPrimaryStorageMsg msg, String hostUuid, ReturnValueCompletion completion) { + final BatchSyncVolumeResourceSizeOnPrimaryStorageReply reply = new BatchSyncVolumeResourceSizeOnPrimaryStorageReply(); GetBatchVolumeSizeCmd cmd = new GetBatchVolumeSizeCmd(); cmd.volumeUuidInstallPaths = msg.getVolumeUuidInstallPaths(); 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 253d2c00d92..7e2f46e64e7 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 @@ -1339,21 +1339,17 @@ protected void handle(DeleteIsoFromPrimaryStorageMsg msg) { @Override protected void handle(AskVolumeSnapshotCapabilityMsg msg) { VolumeSnapshotCapability capability = new VolumeSnapshotCapability(); - HypervisorType hvType = VolumeFormat.getMasterHypervisorTypeByVolumeFormat(msg.getVolume().getFormat()); - if (hvType.toString().equals(KVMConstant.KVM_HYPERVISOR_TYPE)) { - String volumeType = msg.getVolume().getType(); - if (VolumeType.Data.toString().equals(volumeType) || VolumeType.Root.toString().equals(volumeType)) { - capability.setArrangementType(VolumeSnapshotArrangementType.CHAIN); - capability.setPlacementType(VolumeSnapshotCapability.VolumeSnapshotPlacementType.EXTERNAL); - } else if (VolumeType.Memory.toString().equals(volumeType)) { - capability.setArrangementType(VolumeSnapshotArrangementType.INDIVIDUAL); - } else { - throw new CloudRuntimeException(String.format("unknown volume type %s", volumeType)); - } - capability.setSupport(true); + String volumeType = msg.getVolumeType(); + if (VolumeType.Data.toString().equals(volumeType) || VolumeType.Root.toString().equals(volumeType)) { + capability.setArrangementType(VolumeSnapshotArrangementType.CHAIN); + capability.setPlacementType(VolumeSnapshotCapability.VolumeSnapshotPlacementType.EXTERNAL); + capability.setMode(VolumeSnapshotCapability.VolumeSnapshotMode.REDIRECT_ON_WRITE); + } else if (VolumeType.Memory.toString().equals(volumeType)) { + capability.setArrangementType(VolumeSnapshotArrangementType.INDIVIDUAL); } else { - capability.setSupport(false); + throw new CloudRuntimeException(String.format("unknown volume type %s", volumeType)); } + capability.setSupport(true); AskVolumeSnapshotCapabilityReply reply = new AskVolumeSnapshotCapabilityReply(); reply.setCapability(capability); @@ -1409,16 +1405,16 @@ public void fail(ErrorCode errorCode) { } @Override - protected void handle(BatchSyncVolumeSizeOnPrimaryStorageMsg msg) { + protected void handle(BatchSyncVolumeResourceSizeOnPrimaryStorageMsg msg) { NfsPrimaryStorageBackend backend = getBackendByHostUuid(msg.getHostUuid()); - backend.handle(getSelfInventory(), msg, new ReturnValueCompletion(msg) { + backend.handle(getSelfInventory(), msg, new ReturnValueCompletion(msg) { @Override - public void success(BatchSyncVolumeSizeOnPrimaryStorageReply reply) { + public void success(BatchSyncVolumeResourceSizeOnPrimaryStorageReply reply) { bus.reply(msg, reply); } @Override public void fail(ErrorCode errorCode) { - BatchSyncVolumeSizeOnPrimaryStorageReply reply = new BatchSyncVolumeSizeOnPrimaryStorageReply(); + BatchSyncVolumeResourceSizeOnPrimaryStorageReply reply = new BatchSyncVolumeResourceSizeOnPrimaryStorageReply(); reply.setError(errorCode); bus.reply(msg, reply); } 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 6c26e682ce6..5d9aad05c97 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 @@ -8,8 +8,8 @@ import org.zstack.header.storage.primary.*; import org.zstack.header.storage.snapshot.VolumeSnapshotInventory; import org.zstack.header.volume.VolumeStats; -import org.zstack.header.volume.BatchSyncVolumeSizeOnPrimaryStorageMsg; -import org.zstack.header.volume.BatchSyncVolumeSizeOnPrimaryStorageReply; +import org.zstack.header.volume.BatchSyncVolumeResourceSizeOnPrimaryStorageMsg; +import org.zstack.header.volume.BatchSyncVolumeResourceSizeOnPrimaryStorageReply; import org.zstack.header.volume.VolumeInventory; import org.zstack.storage.primary.EstimateVolumeTemplateSizeOnPrimaryStorageMsg; import org.zstack.storage.primary.EstimateVolumeTemplateSizeOnPrimaryStorageReply; @@ -30,7 +30,7 @@ public interface NfsPrimaryStorageBackend { void handle(PrimaryStorageInventory inv, EstimateVolumeTemplateSizeOnPrimaryStorageMsg msg, ReturnValueCompletion completion); - void handle(PrimaryStorageInventory inv, BatchSyncVolumeSizeOnPrimaryStorageMsg msg, ReturnValueCompletion completion); + void handle(PrimaryStorageInventory inv, BatchSyncVolumeResourceSizeOnPrimaryStorageMsg msg, ReturnValueCompletion completion); void handle(PrimaryStorageInventory inv, GetVolumeRootImageUuidFromPrimaryStorageMsg msg, 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 a1341ef4449..1524e1b3539 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 @@ -781,8 +781,8 @@ public void fail(ErrorCode errorCode) { } @Override - public void handle(PrimaryStorageInventory inv, BatchSyncVolumeSizeOnPrimaryStorageMsg msg, ReturnValueCompletion completion) { - BatchSyncVolumeSizeOnPrimaryStorageReply reply = new BatchSyncVolumeSizeOnPrimaryStorageReply(); + public void handle(PrimaryStorageInventory inv, BatchSyncVolumeResourceSizeOnPrimaryStorageMsg msg, ReturnValueCompletion completion) { + BatchSyncVolumeResourceSizeOnPrimaryStorageReply reply = new BatchSyncVolumeResourceSizeOnPrimaryStorageReply(); GetBatchVolumeActualSizeCmd cmd = new GetBatchVolumeActualSizeCmd(); cmd.volumeUuidInstallPaths = msg.getVolumeUuidInstallPaths(); diff --git a/plugin/sharedMountPointPrimaryStorage/src/main/java/org/zstack/storage/primary/smp/SMPPrimaryStorageBase.java b/plugin/sharedMountPointPrimaryStorage/src/main/java/org/zstack/storage/primary/smp/SMPPrimaryStorageBase.java index 6f16e83eb3c..0d3c187718b 100755 --- a/plugin/sharedMountPointPrimaryStorage/src/main/java/org/zstack/storage/primary/smp/SMPPrimaryStorageBase.java +++ b/plugin/sharedMountPointPrimaryStorage/src/main/java/org/zstack/storage/primary/smp/SMPPrimaryStorageBase.java @@ -395,10 +395,11 @@ protected void handle(AskVolumeSnapshotCapabilityMsg msg) { VolumeSnapshotCapability capability = new VolumeSnapshotCapability(); capability.setSupport(true); - String volumeType = msg.getVolume().getType(); + String volumeType = msg.getVolumeType(); if (VolumeType.Data.toString().equals(volumeType) || VolumeType.Root.toString().equals(volumeType)) { capability.setArrangementType(VolumeSnapshotArrangementType.CHAIN); capability.setPlacementType(VolumeSnapshotCapability.VolumeSnapshotPlacementType.EXTERNAL); + capability.setMode(VolumeSnapshotCapability.VolumeSnapshotMode.REDIRECT_ON_WRITE); } else if (VolumeType.Memory.toString().equals(volumeType)) { capability.setArrangementType(VolumeSnapshotArrangementType.INDIVIDUAL); } else { @@ -461,8 +462,8 @@ public void fail(ErrorCode errorCode) { } @Override - protected void handle(BatchSyncVolumeSizeOnPrimaryStorageMsg msg) { - BatchSyncVolumeSizeOnPrimaryStorageReply reply = new BatchSyncVolumeSizeOnPrimaryStorageReply(); + protected void handle(BatchSyncVolumeResourceSizeOnPrimaryStorageMsg msg) { + BatchSyncVolumeResourceSizeOnPrimaryStorageReply reply = new BatchSyncVolumeResourceSizeOnPrimaryStorageReply(); bus.reply(msg, reply); logger.warn("Not supported at current edition"); } diff --git a/plugin/xinfini/src/main/java/org/zstack/xinfini/XInfiniStorageController.java b/plugin/xinfini/src/main/java/org/zstack/xinfini/XInfiniStorageController.java index c9efd282c05..965ddd2f5de 100644 --- a/plugin/xinfini/src/main/java/org/zstack/xinfini/XInfiniStorageController.java +++ b/plugin/xinfini/src/main/java/org/zstack/xinfini/XInfiniStorageController.java @@ -16,6 +16,7 @@ import org.zstack.header.host.HostVO_; import org.zstack.header.storage.addon.*; import org.zstack.header.storage.addon.primary.*; +import org.zstack.header.storage.primary.StorageResourceStats; import org.zstack.header.storage.primary.VolumeSnapshotCapability; import org.zstack.header.storage.snapshot.VolumeSnapshotStats; import org.zstack.header.volume.*; @@ -93,6 +94,7 @@ private String getVhostSocketDir() { scap.setSupport(true); scap.setArrangementType(VolumeSnapshotCapability.VolumeSnapshotArrangementType.INDIVIDUAL); scap.setPlacementType(VolumeSnapshotCapability.VolumeSnapshotPlacementType.INTERNAL); + scap.setMode(VolumeSnapshotCapability.VolumeSnapshotMode.REDIRECT_ON_WRITE); scap.setSupportCreateOnHypervisor(false); scap.setSupportLazyDelete(false); scap.setVolumePathFromInternalSnapshotRegex("^[^@]+"); @@ -872,15 +874,15 @@ private String getParentUri(VolumeModule vol) { } @Override - public void batchStats(Collection installPath, ReturnValueCompletion> comp) { - List stats = installPath.stream().map(it -> { + public void batchStats(Collection installPaths, ReturnValueCompletion> comp) { + List stats = installPaths.stream().map(it -> { VolumeModule vol = apiHelper.getVolume(getVolIdFromPath(it)); VolumeStats s = new VolumeStats(); s.setInstallPath(it); s.setSize(SizeUnit.MEGABYTE.toByte(vol.getSpec().getSizeMb())); s.setActualSize(vol.getStatus().getAllocatedSizeByte()); s.setFormat(VolumeConstant.VOLUME_FORMAT_RAW); - return s; + return (StorageResourceStats) s; }).collect(Collectors.toList()); comp.success(stats); } diff --git a/plugin/zbs/src/main/java/org/zstack/storage/zbs/ZbsStorageController.java b/plugin/zbs/src/main/java/org/zstack/storage/zbs/ZbsStorageController.java index 27810c6bb45..76e556cbb21 100644 --- a/plugin/zbs/src/main/java/org/zstack/storage/zbs/ZbsStorageController.java +++ b/plugin/zbs/src/main/java/org/zstack/storage/zbs/ZbsStorageController.java @@ -100,6 +100,7 @@ public class ZbsStorageController implements PrimaryStorageControllerSvc, Primar public static final String CLONE_VOLUME_PATH = "/zbs/primarystorage/volume/clone"; public static final String QUERY_VOLUME_PATH = "/zbs/primarystorage/volume/query"; public static final String BATCH_QUERY_VOLUME_PATH = "/zbs/primarystorage/volume/query/batch"; + public static final String BATCH_QUERY_VOLUME_WITH_SNAPSHOT_PATH = "/zbs/primarystorage/volume/query/batch/withsnapshot"; public static final String EXPAND_VOLUME_PATH = "/zbs/primarystorage/volume/expand"; public static final String FLATTEN_VOLUME_PATH = "/zbs/primarystorage/volume/flatten"; public static final String CBD_TO_NBD_PATH = "/zbs/primarystorage/volume/cbdtonbd"; @@ -118,6 +119,7 @@ public class ZbsStorageController implements PrimaryStorageControllerSvc, Primar scap.setSupport(true); scap.setArrangementType(VolumeSnapshotCapability.VolumeSnapshotArrangementType.INDIVIDUAL); scap.setPlacementType(VolumeSnapshotCapability.VolumeSnapshotPlacementType.INTERNAL); + scap.setMode(VolumeSnapshotCapability.VolumeSnapshotMode.COPY_ON_WRITE); scap.setSupportCreateOnHypervisor(false); scap.setSupportLazyDelete(false); scap.setVolumePathFromInternalSnapshotRegex("^[^@]+"); @@ -1041,13 +1043,7 @@ public void stats(String installPath, ReturnValueCompletion comp) { httpCall(QUERY_VOLUME_PATH, cmd, QueryVolumeRsp.class, new ReturnValueCompletion(comp) { @Override public void success(QueryVolumeRsp returnValue) { - VolumeStats stats = new VolumeStats(); - stats.setInstallPath(installPath); - stats.setSize(returnValue.getSize()); - stats.setActualSize(returnValue.getActualSize()); - stats.setFormat(VolumeConstant.VOLUME_FORMAT_RAW); - stats.setParentUri(ZbsHelper.normalizeToZbsPath(returnValue.getParentUri())); - comp.success(stats); + comp.success(toVolumeStats(installPath, returnValue)); } @Override @@ -1058,23 +1054,31 @@ public void fail(ErrorCode errorCode) { } @Override - public void batchStats(Collection installPaths, ReturnValueCompletion> comp) { + public void batchStats(Collection installPaths, ReturnValueCompletion> comp) { BatchQueryVolumeCmd cmd = new BatchQueryVolumeCmd(); + Collection paths = installPaths == null ? Collections.emptyList() : installPaths; + List cbdInstallPaths = paths.stream() + .map(it -> convertZbsPathToCbdPath(it, this::getPhysicalPoolName)) + .collect(Collectors.toList()); + boolean withSnapshot = cbdInstallPaths.stream().anyMatch(it -> it.contains("@")); - cmd.setInstallPaths(installPaths.stream().map(it -> convertZbsPathToCbdPath(it, this::getPhysicalPoolName)) - .collect(Collectors.toList())); + cmd.setInstallPaths(cbdInstallPaths); - httpCall(BATCH_QUERY_VOLUME_PATH, cmd, BatchQueryVolumeRsp.class, new ReturnValueCompletion(comp) { + String path = withSnapshot ? BATCH_QUERY_VOLUME_WITH_SNAPSHOT_PATH : BATCH_QUERY_VOLUME_PATH; + httpCall(path, cmd, BatchQueryVolumeRsp.class, new ReturnValueCompletion(comp) { @Override public void success(BatchQueryVolumeRsp returnValue) { - List stats = returnValue.getVolumes().entrySet().stream().map(v -> { + List stats = returnValue.getVolumes().entrySet().stream().map(v -> { VolumeStats s = new VolumeStats(); s.setInstallPath(ZbsHelper.normalizeToZbsPath(v.getKey())); s.setSize(v.getValue().get("length")); s.setActualSize(v.getValue().get("usedSize")); s.setFormat(VolumeConstant.VOLUME_FORMAT_RAW); - return s; + return (StorageResourceStats) s; }).collect(Collectors.toList()); + if (withSnapshot) { + stats.addAll(toVolumeSnapshotStats(returnValue.getSnapshots())); + } comp.success(stats); } @@ -1085,6 +1089,29 @@ public void fail(ErrorCode errorCode) { }); } + private VolumeStats toVolumeStats(String installPath, QueryVolumeRsp rsp) { + VolumeStats stats = new VolumeStats(); + stats.setInstallPath(installPath); + stats.setSize(rsp.getSize()); + stats.setActualSize(rsp.getActualSize()); + stats.setFormat(VolumeConstant.VOLUME_FORMAT_RAW); + stats.setParentUri(ZbsHelper.normalizeToZbsPath(rsp.getParentUri())); + return stats; + } + + private List toVolumeSnapshotStats(Map> snapshots) { + if (snapshots == null || snapshots.isEmpty()) { + return Collections.emptyList(); + } + + return snapshots.entrySet().stream().map(e -> { + VolumeSnapshotStats stats = new VolumeSnapshotStats(); + stats.setInstallPath(ZbsHelper.normalizeToZbsPath(e.getKey())); + stats.setActualSize(e.getValue().get("usedSize")); + return stats; + }).collect(Collectors.toList()); + } + @Override public void expandVolume(String installPath, long size, ReturnValueCompletion comp) { ExpandVolumeCmd cmd = new ExpandVolumeCmd(); @@ -1625,6 +1652,7 @@ public String getParentUri() { public void setParentUri(String parentUri) { this.parentUri = parentUri; } + } public static class FlattenVolumeRsp extends QueryVolumeRsp { @@ -1633,6 +1661,7 @@ public static class FlattenVolumeRsp extends QueryVolumeRsp { public static class BatchQueryVolumeRsp extends AgentResponse { private Map> volumes; + private Map> snapshots; public Map> getVolumes() { return volumes; @@ -1641,6 +1670,14 @@ public Map> getVolumes() { public void setVolumes(Map> volumes) { this.volumes = volumes; } + + public Map> getSnapshots() { + return snapshots; + } + + public void setSnapshots(Map> snapshots) { + this.snapshots = snapshots; + } } public static class CbdToNbdRsp extends AgentResponse { diff --git a/simulator/simulatorImpl/src/main/java/org/zstack/simulator/storage/primary/SimulatorPrimaryStorage.java b/simulator/simulatorImpl/src/main/java/org/zstack/simulator/storage/primary/SimulatorPrimaryStorage.java index 021eb33ebc6..758c11b5ee6 100755 --- a/simulator/simulatorImpl/src/main/java/org/zstack/simulator/storage/primary/SimulatorPrimaryStorage.java +++ b/simulator/simulatorImpl/src/main/java/org/zstack/simulator/storage/primary/SimulatorPrimaryStorage.java @@ -9,8 +9,8 @@ import org.zstack.header.storage.primary.*; import org.zstack.header.storage.primary.VolumeSnapshotCapability.VolumeSnapshotArrangementType; import org.zstack.header.storage.snapshot.ShrinkVolumeSnapshotOnPrimaryStorageMsg; -import org.zstack.header.volume.BatchSyncVolumeSizeOnPrimaryStorageMsg; -import org.zstack.header.volume.BatchSyncVolumeSizeOnPrimaryStorageReply; +import org.zstack.header.volume.BatchSyncVolumeResourceSizeOnPrimaryStorageMsg; +import org.zstack.header.volume.BatchSyncVolumeResourceSizeOnPrimaryStorageReply; import org.zstack.header.volume.VolumeInventory; import org.zstack.storage.primary.EstimateVolumeTemplateSizeOnPrimaryStorageMsg; import org.zstack.storage.primary.PrimaryStorageBase; @@ -159,6 +159,7 @@ protected void handle(AskVolumeSnapshotCapabilityMsg msg) { VolumeSnapshotCapability capability = new VolumeSnapshotCapability(); capability.setArrangementType(VolumeSnapshotArrangementType.CHAIN); capability.setPlacementType(VolumeSnapshotCapability.VolumeSnapshotPlacementType.EXTERNAL); + capability.setMode(VolumeSnapshotCapability.VolumeSnapshotMode.REDIRECT_ON_WRITE); capability.setSupport(true); reply.setCapability(capability); bus.reply(msg, reply); @@ -179,8 +180,8 @@ protected void handle(EstimateVolumeTemplateSizeOnPrimaryStorageMsg msg) { } @Override - protected void handle(BatchSyncVolumeSizeOnPrimaryStorageMsg msg) { - BatchSyncVolumeSizeOnPrimaryStorageReply reply = new BatchSyncVolumeSizeOnPrimaryStorageReply(); + protected void handle(BatchSyncVolumeResourceSizeOnPrimaryStorageMsg msg) { + BatchSyncVolumeResourceSizeOnPrimaryStorageReply reply = new BatchSyncVolumeResourceSizeOnPrimaryStorageReply(); Map actualSize = msg.getVolumeUuidInstallPaths() .entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> 0L)); reply.setActualSizes(actualSize); diff --git a/storage/src/main/java/org/zstack/storage/addon/primary/ExternalPrimaryStorage.java b/storage/src/main/java/org/zstack/storage/addon/primary/ExternalPrimaryStorage.java index 921c8431326..9b089602b47 100644 --- a/storage/src/main/java/org/zstack/storage/addon/primary/ExternalPrimaryStorage.java +++ b/storage/src/main/java/org/zstack/storage/addon/primary/ExternalPrimaryStorage.java @@ -1837,19 +1837,43 @@ public void fail(ErrorCode errorCode) { } @Override - protected void handle(BatchSyncVolumeSizeOnPrimaryStorageMsg msg) { - BatchSyncVolumeSizeOnPrimaryStorageReply reply = new BatchSyncVolumeSizeOnPrimaryStorageReply(); + protected void handle(BatchSyncVolumeResourceSizeOnPrimaryStorageMsg msg) { + BatchSyncVolumeResourceSizeOnPrimaryStorageReply reply = new BatchSyncVolumeResourceSizeOnPrimaryStorageReply(); Map installPathToUuids = msg.getVolumeUuidInstallPaths().entrySet().stream().collect(Collectors.toMap( Map.Entry::getValue, Map.Entry::getKey, (k1, k2) -> k1 )); - controller.batchStats(msg.getVolumeUuidInstallPaths().values(), new ReturnValueCompletion>(msg) { - @Override - public void success(List stats) { - Map actualSizeByUuids = stats.stream().collect(Collectors.toMap( - s -> installPathToUuids.get(s.getInstallPath()), VolumeStats::getActualSize, (k1, k2) -> k1 + Map snapshotInstallPathToUuids = !msg.isWithSnapshot() || msg.getSnapshotUuidInstallPaths() == null ? Collections.emptyMap() : + msg.getSnapshotUuidInstallPaths().entrySet().stream().collect(Collectors.toMap( + Map.Entry::getValue, Map.Entry::getKey, (k1, k2) -> k1 )); + Collection installPaths = new ArrayList<>(msg.getVolumeUuidInstallPaths().values()); + if (msg.isWithSnapshot() && msg.getSnapshotUuidInstallPaths() != null) { + installPaths.addAll(msg.getSnapshotUuidInstallPaths().values()); + } + controller.batchStats(installPaths, new ReturnValueCompletion>(msg) { + @Override + public void success(List stats) { + Map actualSizeByUuids = stats.stream() + .filter(it -> it instanceof VolumeStats) + .filter(it -> it.getActualSize() != null) + .filter(it -> installPathToUuids.containsKey(it.getInstallPath())) + .collect(Collectors.toMap( + s -> installPathToUuids.get(s.getInstallPath()), + StorageResourceStats::getActualSize, + (k1, k2) -> k1 + )); reply.setActualSizes(actualSizeByUuids); + reply.setSnapshotActualSizes(snapshotInstallPathToUuids.isEmpty() ? Collections.emptyMap() : + stats.stream() + .filter(it -> it instanceof VolumeSnapshotStats) + .filter(it -> it.getActualSize() != null) + .filter(it -> snapshotInstallPathToUuids.containsKey(it.getInstallPath())) + .collect(Collectors.toMap( + it -> snapshotInstallPathToUuids.get(it.getInstallPath()), + StorageResourceStats::getActualSize, + (k1, k2) -> k1 + ))); bus.reply(msg, reply); } 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 9920d4900ee..bcf546fa2bf 100755 --- a/storage/src/main/java/org/zstack/storage/primary/PrimaryStorageBase.java +++ b/storage/src/main/java/org/zstack/storage/primary/PrimaryStorageBase.java @@ -150,7 +150,7 @@ public void setNewAdded(boolean newAdded) { protected abstract void handle(EstimateVolumeTemplateSizeOnPrimaryStorageMsg msg); - protected abstract void handle(BatchSyncVolumeSizeOnPrimaryStorageMsg msg); + protected abstract void handle(BatchSyncVolumeResourceSizeOnPrimaryStorageMsg msg); protected abstract void handle(MergeVolumeSnapshotOnPrimaryStorageMsg msg); @@ -367,8 +367,8 @@ protected void handleLocalMessage(Message msg) { handle((SyncVolumeSizeOnPrimaryStorageMsg) msg); } else if (msg instanceof EstimateVolumeTemplateSizeOnPrimaryStorageMsg) { handle((EstimateVolumeTemplateSizeOnPrimaryStorageMsg) msg); - } else if (msg instanceof BatchSyncVolumeSizeOnPrimaryStorageMsg) { - handle((BatchSyncVolumeSizeOnPrimaryStorageMsg) msg); + } else if (msg instanceof BatchSyncVolumeResourceSizeOnPrimaryStorageMsg) { + handle((BatchSyncVolumeResourceSizeOnPrimaryStorageMsg) msg); } else if (msg instanceof PingPrimaryStorageMsg) { handle((PingPrimaryStorageMsg) msg); } else if (msg instanceof ChangePrimaryStorageStatusMsg) { diff --git a/storage/src/main/java/org/zstack/storage/volume/VolumeManagerImpl.java b/storage/src/main/java/org/zstack/storage/volume/VolumeManagerImpl.java index 101ec515b45..55f29a6fcab 100755 --- a/storage/src/main/java/org/zstack/storage/volume/VolumeManagerImpl.java +++ b/storage/src/main/java/org/zstack/storage/volume/VolumeManagerImpl.java @@ -62,6 +62,7 @@ import java.sql.Timestamp; import java.time.LocalDateTime; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -74,6 +75,19 @@ public class VolumeManagerImpl extends AbstractService implements VolumeManager, ResourceOwnerAfterChangeExtensionPoint, VmStateChangedExtensionPoint, VmDetachVolumeExtensionPoint, VmAttachVolumeExtensionPoint, HostAfterConnectedExtensionPoint { private static final CLogger logger = Utils.getLogger(VolumeManagerImpl.class); + private final Map snapshotSizeSyncRequiredCache = new ConcurrentHashMap<>(); + + private static class ActiveVolumeSizeSyncInfo { + private final Map volumeUuidInstallPaths = new HashMap<>(); + private final Set volumeTypes = new HashSet<>(); + + void add(String volumeUuid, String installPath, VolumeType volumeType) { + volumeUuidInstallPaths.put(volumeUuid, installPath); + if (VolumeType.Root == volumeType || VolumeType.Data == volumeType) { + volumeTypes.add(volumeType.toString()); + } + } + } @Autowired private CloudBus bus; @@ -761,34 +775,65 @@ private void handle(BatchSyncActiveVolumeSizeOnHostMsg msg) { bus.reply(msg, reply); return; } - Map> activeVolumesInPs = Q.New(VolumeVO.class) - .select(VolumeVO_.primaryStorageUuid, VolumeVO_.uuid, VolumeVO_.installPath) - .in(VolumeVO_.vmInstanceUuid, activeVmUuids).listTuple().stream() - .collect(Collectors.groupingBy(t -> t.get(0, String.class), Collectors.toMap( - t -> ((Tuple)t).get(1, String.class), t -> ((Tuple)t).get(2, String.class)))); + + Map activeVolumesInPs = new HashMap<>(); + Q.New(VolumeVO.class) + .select(VolumeVO_.primaryStorageUuid, VolumeVO_.uuid, VolumeVO_.installPath, VolumeVO_.type) + .in(VolumeVO_.vmInstanceUuid, activeVmUuids) + .listTuple().forEach(t -> { + String primaryStorageUuid = t.get(0, String.class); + activeVolumesInPs.computeIfAbsent(primaryStorageUuid, k -> new ActiveVolumeSizeSyncInfo()) + .add(t.get(1, String.class), t.get(2, String.class), t.get(3, VolumeType.class)); + }); + + Map primaryStorageTypes = getPrimaryStorageTypesForSnapshotCapabilityCache(activeVolumesInPs.keySet()); new While<>(activeVolumesInPs.entrySet()).each((e, completion) -> { - BatchSyncVolumeSizeOnPrimaryStorageMsg bmsg = new BatchSyncVolumeSizeOnPrimaryStorageMsg(); - bmsg.setHostUuid(msg.getHostUuid()); - bmsg.setPrimaryStorageUuid(e.getKey()); - bmsg.setVolumeUuidInstallPaths(e.getValue()); - bus.makeTargetServiceIdByResourceUuid(bmsg, PrimaryStorageConstant.SERVICE_ID, e.getKey()); - bus.send(bmsg, new CloudBusCallBack(completion) { + String primaryStorageUuid = e.getKey(); + ActiveVolumeSizeSyncInfo activeVolumeSizeSyncInfo = e.getValue(); + isSnapshotSizeSyncRequired(primaryStorageUuid, primaryStorageTypes.get(primaryStorageUuid), + activeVolumeSizeSyncInfo.volumeTypes, new ReturnValueCompletion(completion) { @Override - public void run(MessageReply r) { - if (r.isSuccess()) { - BatchSyncVolumeSizeOnPrimaryStorageReply br = r.castReply(); - Map actualSizes = br.getActualSizes(); + public void success(Boolean syncSnapshotSize) { + syncVolumeSize(syncSnapshotSize); + } - reply.addSuccessCount(actualSizes.size()); - reply.addFailCount(e.getValue().size() - actualSizes.size()); + @Override + public void fail(ErrorCode errorCode) { + syncVolumeSize(false); + } - refreshVolume(actualSizes); - } else { - reply.addFailCount(e.getValue().size()); + private void syncVolumeSize(boolean syncSnapshotSize) { + BatchSyncVolumeResourceSizeOnPrimaryStorageMsg bmsg = new BatchSyncVolumeResourceSizeOnPrimaryStorageMsg(); + bmsg.setHostUuid(msg.getHostUuid()); + bmsg.setPrimaryStorageUuid(primaryStorageUuid); + bmsg.setVolumeUuidInstallPaths(activeVolumeSizeSyncInfo.volumeUuidInstallPaths); + bmsg.setWithSnapshot(syncSnapshotSize); + if (syncSnapshotSize) { + bmsg.setSnapshotUuidInstallPaths(getSnapshotUuidInstallPaths(primaryStorageUuid, + activeVolumeSizeSyncInfo.volumeUuidInstallPaths.keySet())); } - - completion.done(); + bus.makeTargetServiceIdByResourceUuid(bmsg, PrimaryStorageConstant.SERVICE_ID, primaryStorageUuid); + bus.send(bmsg, new CloudBusCallBack(completion) { + @Override + public void run(MessageReply r) { + if (r.isSuccess()) { + BatchSyncVolumeResourceSizeOnPrimaryStorageReply br = r.castReply(); + Map actualSizes = br.getActualSizes(); + + reply.addSuccessCount(actualSizes.size()); + reply.addFailCount(activeVolumeSizeSyncInfo.volumeUuidInstallPaths.size() - actualSizes.size()); + + if (syncSnapshotSize) { + updateSnapshotActualSizes(br.getSnapshotActualSizes()); + } + refreshVolume(actualSizes); + } else { + reply.addFailCount(activeVolumeSizeSyncInfo.volumeUuidInstallPaths.size()); + } + completion.done(); + } + }); } @Transactional(readOnly = true) @@ -826,6 +871,133 @@ public void done(ErrorCodeList errorCodeList) { }); } + private void isSnapshotSizeSyncRequired(String primaryStorageUuid, String primaryStorageType, + Collection volumeTypes, ReturnValueCompletion completion) { + if (primaryStorageType == null) { + completion.success(false); + return; + } + + if (volumeTypes.isEmpty()) { + completion.success(false); + return; + } + + List uncachedVolumeTypes = volumeTypes.stream() + .filter(volumeType -> !snapshotSizeSyncRequiredCache.containsKey(snapshotSizeSyncRequiredCacheKey(primaryStorageType, volumeType))) + .collect(Collectors.toList()); + if (uncachedVolumeTypes.isEmpty()) { + completion.success(isSnapshotSizeSyncRequired(primaryStorageType, volumeTypes)); + return; + } + + new While<>(uncachedVolumeTypes).each((volumeType, compl) -> { + AskVolumeSnapshotCapabilityMsg msg = new AskVolumeSnapshotCapabilityMsg(); + msg.setPrimaryStorageUuid(primaryStorageUuid); + msg.setVolumeType(volumeType); + bus.makeTargetServiceIdByResourceUuid(msg, PrimaryStorageConstant.SERVICE_ID, primaryStorageUuid); + bus.send(msg, new CloudBusCallBack(compl) { + @Override + public void run(MessageReply reply) { + if (!reply.isSuccess()) { + compl.addError(reply.getError()); + compl.done(); + return; + } + + AskVolumeSnapshotCapabilityReply r = reply.castReply(); + snapshotSizeSyncRequiredCache.put(snapshotSizeSyncRequiredCacheKey(primaryStorageType, volumeType), + isCowSnapshotSupported(r.getCapability())); + compl.done(); + } + }); + }).run(new WhileDoneCompletion(completion) { + @Override + public void done(ErrorCodeList errorCodeList) { + if (!errorCodeList.getCauses().isEmpty()) { + completion.fail(errorCodeList.getCauses().get(0)); + return; + } + + completion.success(isSnapshotSizeSyncRequired(primaryStorageType, volumeTypes)); + } + }); + } + + private Map getPrimaryStorageTypesForSnapshotCapabilityCache(Collection primaryStorageUuids) { + if (primaryStorageUuids == null || primaryStorageUuids.isEmpty()) { + return Collections.emptyMap(); + } + + return Q.New(PrimaryStorageVO.class) + .select(PrimaryStorageVO_.uuid, PrimaryStorageVO_.type) + .in(PrimaryStorageVO_.uuid, primaryStorageUuids) + .listTuple().stream() + .collect(Collectors.toMap( + t -> t.get(0, String.class), + t -> getPrimaryStorageTypeForSnapshotCapabilityCache(t.get(0, String.class), t.get(1, String.class)))); + } + + private String getPrimaryStorageTypeForSnapshotCapabilityCache(String primaryStorageUuid, String primaryStorageType) { + Optional factory = pluginRgty.getExtensionList(PrimaryStorageFactory.class).stream() + .filter(f -> f.getPrimaryStorageType().toString().equals(primaryStorageType)) + .findFirst(); + if (!factory.isPresent()) { + return primaryStorageType; + } + + String identity = factory.get().getInventory(primaryStorageUuid).getIdentity(); + return identity == null ? primaryStorageType : identity; + } + + private boolean isSnapshotSizeSyncRequired(String primaryStorageType, Collection volumeTypes) { + return volumeTypes.stream() + .anyMatch(volumeType -> snapshotSizeSyncRequiredCache.getOrDefault(snapshotSizeSyncRequiredCacheKey(primaryStorageType, volumeType), false)); + } + + private String snapshotSizeSyncRequiredCacheKey(String primaryStorageType, String volumeType) { + return String.format("%s-%s", primaryStorageType, volumeType); + } + + private boolean isCowSnapshotSupported(VolumeSnapshotCapability capability) { + return capability != null && capability.isSupport() && + capability.getMode() == VolumeSnapshotCapability.VolumeSnapshotMode.COPY_ON_WRITE; + } + + private Map getSnapshotUuidInstallPaths(String primaryStorageUuid, Collection volumeUuids) { + if (volumeUuids == null || volumeUuids.isEmpty()) { + return Collections.emptyMap(); + } + + String sql = "select sp.uuid, sp.primaryStorageInstallPath from VolumeSnapshotVO sp " + + "where sp.volumeUuid in :volumeUuids " + + "and sp.primaryStorageUuid = :primaryStorageUuid " + + "and sp.primaryStorageInstallPath is not null " + + "and sp.status = :status"; + TypedQuery q = dbf.getEntityManager().createQuery(sql, Tuple.class); + q.setParameter("volumeUuids", volumeUuids); + q.setParameter("primaryStorageUuid", primaryStorageUuid); + q.setParameter("status", VolumeSnapshotStatus.Ready); + return q.getResultList().stream().collect(Collectors.toMap( + t -> t.get(0, String.class), + t -> t.get(1, String.class), + (v1, v2) -> v1 + )); + } + + private void updateSnapshotActualSizes(Map snapshotActualSizes) { + if (snapshotActualSizes == null || snapshotActualSizes.isEmpty()) { + return; + } + + snapshotActualSizes.entrySet().stream() + .filter(e -> e.getValue() != null && e.getValue() > 0) + .forEach(e -> SQL.New(VolumeSnapshotVO.class) + .eq(VolumeSnapshotVO_.uuid, e.getKey()) + .set(VolumeSnapshotVO_.size, e.getValue()) + .update()); + } + private void handle(CreateDataVolumeFromVolumeSnapshotMsg msg) { final CreateDataVolumeFromVolumeSnapshotReply reply = new CreateDataVolumeFromVolumeSnapshotReply(); final VolumeVO vo = new VolumeVO(); @@ -1425,4 +1597,4 @@ public void run(MessageReply reply) { }); }); } -} \ No newline at end of file +} diff --git a/test/src/test/groovy/org/zstack/test/integration/storage/primary/addon/zbs/ZbsPrimaryStorageCase.groovy b/test/src/test/groovy/org/zstack/test/integration/storage/primary/addon/zbs/ZbsPrimaryStorageCase.groovy index 970f51dfadd..a5a56867693 100644 --- a/test/src/test/groovy/org/zstack/test/integration/storage/primary/addon/zbs/ZbsPrimaryStorageCase.groovy +++ b/test/src/test/groovy/org/zstack/test/integration/storage/primary/addon/zbs/ZbsPrimaryStorageCase.groovy @@ -12,8 +12,8 @@ import org.zstack.header.message.MessageReply import org.zstack.header.storage.primary.GetVolumeBackingChainFromPrimaryStorageMsg import org.zstack.header.storage.primary.GetVolumeBackingChainFromPrimaryStorageReply import org.zstack.header.storage.primary.PrimaryStorageConstant -import org.zstack.header.volume.BatchSyncVolumeSizeOnPrimaryStorageMsg -import org.zstack.header.volume.BatchSyncVolumeSizeOnPrimaryStorageReply +import org.zstack.header.volume.BatchSyncVolumeResourceSizeOnPrimaryStorageMsg +import org.zstack.header.volume.BatchSyncVolumeResourceSizeOnPrimaryStorageReply import org.zstack.header.storage.addon.primary.ExternalPrimaryStorageVO import org.zstack.header.storage.addon.primary.ExternalPrimaryStorageSpaceVO import org.zstack.header.storage.addon.primary.ExternalPrimaryStorageVO_ @@ -1221,14 +1221,14 @@ class ZbsPrimaryStorageCase extends SubCase { } CloudBus bus = bean(CloudBus.class) - BatchSyncVolumeSizeOnPrimaryStorageMsg msg = new BatchSyncVolumeSizeOnPrimaryStorageMsg() + BatchSyncVolumeResourceSizeOnPrimaryStorageMsg msg = new BatchSyncVolumeResourceSizeOnPrimaryStorageMsg() msg.setPrimaryStorageUuid(ps.uuid) msg.setVolumeUuidInstallPaths([(volUuid): zbsPath]) bus.makeTargetServiceIdByResourceUuid(msg, PrimaryStorageConstant.SERVICE_ID, ps.uuid) MessageReply reply = bus.call(msg) assert reply.isSuccess() : "batch size sync must not fail on cbd-form install path: ${reply.error}" - BatchSyncVolumeSizeOnPrimaryStorageReply r = reply as BatchSyncVolumeSizeOnPrimaryStorageReply + BatchSyncVolumeResourceSizeOnPrimaryStorageReply r = reply as BatchSyncVolumeResourceSizeOnPrimaryStorageReply assert r.actualSizes.get(volUuid) == usedSize : "actualSize must map back to uuid via zbs:// install path, got ${r.actualSizes}"