diff --git a/header/src/main/java/org/zstack/header/storage/addon/primary/ExternalPrimaryStorageImageDownloadContext.java b/header/src/main/java/org/zstack/header/storage/addon/primary/ExternalPrimaryStorageImageDownloadContext.java new file mode 100644 index 00000000000..5337d519206 --- /dev/null +++ b/header/src/main/java/org/zstack/header/storage/addon/primary/ExternalPrimaryStorageImageDownloadContext.java @@ -0,0 +1,72 @@ +package org.zstack.header.storage.addon.primary; + +import org.zstack.header.image.ImageInventory; +import org.zstack.header.storage.addon.RemoteTarget; +import org.zstack.header.volume.VolumeStats; + +public class ExternalPrimaryStorageImageDownloadContext { + private String primaryStorageUuid; + private String primaryStorageType; + private ImageInventory image; + private CreateVolumeSpec spec; + private String targetResourceType; + private VolumeStats volume; + private RemoteTarget remoteTarget; + + public String getPrimaryStorageUuid() { + return primaryStorageUuid; + } + + public void setPrimaryStorageUuid(String primaryStorageUuid) { + this.primaryStorageUuid = primaryStorageUuid; + } + + public String getPrimaryStorageType() { + return primaryStorageType; + } + + public void setPrimaryStorageType(String primaryStorageType) { + this.primaryStorageType = primaryStorageType; + } + + public ImageInventory getImage() { + return image; + } + + public void setImage(ImageInventory image) { + this.image = image; + } + + public CreateVolumeSpec getSpec() { + return spec; + } + + public void setSpec(CreateVolumeSpec spec) { + this.spec = spec; + } + + public String getTargetResourceType() { + return targetResourceType; + } + + public void setTargetResourceType(String targetResourceType) { + this.targetResourceType = targetResourceType; + } + + public VolumeStats getVolume() { + return volume; + } + + public void setVolume(VolumeStats volume) { + this.volume = volume; + } + + public RemoteTarget getRemoteTarget() { + return remoteTarget; + } + + public void setRemoteTarget(RemoteTarget remoteTarget) { + this.remoteTarget = remoteTarget; + } + +} diff --git a/header/src/main/java/org/zstack/header/storage/addon/primary/ExternalPrimaryStorageImageDownloadExtensionPoint.java b/header/src/main/java/org/zstack/header/storage/addon/primary/ExternalPrimaryStorageImageDownloadExtensionPoint.java new file mode 100644 index 00000000000..42b02d1af69 --- /dev/null +++ b/header/src/main/java/org/zstack/header/storage/addon/primary/ExternalPrimaryStorageImageDownloadExtensionPoint.java @@ -0,0 +1,13 @@ +package org.zstack.header.storage.addon.primary; + +import org.zstack.header.core.Completion; +import org.zstack.header.image.ImageInventory; + +public interface ExternalPrimaryStorageImageDownloadExtensionPoint { + void beforeDownload(String primaryStorageUuid, String primaryStorageType, ImageInventory image, + CreateVolumeSpec spec, String targetResourceType); + + boolean supportsDownload(ExternalPrimaryStorageImageDownloadContext context); + + void download(ExternalPrimaryStorageImageDownloadContext context, Completion completion); +} 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 ed60c4a1f01..b5499869d8f 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 @@ -1329,6 +1329,27 @@ private void downloadImageTo(ImageInventory image, CreateVolumeSpec spec, String final String exportProtocol = controller.reportCapabilities().getDefaultImageExportProtocol() != null ? controller.reportCapabilities().getDefaultImageExportProtocol().toString() : rcf.getResourceConfigValue(ExternalPrimaryStorageGlobalConfig.IMAGE_EXPORT_PROTOCOL, self.getUuid(), String.class); + + private ExternalPrimaryStorageImageDownloadContext buildDownloadContext() { + ExternalPrimaryStorageImageDownloadContext context = new ExternalPrimaryStorageImageDownloadContext(); + context.setPrimaryStorageUuid(self.getUuid()); + context.setPrimaryStorageType(externalVO.getIdentity()); + context.setImage(image); + context.setSpec(spec); + context.setTargetResourceType(targetClz); + return context; + } + + private ExternalPrimaryStorageImageDownloadExtensionPoint findDownloadExtension( + ExternalPrimaryStorageImageDownloadContext context) { + for (ExternalPrimaryStorageImageDownloadExtensionPoint ext : pluginRgty.getExtensionList(ExternalPrimaryStorageImageDownloadExtensionPoint.class)) { + if (ext.supportsDownload(context)) { + return ext; + } + } + return null; + } + @Override public void setup() { flow(new Flow() { @@ -1337,6 +1358,9 @@ public void setup() { @Override public void run(FlowTrigger trigger, Map data) { spec.setSize(Long.max(image.getActualSize(), image.getSize())); + for (ExternalPrimaryStorageImageDownloadExtensionPoint ext : pluginRgty.getExtensionList(ExternalPrimaryStorageImageDownloadExtensionPoint.class)) { + ext.beforeDownload(self.getUuid(), externalVO.getIdentity(), image, spec, targetClz); + } controller.createVolume(spec, new ReturnValueCompletion(trigger) { @Override public void success(VolumeStats stats) { @@ -1415,9 +1439,47 @@ public void fail(ErrorCode errorCode) { } }); + flow(new NoRollbackFlow() { + String __name__ = "download-encrypted-image"; + + @Override + public boolean skip(Map data) { + return !spec.isEncrypted(); + } + + @Override + public void run(FlowTrigger trigger, Map data) { + ExternalPrimaryStorageImageDownloadContext context = buildDownloadContext(); + context.setVolume(volume); + context.setRemoteTarget(remoteTarget); + ExternalPrimaryStorageImageDownloadExtensionPoint ext = findDownloadExtension(context); + if (ext == null) { + trigger.fail(operr("cannot find external primary storage image download extension for encrypted image[uuid:%s]", + image.getUuid())); + return; + } + ext.download(context, new Completion(trigger) { + @Override + public void success() { + trigger.next(); + } + + @Override + public void fail(ErrorCode errorCode) { + trigger.fail(errorCode); + } + }); + } + }); + flow(new NoRollbackFlow() { String __name__ = "download-image"; + @Override + public boolean skip(Map data) { + return spec.isEncrypted(); + } + @Override public void run(FlowTrigger trigger, Map data) { UploadImageToRemoteTargetMsg dmsg = new UploadImageToRemoteTargetMsg();