Skip to content

Commit 29329d8

Browse files
author
kunal.behbudzade
committed
KVM: add UEFI NVRAM plumbing for disk-only instance snapshots
Add the command payload, answer metadata, and host capability plumbing required for KVM disk-only instance snapshots to carry UEFI NVRAM state between management and the KVM agent. Also synchronize host capability booleans on reconnect so stale UEFI/NVRAM support details are removed when an older agent reconnects.
1 parent be89e6f commit 29329d8

8 files changed

Lines changed: 119 additions & 7 deletions

File tree

api/src/main/java/com/cloud/host/Host.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ public static String[] toStrings(Host.Type... types) {
5555
}
5656

5757
String HOST_UEFI_ENABLE = "host.uefi.enable";
58+
String HOST_KVM_DISK_ONLY_VM_SNAPSHOT_NVRAM = "host.kvm.diskonlyvmsnapshot.nvram";
5859
String HOST_VOLUME_ENCRYPTION = "host.volume.encryption";
5960
String HOST_INSTANCE_CONVERSION = "host.instance.conversion";
6061
String HOST_VDDK_SUPPORT = "host.vddk.support";

core/src/main/java/com/cloud/agent/api/storage/CreateDiskOnlyVmSnapshotAnswer.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,24 @@
2727
public class CreateDiskOnlyVmSnapshotAnswer extends Answer {
2828

2929
protected Map<String, Pair<Long, String>> mapVolumeToSnapshotSizeAndNewVolumePath;
30+
private String nvramSnapshotPath;
3031

3132
public CreateDiskOnlyVmSnapshotAnswer(Command command, boolean success, String details, Map<String, Pair<Long, String>> mapVolumeToSnapshotSizeAndNewVolumePath) {
33+
this(command, success, details, mapVolumeToSnapshotSizeAndNewVolumePath, null);
34+
}
35+
36+
public CreateDiskOnlyVmSnapshotAnswer(Command command, boolean success, String details, Map<String, Pair<Long, String>> mapVolumeToSnapshotSizeAndNewVolumePath,
37+
String nvramSnapshotPath) {
3238
super(command, success, details);
3339
this.mapVolumeToSnapshotSizeAndNewVolumePath = mapVolumeToSnapshotSizeAndNewVolumePath;
40+
this.nvramSnapshotPath = nvramSnapshotPath;
3441
}
3542

3643
public Map<String, Pair<Long, String>> getMapVolumeToSnapshotSizeAndNewVolumePath() {
3744
return mapVolumeToSnapshotSizeAndNewVolumePath;
3845
}
46+
47+
public String getNvramSnapshotPath() {
48+
return nvramSnapshotPath;
49+
}
3950
}

core/src/main/java/com/cloud/agent/api/storage/CreateDiskOnlyVmSnapshotCommand.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,30 @@
2929
public class CreateDiskOnlyVmSnapshotCommand extends VMSnapshotBaseCommand {
3030

3131
protected VirtualMachine.State vmState;
32+
private final String vmUuid;
33+
private final boolean uefiEnabled;
3234

3335
public CreateDiskOnlyVmSnapshotCommand(String vmName, VMSnapshotTO snapshot, List<VolumeObjectTO> volumeTOs, String guestOSType, VirtualMachine.State vmState) {
36+
this(vmName, null, snapshot, volumeTOs, guestOSType, vmState, false);
37+
}
38+
39+
public CreateDiskOnlyVmSnapshotCommand(String vmName, String vmUuid, VMSnapshotTO snapshot, List<VolumeObjectTO> volumeTOs, String guestOSType,
40+
VirtualMachine.State vmState, boolean uefiEnabled) {
3441
super(vmName, snapshot, volumeTOs, guestOSType);
42+
this.vmUuid = vmUuid;
3543
this.vmState = vmState;
44+
this.uefiEnabled = uefiEnabled;
3645
}
3746

3847
public VirtualMachine.State getVmState() {
3948
return vmState;
4049
}
50+
51+
public String getVmUuid() {
52+
return vmUuid;
53+
}
54+
55+
public boolean isUefiEnabled() {
56+
return uefiEnabled;
57+
}
4158
}

