diff --git a/compute/src/main/java/org/zstack/compute/vm/devices/VmTpmExtensions.java b/compute/src/main/java/org/zstack/compute/vm/devices/VmTpmExtensions.java
index 4b25befa82f..5c5baf10732 100644
--- a/compute/src/main/java/org/zstack/compute/vm/devices/VmTpmExtensions.java
+++ b/compute/src/main/java/org/zstack/compute/vm/devices/VmTpmExtensions.java
@@ -1,12 +1,18 @@
package org.zstack.compute.vm.devices;
import org.springframework.beans.factory.annotation.Autowired;
+import org.zstack.compute.vm.BuildVmSpecExtensionPoint;
import org.zstack.header.vm.CreateVmInstanceMsg;
+import org.zstack.header.vm.DiskAO;
import org.zstack.header.vm.VmInstanceCreateExtensionPoint;
+import org.zstack.header.vm.VmInstanceSpec;
import org.zstack.header.vm.VmInstanceVO;
import org.zstack.header.vm.devices.VmDevicesSpec;
-public class VmTpmExtensions implements VmInstanceCreateExtensionPoint {
+import static org.zstack.header.vm.VmInstanceConstant.NVRAM_DEFAULT_SIZE;
+
+public class VmTpmExtensions implements VmInstanceCreateExtensionPoint,
+ BuildVmSpecExtensionPoint {
@Autowired
private VmTpmManager vmTpmManager;
@@ -24,4 +30,17 @@ public void afterPersistVmInstanceVO(VmInstanceVO vo, CreateVmInstanceMsg msg) {
vmTpmManager.persistTpmVO(null, vo.getUuid());
}
+
+ @Override
+ public void afterBuildVmSpec(VmInstanceSpec spec) {
+ String vmUuid = spec.getVmInventory().getUuid();
+ if (!vmTpmManager.needRegisterNvram(vmUuid)) {
+ return;
+ }
+
+ DiskAO nvramSpec = new DiskAO();
+ nvramSpec.setSize(NVRAM_DEFAULT_SIZE);
+ nvramSpec.setName("nvram-of-VM-" + vmUuid);
+ spec.setNvRamSpec(nvramSpec);
+ }
}
diff --git a/compute/src/main/java/org/zstack/compute/vm/devices/VmTpmManager.java b/compute/src/main/java/org/zstack/compute/vm/devices/VmTpmManager.java
index a37e57d8426..6381bf4b10d 100644
--- a/compute/src/main/java/org/zstack/compute/vm/devices/VmTpmManager.java
+++ b/compute/src/main/java/org/zstack/compute/vm/devices/VmTpmManager.java
@@ -1,17 +1,29 @@
package org.zstack.compute.vm.devices;
import org.springframework.beans.factory.annotation.Autowired;
+import org.zstack.compute.vm.VmSystemTags;
import org.zstack.core.Platform;
import org.zstack.core.db.DatabaseFacade;
+import org.zstack.core.db.Q;
+import org.zstack.header.image.ImageBootMode;
import org.zstack.header.tpm.entity.TpmVO;
+import org.zstack.header.tpm.entity.TpmVO_;
+import org.zstack.resourceconfig.ResourceConfig;
+import org.zstack.resourceconfig.ResourceConfigFacade;
import org.zstack.utils.Utils;
import org.zstack.utils.logging.CLogger;
+import java.util.Objects;
+
+import static org.zstack.compute.vm.VmGlobalConfig.ENABLE_UEFI_SECURE_BOOT;
+
public class VmTpmManager {
private static final CLogger logger = Utils.getLogger(VmTpmManager.class);
@Autowired
private DatabaseFacade databaseFacade;
+ @Autowired
+ private ResourceConfigFacade resourceConfigFacade;
public TpmVO persistTpmVO(String tpmUuid, String vmUuid) {
if (tpmUuid == null) {
@@ -26,4 +38,29 @@ public TpmVO persistTpmVO(String tpmUuid, String vmUuid) {
logger.debug("Persisted TpmVO for VM " + vmUuid + " with uuid=" + tpm.getUuid());
return tpm;
}
+
+ public boolean needRegisterNvram(String vmUuid) {
+ boolean tpmExists = Q.New(TpmVO.class)
+ .eq(TpmVO_.vmInstanceUuid, vmUuid)
+ .isExists();
+ if (tpmExists) {
+ return true;
+ }
+
+ String bootMode = VmSystemTags.BOOT_MODE.getTokenByResourceUuid(vmUuid, VmSystemTags.BOOT_MODE_TOKEN);
+ if (!isUefiBootMode(bootMode)) {
+ return false;
+ }
+
+ ResourceConfig resourceConfig = resourceConfigFacade.getResourceConfig(ENABLE_UEFI_SECURE_BOOT.getIdentity());
+ return resourceConfig.getResourceConfigValue(vmUuid, Boolean.class) == Boolean.TRUE;
+ }
+
+ /**
+ * @param bootMode boot mode, null is Legacy
+ */
+ public static boolean isUefiBootMode(String bootMode) {
+ return Objects.equals(bootMode, ImageBootMode.UEFI.toString())
+ || Objects.equals(bootMode, ImageBootMode.UEFI_WITH_CSM.toString());
+ }
}
diff --git a/conf/persistence.xml b/conf/persistence.xml
index ff82022f554..5a1b855d9e8 100755
--- a/conf/persistence.xml
+++ b/conf/persistence.xml
@@ -18,6 +18,8 @@
org.zstack.resourceconfig.ResourceConfigVO
org.zstack.header.managementnode.ManagementNodeVO
org.zstack.header.managementnode.ManagementNodeContextVO
+ org.zstack.header.tpm.entity.TpmHostRefVO
+ org.zstack.header.tpm.entity.TpmVO
org.zstack.header.zone.ZoneVO
org.zstack.header.zone.ZoneEO
org.zstack.header.cluster.ClusterVO
diff --git a/conf/springConfigXml/Kvm.xml b/conf/springConfigXml/Kvm.xml
index 9536f559eb3..16cd80fadf9 100755
--- a/conf/springConfigXml/Kvm.xml
+++ b/conf/springConfigXml/Kvm.xml
@@ -261,6 +261,7 @@
+
diff --git a/conf/springConfigXml/VmInstanceManager.xml b/conf/springConfigXml/VmInstanceManager.xml
index 6786f86c780..1ac69cd5e1c 100755
--- a/conf/springConfigXml/VmInstanceManager.xml
+++ b/conf/springConfigXml/VmInstanceManager.xml
@@ -285,6 +285,7 @@
+
diff --git a/header/src/main/java/org/zstack/header/vm/VmInstanceConstant.java b/header/src/main/java/org/zstack/header/vm/VmInstanceConstant.java
index 9d0efdd77f1..64520fada14 100755
--- a/header/src/main/java/org/zstack/header/vm/VmInstanceConstant.java
+++ b/header/src/main/java/org/zstack/header/vm/VmInstanceConstant.java
@@ -1,6 +1,7 @@
package org.zstack.header.vm;
import org.zstack.header.configuration.PythonClass;
+import org.zstack.utils.data.SizeUnit;
@PythonClass
public interface VmInstanceConstant {
@@ -25,6 +26,8 @@ public interface VmInstanceConstant {
String SHUTDOWN_DETAIL_BY_GUEST = "by guest";
String SHUTDOWN_DETAIL_FINISHED = "finished";
+ long NVRAM_DEFAULT_SIZE = SizeUnit.MEGABYTE.toByte(1);
+
enum Params {
VmInstanceSpec,
AttachingVolumeInventory,
diff --git a/header/src/main/java/org/zstack/header/vm/VmInstanceSpec.java b/header/src/main/java/org/zstack/header/vm/VmInstanceSpec.java
index 18620a65fad..95ccdc22bf2 100755
--- a/header/src/main/java/org/zstack/header/vm/VmInstanceSpec.java
+++ b/header/src/main/java/org/zstack/header/vm/VmInstanceSpec.java
@@ -405,6 +405,7 @@ public void setCandidatePrimaryStorageUuidsForDataVolume(List candidateP
private List deprecatedDisksSpecs = new ArrayList<>();
private VmCustomSpecificationStruct vmCustomSpecification;
private VmDevicesSpec devicesSpec;
+ private DiskAO nvRamSpec;
public DiskAO getRootDisk() {
return rootDisk;
@@ -446,6 +447,14 @@ public void setDevicesSpec(VmDevicesSpec devicesSpec) {
this.devicesSpec = devicesSpec;
}
+ public DiskAO getNvRamSpec() {
+ return nvRamSpec;
+ }
+
+ public void setNvRamSpec(DiskAO nvRamSpec) {
+ this.nvRamSpec = nvRamSpec;
+ }
+
public boolean isSkipIpAllocation() {
return skipIpAllocation;
}
diff --git a/header/src/main/java/org/zstack/header/volume/VolumeType.java b/header/src/main/java/org/zstack/header/volume/VolumeType.java
index e373e5aef98..88cf6af349e 100755
--- a/header/src/main/java/org/zstack/header/volume/VolumeType.java
+++ b/header/src/main/java/org/zstack/header/volume/VolumeType.java
@@ -4,5 +4,6 @@ public enum VolumeType {
Root,
Data,
Memory,
- Cache
+ Cache,
+ NVRAM,
}
diff --git a/plugin/kvm/src/main/java/org/zstack/kvm/KVMAgentCommands.java b/plugin/kvm/src/main/java/org/zstack/kvm/KVMAgentCommands.java
index 6ec7e3429b7..16865309ec0 100755
--- a/plugin/kvm/src/main/java/org/zstack/kvm/KVMAgentCommands.java
+++ b/plugin/kvm/src/main/java/org/zstack/kvm/KVMAgentCommands.java
@@ -2068,6 +2068,7 @@ public static class StartVmCmd extends vdiCmd implements VmAddOnsCmd {
private List cdRoms = new ArrayList<>();
private List dataVolumes;
private List cacheVolumes;
+ private VolumeTO nvRam;
private List nics;
private TpmTO tpm;
private long timeout;
@@ -2549,6 +2550,14 @@ public void setCacheVolumes(List cacheVolumes) {
this.cacheVolumes = cacheVolumes;
}
+ public VolumeTO getNvRam() {
+ return nvRam;
+ }
+
+ public void setNvRam(VolumeTO nvRam) {
+ this.nvRam = nvRam;
+ }
+
public List getNics() {
return nics;
}
diff --git a/plugin/kvm/src/main/java/org/zstack/kvm/efi/KvmSecureBootExtensions.java b/plugin/kvm/src/main/java/org/zstack/kvm/efi/KvmSecureBootExtensions.java
index 138fbda2d6d..8cb982484f7 100644
--- a/plugin/kvm/src/main/java/org/zstack/kvm/efi/KvmSecureBootExtensions.java
+++ b/plugin/kvm/src/main/java/org/zstack/kvm/efi/KvmSecureBootExtensions.java
@@ -2,45 +2,88 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.zstack.compute.vm.VmGlobalConfig;
+import org.zstack.compute.vm.devices.VmTpmManager;
+import org.zstack.core.cloudbus.CloudBus;
+import org.zstack.core.cloudbus.CloudBusCallBack;
+import org.zstack.core.db.Q;
+import org.zstack.core.workflow.SimpleFlowChain;
+import org.zstack.header.core.Completion;
+import org.zstack.header.core.workflow.FlowDoneHandler;
+import org.zstack.header.core.workflow.FlowErrorHandler;
+import org.zstack.header.core.workflow.FlowRollback;
+import org.zstack.header.core.workflow.FlowTrigger;
+import org.zstack.header.core.workflow.NoRollbackFlow;
import org.zstack.header.errorcode.ErrorCode;
-import org.zstack.header.image.ImageBootMode;
+import org.zstack.header.identity.AccountResourceRefVO;
+import org.zstack.header.identity.AccountResourceRefVO_;
+import org.zstack.header.message.MessageReply;
+import org.zstack.header.vm.DiskAO;
+import org.zstack.header.vm.PreVmInstantiateResourceExtensionPoint;
import org.zstack.header.vm.VmInstanceSpec;
+import org.zstack.header.vm.VmInstantiateResourceException;
+import org.zstack.header.volume.CreateVolumeMsg;
+import org.zstack.header.volume.CreateVolumeReply;
+import org.zstack.header.volume.DeleteVolumeMsg;
+import org.zstack.header.volume.InstantiateVolumeMsg;
+import org.zstack.header.volume.VolumeConstant;
+import org.zstack.header.volume.VolumeDeletionPolicyManager;
+import org.zstack.header.volume.VolumeInventory;
+import org.zstack.header.volume.VolumeType;
+import org.zstack.header.volume.VolumeVO;
+import org.zstack.header.volume.VolumeVO_;
import org.zstack.kvm.KVMAgentCommands;
import org.zstack.kvm.KVMGlobalConfig;
import org.zstack.kvm.KVMHostInventory;
import org.zstack.kvm.KVMStartVmExtensionPoint;
+import org.zstack.kvm.VolumeTO;
import org.zstack.resourceconfig.ResourceConfig;
import org.zstack.resourceconfig.ResourceConfigFacade;
import org.zstack.utils.Utils;
import org.zstack.utils.logging.CLogger;
+import java.util.Map;
import java.util.Objects;
+import static org.zstack.core.Platform.operr;
import static org.zstack.kvm.KVMConstant.EDK_VERSION_NONE;
-public class KvmSecureBootExtensions implements KVMStartVmExtensionPoint {
+public class KvmSecureBootExtensions implements KVMStartVmExtensionPoint,
+ PreVmInstantiateResourceExtensionPoint {
private static final CLogger logger = Utils.getLogger(KvmSecureBootExtensions.class);
+ @Autowired
+ private CloudBus bus;
@Autowired
private ResourceConfigFacade resourceConfigFacade;
@Override
public void beforeStartVmOnKvm(KVMHostInventory host, VmInstanceSpec spec, KVMAgentCommands.StartVmCmd cmd) {
- if (!isUefiBootMode(cmd.getBootMode())) {
- return;
- }
+ if (isUefiBootMode(cmd.getBootMode())) {
+ ResourceConfig resourceConfig;
+ resourceConfig = resourceConfigFacade.getResourceConfig(VmGlobalConfig.ENABLE_UEFI_SECURE_BOOT.getIdentity());
+ cmd.setSecureBoot(resourceConfig.getResourceConfigValue(spec.getVmInventory().getUuid(), Boolean.class));
- ResourceConfig resourceConfig;
- resourceConfig = resourceConfigFacade.getResourceConfig(VmGlobalConfig.ENABLE_UEFI_SECURE_BOOT.getIdentity());
- cmd.setSecureBoot(resourceConfig.getResourceConfigValue(spec.getVmInventory().getUuid(), Boolean.class));
+ resourceConfig = resourceConfigFacade.getResourceConfig(KVMGlobalConfig.VM_EDK_VERSION_CONFIG.getIdentity());
+ final String edkVersion = resourceConfig.getResourceConfigValue(spec.getVmInventory().getUuid(), String.class);
+ if (!Objects.equals(edkVersion, EDK_VERSION_NONE)) {
+ cmd.setEdkVersion(edkVersion);
+ }
+ }
- resourceConfig = resourceConfigFacade.getResourceConfig(KVMGlobalConfig.VM_EDK_VERSION_CONFIG.getIdentity());
- final String edkVersion = resourceConfig.getResourceConfigValue(spec.getVmInventory().getUuid(), String.class);
- if (!Objects.equals(edkVersion, EDK_VERSION_NONE)) {
- cmd.setEdkVersion(edkVersion);
+ if (spec.getNvRamSpec() != null) {
+ prepareNvRamToStartVmCmd(cmd, spec.getNvRamSpec(), host);
}
}
+ private void prepareNvRamToStartVmCmd(KVMAgentCommands.StartVmCmd cmd, DiskAO nvRamSpec, KVMHostInventory host) {
+ VolumeVO vo = Q.New(VolumeVO.class)
+ .eq(VolumeVO_.uuid, nvRamSpec.getSourceUuid())
+ .find();
+ VolumeInventory nvRamVolume = VolumeInventory.valueOf(vo);
+ VolumeTO volume = VolumeTO.valueOfWithOutExtension(nvRamVolume, host, null);
+ cmd.setNvRam(volume);
+ }
+
@Override
public void startVmOnKvmSuccess(KVMHostInventory host, VmInstanceSpec spec) {
// do-nothing
@@ -52,6 +95,208 @@ public void startVmOnKvmFailed(KVMHostInventory host, VmInstanceSpec spec, Error
}
private boolean isUefiBootMode(String bootMode) {
- return bootMode.equals(ImageBootMode.UEFI.toString()) || bootMode.equals(ImageBootMode.UEFI_WITH_CSM.toString());
+ return VmTpmManager.isUefiBootMode(bootMode);
+ }
+
+ @Override
+ public void preBeforeInstantiateVmResource(VmInstanceSpec spec) throws VmInstantiateResourceException {
+ // do-nothing
+ }
+
+ @Override
+ public void preInstantiateVmResource(VmInstanceSpec spec, Completion completion) {
+ final DiskAO nvRamSpec = spec.getNvRamSpec();
+ boolean needRegisterNvRam = nvRamSpec != null;
+
+ String nvRamVolumeUuid = Q.New(VolumeVO.class)
+ .eq(VolumeVO_.vmInstanceUuid, spec.getVmInventory().getUuid())
+ .eq(VolumeVO_.type, VolumeType.NVRAM)
+ .select(VolumeVO_.uuid)
+ .findValue();
+ if (needRegisterNvRam == (nvRamVolumeUuid != null)) {
+ if (nvRamVolumeUuid != null) {
+ nvRamSpec.setSourceUuid(nvRamVolumeUuid);
+ }
+ completion.success(); // TODO make sure status is Ready
+ return;
+ }
+
+ if (needRegisterNvRam) {
+ nvRamSpec.setPrimaryStorageUuid(Q.New(VolumeVO.class)
+ .eq(VolumeVO_.type, VolumeType.Root)
+ .eq(VolumeVO_.vmInstanceUuid, spec.getVmInventory().getUuid())
+ .select(VolumeVO_.primaryStorageUuid)
+ .findValue());
+
+ NvRamVolumeContext context = new NvRamVolumeContext();
+ context.vmUuid = spec.getVmInventory().getUuid();
+ context.nvRamSpec = nvRamSpec;
+ context.spec = spec;
+ createNvRamVolume(context, new Completion(completion) {
+ @Override
+ public void success() {
+ nvRamSpec.setSourceUuid(context.inventory.getUuid());
+ completion.success();
+ }
+
+ @Override
+ public void fail(ErrorCode errorCode) {
+ completion.fail(errorCode);
+ }
+ });
+ return;
+ }
+
+ deleteNvramVolumeIfExists(spec.getVmInventory().getUuid(), new Completion(completion) {
+ @Override
+ public void success() {
+ completion.success();
+ }
+
+ @Override
+ public void fail(ErrorCode errorCode) {
+ logger.warn("failed to delete NVRAM but still continue: " + errorCode.getReadableDetails());
+ completion.success();
+ }
+ });
+ }
+
+ @Override
+ public void preReleaseVmResource(VmInstanceSpec spec, Completion completion) {
+ deleteNvramVolumeIfExists(spec.getVmInventory().getUuid(), completion);
+ }
+
+ static class NvRamVolumeContext {
+ String vmUuid;
+ DiskAO nvRamSpec;
+ VmInstanceSpec spec;
+
+ VolumeInventory inventory;
+ }
+
+ @SuppressWarnings("rawtypes")
+ private void createNvRamVolume(NvRamVolumeContext context, Completion completion) {
+ SimpleFlowChain chain = new SimpleFlowChain();
+ chain.setChainName("create-nvram-volume-for-vm-" + context.vmUuid);
+ chain.then(new NoRollbackFlow() {
+ String __name__ = "create-nvram-volume";
+
+ @Override
+ public void run(FlowTrigger trigger, Map data) {
+ String accountUuid = Q.New(AccountResourceRefVO.class)
+ .eq(AccountResourceRefVO_.resourceUuid, context.vmUuid)
+ .select(AccountResourceRefVO_.accountUuid)
+ .findValue();
+
+ CreateVolumeMsg msg = new CreateVolumeMsg();
+ msg.setAccountUuid(accountUuid);
+ msg.setSize(context.nvRamSpec.getSize());
+ msg.setVmInstanceUuid(context.vmUuid);
+ msg.setPrimaryStorageUuid(context.nvRamSpec.getPrimaryStorageUuid());
+
+ // NVRAM is raw type (*.fd) in libvirt 8.0.0
+ // and qcow2 in libvirt 8.1.0+ (soon)
+ msg.setFormat(VolumeConstant.VOLUME_FORMAT_RAW);
+ msg.setName(context.nvRamSpec.getName());
+ msg.setVolumeType(VolumeType.NVRAM.toString());
+
+ bus.makeLocalServiceId(msg, VolumeConstant.SERVICE_ID);
+ bus.send(msg, new CloudBusCallBack(completion) {
+ @Override
+ public void run(MessageReply reply) {
+ if (reply.isSuccess()) {
+ CreateVolumeReply castReply = reply.castReply();
+ context.inventory = castReply.getInventory();
+ trigger.next();
+ return;
+ }
+ trigger.fail(operr("failed to create NVRAM volume")
+ .withOpaque("vm.uuid", context.vmUuid)
+ .withCause(reply.getError()));
+ }
+ });
+ }
+
+ @Override
+ public void rollback(FlowRollback trigger, Map data) {
+ deleteNvramVolumeIfExists(context.vmUuid, new Completion(trigger) {
+ @Override
+ public void success() {
+ trigger.rollback();
+ }
+
+ @Override
+ public void fail(ErrorCode errorCode) {
+ logger.warn("failed to delete NVRAM but still continue: " + errorCode.getReadableDetails());
+ trigger.rollback();
+ }
+ });
+ }
+ }).then(new NoRollbackFlow() {
+ String __name__ = "instantiate-nvram-volume";
+
+ @Override
+ public void run(FlowTrigger trigger, Map data) {
+ InstantiateVolumeMsg msg = new InstantiateVolumeMsg();
+ msg.setHostUuid(context.spec.getDestHost().getUuid());
+ msg.setPrimaryStorageUuid(context.nvRamSpec.getPrimaryStorageUuid());
+ msg.setVolumeUuid(context.inventory.getUuid());
+
+ bus.makeTargetServiceIdByResourceUuid(msg, VolumeConstant.SERVICE_ID, msg.getVolumeUuid());
+ bus.send(msg, new CloudBusCallBack(completion) {
+ @Override
+ public void run(MessageReply reply) {
+ if (reply.isSuccess()) {
+ trigger.next();
+ return;
+ }
+ trigger.fail(operr("failed to instantiate NVRAM volume")
+ .withOpaque("vm.uuid", context.vmUuid)
+ .withCause(reply.getError()));
+ }
+ });
+ }
+ }).done(new FlowDoneHandler(completion) {
+ @Override
+ public void handle(Map data) {
+ completion.success();
+ }
+ }).error(new FlowErrorHandler(completion) {
+ @Override
+ public void handle(ErrorCode errCode, Map data) {
+ completion.fail(errCode);
+ }
+ }).start();
+ }
+
+ private void deleteNvramVolumeIfExists(String vmUuid, Completion completion) {
+ String volumeUuid = Q.New(VolumeVO.class)
+ .eq(VolumeVO_.vmInstanceUuid, vmUuid)
+ .eq(VolumeVO_.type, VolumeType.NVRAM)
+ .select(VolumeVO_.uuid)
+ .findValue();
+ if (volumeUuid == null) {
+ completion.success();
+ return;
+ }
+
+ DeleteVolumeMsg msg = new DeleteVolumeMsg();
+ msg.setDetachBeforeDeleting(false);
+ msg.setUuid(volumeUuid);
+ msg.setDeletionPolicy(VolumeDeletionPolicyManager.VolumeDeletionPolicy.Direct.toString());
+ bus.makeTargetServiceIdByResourceUuid(msg, VolumeConstant.SERVICE_ID, volumeUuid);
+ bus.send(msg, new CloudBusCallBack(completion) {
+ @Override
+ public void run(MessageReply reply) {
+ if (reply.isSuccess()) {
+ completion.success();
+ return;
+ }
+ completion.fail(operr("failed to delete NVRAM volume")
+ .withOpaque("vm.uuid", vmUuid)
+ .withOpaque("volume.uuid", volumeUuid)
+ .withCause(reply.getError()));
+ }
+ });
}
}
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 e8d268e518a..998bf3b9417 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
@@ -33,6 +33,7 @@
import org.zstack.header.exception.CloudRuntimeException;
import org.zstack.header.host.*;
import org.zstack.header.image.ImageBackupStorageRefInventory;
+import org.zstack.header.image.ImageConstant;
import org.zstack.header.image.ImageConstant.ImageMediaType;
import org.zstack.header.image.ImageInventory;
import org.zstack.header.image.ImageStatus;
@@ -202,6 +203,7 @@ public static class CreateEmptyVolumeCmd extends AgentCommand {
private String name;
private String volumeUuid;
private String backingFile;
+ private String format;
public String getBackingFile() {
return backingFile;
@@ -211,6 +213,14 @@ public void setBackingFile(String backingFile) {
this.backingFile = backingFile;
}
+ public String getFormat() {
+ return format;
+ }
+
+ public void setFormat(String format) {
+ this.format = format;
+ }
+
public String getInstallUrl() {
return installUrl;
}
@@ -963,6 +973,10 @@ public String makeDataVolumeInstallUrl(String volUuid) {
return PathUtil.join(self.getUrl(), PrimaryStoragePathMaker.makeDataVolumeInstallPath(volUuid));
}
+ public String makeNvRamVolumeInstallUrl(String volUuid) {
+ return PathUtil.join(self.getUrl(), PrimaryStoragePathMaker.makeNvRamVolumeInstallPath(volUuid));
+ }
+
public boolean isCachedImageUrl(String path){
return path.startsWith(PathUtil.join(self.getUrl(), PrimaryStoragePathMaker.getCachedImageInstallDir()));
}
@@ -1254,7 +1268,7 @@ public void success(VolumeStats returnValue) {
VolumeInventory vol = msg.getVolume();
vol.setInstallPath(returnValue.getInstallPath());
vol.setActualSize(returnValue.getActualSize());
- vol.setFormat(VolumeConstant.VOLUME_FORMAT_QCOW2);
+ vol.setFormat(returnValue.getFormat());
if (returnValue.getSize() != null) {
vol.setSize(returnValue.getSize());
}
@@ -1279,7 +1293,9 @@ public void createEmptyVolumeWithBackingFile(final VolumeInventory volume, final
cmd.setAccountUuid(acntMgr.getOwnerAccountUuidOfResource(volume.getUuid()));
if (volume.getInstallPath() != null && !volume.getInstallPath().equals("")) {
cmd.setInstallUrl(volume.getInstallPath());
+ cmd.setFormat(ImageConstant.QCOW2_FORMAT_STRING);
} else {
+ cmd.setFormat(ImageConstant.QCOW2_FORMAT_STRING);
if (VolumeType.Root.toString().equals(volume.getType())) {
cmd.setInstallUrl(makeRootVolumeInstallUrl(volume));
} else if (VolumeType.Data.toString().equals(volume.getType())) {
@@ -1288,6 +1304,9 @@ public void createEmptyVolumeWithBackingFile(final VolumeInventory volume, final
cmd.setInstallUrl(makeMemoryVolumeInstallUrl(volume));
} else if (VolumeType.Cache.toString().equals(volume.getType())) {
cmd.setInstallUrl(makeDataVolumeInstallUrl(volume.getUuid()));
+ } else if (VolumeType.NVRAM.toString().equals(volume.getType())) {
+ cmd.setInstallUrl(makeNvRamVolumeInstallUrl(volume.getUuid()));
+ cmd.setFormat(ImageConstant.RAW_FORMAT_STRING);
}
}
cmd.setName(volume.getName());
@@ -1298,7 +1317,9 @@ public void createEmptyVolumeWithBackingFile(final VolumeInventory volume, final
httpCall(CREATE_EMPTY_VOLUME_PATH, hostUuid, cmd, CreateEmptyVolumeRsp.class, new ReturnValueCompletion(completion) {
@Override
public void success(CreateEmptyVolumeRsp returnValue) {
- completion.success(new VolumeStats(cmd.getInstallUrl(), returnValue.actualSize, returnValue.size));
+ final VolumeStats stats = new VolumeStats(cmd.getInstallUrl(), returnValue.actualSize, returnValue.size);
+ stats.setFormat(cmd.getFormat());
+ completion.success(stats);
}
@Override
diff --git a/storage/src/main/java/org/zstack/storage/primary/PrimaryStoragePathMaker.java b/storage/src/main/java/org/zstack/storage/primary/PrimaryStoragePathMaker.java
index 7ef4b4dcd24..a4346f3e28c 100755
--- a/storage/src/main/java/org/zstack/storage/primary/PrimaryStoragePathMaker.java
+++ b/storage/src/main/java/org/zstack/storage/primary/PrimaryStoragePathMaker.java
@@ -43,6 +43,10 @@ public static String makeDataVolumeInstallPath(String volUuid) {
return PathUtil.join("dataVolumes", "acct-" + getAccountUuidOfResource(volUuid), "vol-" + volUuid, volUuid + ".qcow2");
}
+ public static String makeNvRamVolumeInstallPath(String volUuid) {
+ return PathUtil.join("nvRam", "acct-" + getAccountUuidOfResource(volUuid), "vol-" + volUuid, volUuid + ".xfs");
+ }
+
public static String makeImageFromSnapshotWorkspacePath(String imageUuid) {
return PathUtil.join("snapshotWorkspace", String.format("image-%s", imageUuid));
}
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 272d75d7a09..286fed030c3 100755
--- a/storage/src/main/java/org/zstack/storage/volume/VolumeBase.java
+++ b/storage/src/main/java/org/zstack/storage/volume/VolumeBase.java
@@ -551,7 +551,8 @@ public void fail(ErrorCode errorCode) {
} else if (msg instanceof InstantiateMemoryVolumeMsg) {
instantiateMemoryVolume(msg, trigger);
} else {
- instantiateDataVolume(msg, trigger);
+ // include: data volume, NVRAM, TpmState
+ instantiateOtherVolume(msg, trigger);
}
}
}
@@ -593,7 +594,7 @@ private void instantiateRootVolume(InstantiateRootVolumeMsg msg, FlowTrigger tri
doInstantiateVolume(imsg, trigger);
}
- private void instantiateDataVolume(InstantiateVolumeMsg msg, FlowTrigger trigger) {
+ private void instantiateOtherVolume(InstantiateVolumeMsg msg, FlowTrigger trigger) {
InstantiateVolumeOnPrimaryStorageMsg imsg = new InstantiateVolumeOnPrimaryStorageMsg();
prepareMsg(msg, imsg);
doInstantiateVolume(imsg, trigger);
diff --git a/test/src/test/resources/springConfigXml/Kvm.xml b/test/src/test/resources/springConfigXml/Kvm.xml
index 462aa352dc8..4e57880a7ce 100755
--- a/test/src/test/resources/springConfigXml/Kvm.xml
+++ b/test/src/test/resources/springConfigXml/Kvm.xml
@@ -260,6 +260,7 @@
+