core/src/main/java/com/cloud/agent/api/storage/DeleteDiskOnlyVmSnapshotCommand.java

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,24 +19,43 @@
1919
package com.cloud.agent.api.storage;
2020

2121
import com.cloud.agent.api.Command;
22-
2322
import com.cloud.agent.api.to.DataTO;
24-
23+
import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
2524

2625
import java.util.List;
2726

2827
public class DeleteDiskOnlyVmSnapshotCommand extends Command {
2928

30-
List<DataTO> snapshots;
29+
private final List<DataTO> snapshots;
30+
private final String nvramSnapshotPath;
31+
private final PrimaryDataStoreTO primaryDataStore;
3132

3233
public DeleteDiskOnlyVmSnapshotCommand(List<DataTO> snapshots) {
34+
this(snapshots, null);
35+
}
36+
37+
public DeleteDiskOnlyVmSnapshotCommand(List<DataTO> snapshots, String nvramSnapshotPath) {
38+
this(snapshots, nvramSnapshotPath, null);
39+
}
40+
41+
public DeleteDiskOnlyVmSnapshotCommand(List<DataTO> snapshots, String nvramSnapshotPath, PrimaryDataStoreTO primaryDataStore) {
3342
this.snapshots = snapshots;
43+
this.nvramSnapshotPath = nvramSnapshotPath;
44+
this.primaryDataStore = primaryDataStore;
3445
}
3546

3647
public List<DataTO> getSnapshots() {
3748
return snapshots;
3849
}
3950

51+
public String getNvramSnapshotPath() {
52+
return nvramSnapshotPath;
53+
}
54+
55+
public PrimaryDataStoreTO getPrimaryDataStore() {
56+
return primaryDataStore;
57+
}
58+
4059
@Override
4160
public boolean executeInSequence() {
4261
return false;

core/src/main/java/com/cloud/agent/api/storage/RevertDiskOnlyVmSnapshotCommand.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,21 @@ public class RevertDiskOnlyVmSnapshotCommand extends Command {
2727

2828
private List<SnapshotObjectTO> snapshotObjectTos;
2929
private String vmName;
30+
private final String vmUuid;
31+
private final boolean uefiEnabled;
32+
private final String nvramSnapshotPath;
3033

3134
public RevertDiskOnlyVmSnapshotCommand(List<SnapshotObjectTO> snapshotObjectTos, String vmName) {
35+
this(snapshotObjectTos, vmName, null, false, null);
36+
}
37+
38+
public RevertDiskOnlyVmSnapshotCommand(List<SnapshotObjectTO> snapshotObjectTos, String vmName, String vmUuid, boolean uefiEnabled, String nvramSnapshotPath) {
3239
super();
3340
this.snapshotObjectTos = snapshotObjectTos;
3441
this.vmName = vmName;
42+
this.vmUuid = vmUuid;
43+
this.uefiEnabled = uefiEnabled;
44+
this.nvramSnapshotPath = nvramSnapshotPath;
3545
}
3646

3747
public List<SnapshotObjectTO> getSnapshotObjectTos() {
@@ -42,6 +52,18 @@ public String getVmName() {
4252
return vmName;
4353
}
4454

55+
public String getVmUuid() {
56+
return vmUuid;
57+
}
58+
59+
public boolean isUefiEnabled() {
60+
return uefiEnabled;
61+
}
62+
63+
public String getNvramSnapshotPath() {
64+
return nvramSnapshotPath;
65+
}
66+
4567
@Override
4668
public boolean executeInSequence() {
4769
return false;

engine/orchestration/src/main/java/com/cloud/agent/manager/AgentManagerImpl.java

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,10 +108,12 @@
108108
import com.cloud.exception.UnsupportedVersionException;
109109
import com.cloud.ha.HighAvailabilityManager;
110110
import com.cloud.host.Host;
111+
import com.cloud.host.DetailVO;
111112
import com.cloud.host.HostVO;
112113
import com.cloud.host.Status;
113114
import com.cloud.host.Status.Event;
114115
import com.cloud.host.dao.HostDao;
116+
import com.cloud.host.dao.HostDetailsDao;
115117
import com.cloud.hypervisor.Hypervisor.HypervisorType;
116118
import com.cloud.hypervisor.HypervisorGuruManager;
117119
import com.cloud.org.Cluster;
@@ -167,6 +169,8 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl
167169
@Inject
168170
protected HostDao _hostDao = null;
169171
@Inject
172+
protected HostDetailsDao _hostDetailsDao = null;
173+
@Inject
170174
private ManagementServerHostDao _mshostDao;
171175
@Inject
172176
protected OutOfBandManagementDao outOfBandManagementDao;
@@ -802,18 +806,24 @@ protected AgentAttache notifyMonitorsOfConnection(final AgentAttache attache, fi
802806
ReadyAnswer readyAnswer = (ReadyAnswer)answer;
803807
Map<String, String> detailsMap = readyAnswer.getDetailsMap();
804808
if (detailsMap != null) {
809+
_hostDao.loadDetails(host);
810+
if (host.getDetails() == null) {
811+
host.setDetails(new HashMap<>());
812+
}
805813
String uefiEnabled = detailsMap.get(Host.HOST_UEFI_ENABLE);
814+
String diskOnlyVmSnapshotNvramSupport = detailsMap.get(Host.HOST_KVM_DISK_ONLY_VM_SNAPSHOT_NVRAM);
806815
String virtv2vVersion = detailsMap.get(Host.HOST_VIRTV2V_VERSION);
807816
String ovftoolVersion = detailsMap.get(Host.HOST_OVFTOOL_VERSION);
808817
String vddkSupport = detailsMap.get(Host.HOST_VDDK_SUPPORT);
809818
String vddkLibDir = detailsMap.get(Host.HOST_VDDK_LIB_DIR);
810819
String vddkVersion = detailsMap.get(Host.HOST_VDDK_VERSION);
811820
logger.debug("Got HOST_UEFI_ENABLE [{}] for host [{}]:", uefiEnabled, host);
812-
if (ObjectUtils.anyNotNull(uefiEnabled, virtv2vVersion, ovftoolVersion, vddkSupport, vddkLibDir, vddkVersion)) {
813-
_hostDao.loadDetails(host);
821+
if (ObjectUtils.anyNotNull(uefiEnabled, diskOnlyVmSnapshotNvramSupport, virtv2vVersion, ovftoolVersion, vddkSupport, vddkLibDir, vddkVersion)) {
814822
boolean updateNeeded = false;
815-
if (StringUtils.isNotBlank(uefiEnabled) && !uefiEnabled.equals(host.getDetails().get(Host.HOST_UEFI_ENABLE))) {
816-
host.getDetails().put(Host.HOST_UEFI_ENABLE, uefiEnabled);
823+
if (syncBooleanHostCapability(host, Host.HOST_UEFI_ENABLE, uefiEnabled)) {
824+
updateNeeded = true;
825+
}
826+
if (syncBooleanHostCapability(host, Host.HOST_KVM_DISK_ONLY_VM_SNAPSHOT_NVRAM, diskOnlyVmSnapshotNvramSupport)) {
817827
updateNeeded = true;
818828
}
819829
if (StringUtils.isNotBlank(virtv2vVersion) && !virtv2vVersion.equals(host.getDetails().get(Host.HOST_VIRTV2V_VERSION))) {
@@ -856,6 +866,26 @@ protected AgentAttache notifyMonitorsOfConnection(final AgentAttache attache, fi
856866
return attache;
857867
}
858868

869+
protected boolean syncBooleanHostCapability(HostVO host, String capabilityName, String advertisedValue) {
870+
if (StringUtils.isNotBlank(advertisedValue)) {
871+
if (!advertisedValue.equals(host.getDetails().get(capabilityName))) {
872+
host.getDetails().put(capabilityName, advertisedValue);
873+
return true;
874+
}
875+
return false;
876+
}
877+
878+
if (host.getDetails().containsKey(capabilityName)) {
879+
host.getDetails().remove(capabilityName);
880+
DetailVO hostDetail = _hostDetailsDao.findDetail(host.getId(), capabilityName);
881+
if (hostDetail != null) {
882+
_hostDetailsDao.remove(hostDetail.getId());
883+
}
884+
return true;
885+
}
886+
return false;
887+
}
888+
859889
@Override
860890
public boolean start() {
861891
ManagementServerHostVO msHost = _mshostDao.findByMsid(_nodeId);

plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package com.cloud.hypervisor.kvm.resource;
1818

1919
import static com.cloud.host.Host.HOST_INSTANCE_CONVERSION;
20+
import static com.cloud.host.Host.HOST_KVM_DISK_ONLY_VM_SNAPSHOT_NVRAM;
2021
import static com.cloud.host.Host.HOST_OVFTOOL_VERSION;
2122
import static com.cloud.host.Host.HOST_VDDK_LIB_DIR;
2223
import static com.cloud.host.Host.HOST_VDDK_SUPPORT;
@@ -4265,6 +4266,7 @@ public StartupCommand[] initialize() {
42654266
privateIp = cmd.getPrivateIpAddress();
42664267
cmd.getHostDetails().putAll(getVersionStrings());
42674268
cmd.getHostDetails().put(KeyStoreUtils.SECURED, String.valueOf(isHostSecured()).toLowerCase());
4269+
cmd.getHostDetails().put(HOST_KVM_DISK_ONLY_VM_SNAPSHOT_NVRAM, Boolean.TRUE.toString());
42684270
cmd.setPool(pool);
42694271
cmd.setCluster(clusterId);
42704272
cmd.setGatewayIpAddress(localGateway);
@@ -6334,6 +6336,15 @@ public String getSnapshotTemporaryPath(String diskPath, String snapshotName) {
63346336
return String.join(File.separator, diskPathSplitted);
63356337
}
63366338

6339+
public String getUefiNvramPath(String vmUuid) {
6340+
String nvramDirectory = uefiProperties.getProperty(LibvirtVMDef.GuestDef.GUEST_NVRAM_PATH);
6341+
if (StringUtils.isBlank(nvramDirectory) || StringUtils.isBlank(vmUuid)) {
6342+
return null;
6343+
}
6344+
6345+
return nvramDirectory + vmUuid + ".fd";
6346+
}
6347+
63376348
public static String generateSecretUUIDFromString(String seed) {
63386349
return UuidUtils.nameUUIDFromBytes(seed.getBytes()).toString();
63396350
}

plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtReadyCommandWrapper.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ public final class LibvirtReadyCommandWrapper extends CommandWrapper<ReadyComman
4343
@Override
4444
public Answer execute(final ReadyCommand command, final LibvirtComputingResource libvirtComputingResource) {
4545
Map<String, String> hostDetails = new HashMap<String, String>();
46+
hostDetails.put(Host.HOST_KVM_DISK_ONLY_VM_SNAPSHOT_NVRAM, Boolean.TRUE.toString());
4647

4748
if (hostSupportsUefi(libvirtComputingResource.isUbuntuOrDebianHost()) && libvirtComputingResource.isUefiPropertiesFileLoaded()) {
4849
hostDetails.put(Host.HOST_UEFI_ENABLE, Boolean.TRUE.toString());

0 commit comments

Comments
 (0)