Skip to content

Commit de81066

Browse files
author
Zhang Wenhao
committed
<fix>[kvm]: mark TPM VM host files changed on start/shutdown
When a VM with TPM starts or shuts down, NvRam/TpmState data must have changed. Preemptively set changeDate on the corresponding VmHostFileVO so the periodic tracker knows to sync them, even if the direct sync fails. Also add ResourceDestinationMaker check to all VM canonical event handlers in KvmSecureBootManager to ensure only the owning management node processes each event. Resolves: ZSV-11779 Related: ZSV-11310 Change-Id: I6e6d6c6c647175716669756b75756a6f72657277
1 parent 449ef01 commit de81066

3 files changed

Lines changed: 119 additions & 39 deletions

File tree

plugin/kvm/src/main/java/org/zstack/kvm/efi/KvmSecureBootExtensions.java

Lines changed: 16 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import org.apache.commons.lang.StringUtils;
44
import org.springframework.beans.factory.annotation.Autowired;
55
import org.zstack.compute.vm.VmGlobalConfig;
6-
import org.zstack.compute.vm.VmSystemTags;
76
import org.zstack.compute.vm.devices.VmTpmManager;
87
import org.zstack.core.Platform;
98
import org.zstack.core.cloudbus.CloudBus;
@@ -23,8 +22,6 @@
2322
import org.zstack.header.errorcode.ErrorCode;
2423
import org.zstack.header.exception.CloudRuntimeException;
2524
import org.zstack.header.message.MessageReply;
26-
import org.zstack.header.tpm.entity.TpmVO;
27-
import org.zstack.header.tpm.entity.TpmVO_;
2825
import org.zstack.header.vm.DiskAO;
2926
import org.zstack.header.vm.PreVmInstantiateResourceExtensionPoint;
3027
import org.zstack.header.vm.VmInstanceVO;
@@ -79,14 +76,14 @@
7976
import java.util.Map;
8077
import java.util.Objects;
8178

82-
import static org.zstack.compute.vm.VmGlobalConfig.ENABLE_UEFI_SECURE_BOOT;
8379
import static org.zstack.core.Platform.operr;
8480
import static org.zstack.header.vm.additions.VmHostFileSyncReason.PostMigration;
8581
import static org.zstack.header.vm.additions.VmHostFileSyncReason.BeforeHaStart;
8682
import static org.zstack.header.vm.additions.VmHostFileSyncReason.PrepareReRead;
8783
import static org.zstack.header.vm.additions.VmHostFileSyncReason.PrepareRead;
8884
import static org.zstack.header.vm.additions.VmHostFileSyncReason.ResourceRelease;
8985
import static org.zstack.header.vm.additions.VmHostFileSyncReason.SnapshotGroupOnlineBackup;
86+
import static org.zstack.header.vm.additions.VmHostFileType.NvRam;
9087
import static org.zstack.kvm.KVMConstant.*;
9188
import static org.zstack.utils.CollectionDSL.list;
9289

@@ -107,6 +104,8 @@ public class KvmSecureBootExtensions implements KVMStartVmExtensionPoint,
107104
private ResourceConfigFacade resourceConfigFacade;
108105
@Autowired
109106
private DatabaseFacade databaseFacade;
107+
@Autowired
108+
private KvmVmHostFileFactory vmHostFileFactory;
110109

111110
private final Object hostFileLock = new Object();
112111

@@ -141,15 +140,15 @@ private void prepareNvRamToStartVmCmd(KVMAgentCommands.StartVmCmd cmd, NvRamSpec
141140
final Timestamp now = Timestamp.from(Instant.now());
142141
VmHostFileVO nvRamFile = Q.New(VmHostFileVO.class)
143142
.eq(VmHostFileVO_.vmInstanceUuid, cmd.getVmInstanceUuid())
144-
.eq(VmHostFileVO_.type, VmHostFileType.NvRam)
143+
.eq(VmHostFileVO_.type, NvRam)
145144
.eq(VmHostFileVO_.hostUuid, host.getUuid())
146145
.find();
147146
if (nvRamFile == null) {
148147
nvRamFile = new VmHostFileVO();
149148
nvRamFile.setUuid(Platform.getUuid());
150149
nvRamFile.setHostUuid(host.getUuid());
151150
nvRamFile.setVmInstanceUuid(cmd.getVmInstanceUuid());
152-
nvRamFile.setType(VmHostFileType.NvRam);
151+
nvRamFile.setType(NvRam);
153152
nvRamFile.setPath(volume.getInstallPath());
154153
nvRamFile.setCreateDate(now);
155154
nvRamFile.setResourceName("NvRam file for " + cmd.getVmInstanceUuid());
@@ -180,29 +179,17 @@ private void prepareNvRamBeforeMigration(VmInstanceInventory vm, String dstHostU
180179
return;
181180
}
182181

183-
String tpmUuid = Q.New(TpmVO.class)
184-
.eq(TpmVO_.vmInstanceUuid, vm.getUuid())
185-
.select(TpmVO_.uuid)
186-
.findValue();
187-
boolean needRegisterNvRam = tpmUuid != null;
182+
boolean needRegisterNvRam = vmHostFileFactory.needRegister(NvRam, vm.getUuid());
188183
if (!needRegisterNvRam) {
189-
String bootMode = VmSystemTags.BOOT_MODE.getTokenByResourceUuid(vm.getUuid(), VmSystemTags.BOOT_MODE_TOKEN);
190-
if (isUefiBootMode(bootMode)) {
191-
ResourceConfig resourceConfig = resourceConfigFacade.getResourceConfig(ENABLE_UEFI_SECURE_BOOT.getIdentity());
192-
needRegisterNvRam = resourceConfig.getResourceConfigValue(vm.getUuid(), Boolean.class) == Boolean.TRUE;
193-
}
194-
195-
if (!needRegisterNvRam) {
196-
completion.success();
197-
return;
198-
}
184+
completion.success();
185+
return;
199186
}
200187

201188
SimpleFlowChain.of("prepare-nvram-before-vm-" + vm.getUuid() + "-migrate")
202189
.then("prepare-nvram-folder-on-dest-host", trigger -> {
203190
VmHostFileTO to = new VmHostFileTO();
204191
to.setPath(buildNvramFilePath(vm.getUuid()));
205-
to.setType(VmHostFileType.NvRam.toString());
192+
to.setType(NvRam.toString());
206193
to.setOperation(VmHostFileOperation.Prepare.toString());
207194

208195
RewriteVmHostFilesContext context = new RewriteVmHostFilesContext();
@@ -290,7 +277,7 @@ public void preInstantiateVmResource(VmInstanceSpec spec, Completion completion)
290277
PrepareHostFileContext context = new PrepareHostFileContext();
291278
context.hostUuid = spec.getDestHost().getUuid();
292279
context.vmUuid = spec.getVmInventory().getUuid();
293-
context.type = VmHostFileType.NvRam;
280+
context.type = NvRam;
294281
context.backupUuid = nvRamSpec.getBackupFileUuid();
295282
context.syncReason = "pre-instantiate VM resource";
296283
prepareHostFileOnHost(context, completion);
@@ -349,7 +336,7 @@ public void run(FlowTrigger trigger, Map data) {
349336
syncMsg.setVmUuid(context.vmUuid);
350337
syncMsg.setSyncReason(PrepareRead.reason(context.syncReason));
351338

352-
if (vmHostFile.getType() == VmHostFileType.NvRam) {
339+
if (vmHostFile.getType() == NvRam) {
353340
context.path = vmHostFile.getPath();
354341
syncMsg.setNvRamPath(context.path);
355342
} else if (vmHostFile.getType() == VmHostFileType.TpmState) {
@@ -471,7 +458,7 @@ public void run(FlowTrigger trigger, Map data) {
471458
syncMsg.setVmUuid(context.vmUuid);
472459
syncMsg.setSyncReason(PrepareReRead.reason(context.syncReason));
473460

474-
if (context.type == VmHostFileType.NvRam) {
461+
if (context.type == NvRam) {
475462
syncMsg.setNvRamPath(context.path);
476463
} else if (context.type == VmHostFileType.TpmState) {
477464
syncMsg.setTpmStateFolder(context.path);
@@ -574,7 +561,7 @@ public void releaseVmResource(VmInstanceSpec spec, Completion completion) {
574561
syncMsg.setSyncReason(ResourceRelease.reason());
575562

576563
for (VmHostFileVO file : vmHostFiles) {
577-
if (file.getType() == VmHostFileType.NvRam) {
564+
if (file.getType() == NvRam) {
578565
syncMsg.setNvRamPath(file.getPath());
579566
} else if (file.getType() == VmHostFileType.TpmState) {
580567
syncMsg.setTpmStateFolder(file.getPath());
@@ -633,7 +620,7 @@ public void beforeHaStartVmInstance(String vmUuid, String judgerClassName, List<
633620
syncMsg.setSyncReason(BeforeHaStart.reason());
634621

635622
for (VmHostFileVO file : vmHostFiles) {
636-
if (file.getType() == VmHostFileType.NvRam) {
623+
if (file.getType() == NvRam) {
637624
syncMsg.setNvRamPath(file.getPath());
638625
} else if (file.getType() == VmHostFileType.TpmState) {
639626
syncMsg.setTpmStateFolder(file.getPath());
@@ -699,7 +686,7 @@ public void afterMigrateVm(VmInstanceInventory inv, String srcHostUuid, NoErrorC
699686
syncMsg.setSyncReason(PostMigration.reason());
700687

701688
for (VmHostFileVO file : vmHostFiles) {
702-
if (file.getType() == VmHostFileType.NvRam) {
689+
if (file.getType() == NvRam) {
703690
syncMsg.setNvRamPath(file.getPath());
704691
} else if (file.getType() == VmHostFileType.TpmState) {
705692
syncMsg.setTpmStateFolder(file.getPath());
@@ -759,7 +746,7 @@ public void afterVolumeLiveSnapshotGroupCreatedOnBackend(CreateVolumesSnapshotOv
759746
syncMsg.setHostUuid(hostUuid);
760747

761748
for (VmHostFileVO file : hostFiles) {
762-
if (file.getType() == VmHostFileType.NvRam) {
749+
if (file.getType() == NvRam) {
763750
syncMsg.setNvRamPath(buildNvramSnapshotBackupFilePath(vmUuid));
764751
} else if (file.getType() == VmHostFileType.TpmState) {
765752
syncMsg.setTpmStateFolder(buildTpmStateSnapshotBackupFilePath(vmUuid));

plugin/kvm/src/main/java/org/zstack/kvm/efi/KvmSecureBootManager.java

Lines changed: 70 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import org.zstack.core.cloudbus.EventCallback;
99
import org.zstack.core.cloudbus.EventFacadeImpl;
1010
import org.zstack.core.cloudbus.MessageSafe;
11+
import org.zstack.core.cloudbus.ResourceDestinationMaker;
1112
import org.zstack.core.db.DatabaseFacade;
1213
import org.zstack.core.db.Q;
1314
import org.zstack.core.db.SQL;
@@ -76,7 +77,6 @@
7677
import java.util.Set;
7778
import java.util.function.Function;
7879

79-
import static org.zstack.compute.vm.VmGlobalConfig.ENABLE_UEFI_SECURE_BOOT;
8080
import static org.zstack.compute.vm.VmGlobalConfig.RESET_TPM_AFTER_VM_CLONE;
8181
import static org.zstack.core.Platform.operr;
8282
import static org.zstack.header.vm.additions.VmHostFileSyncReason.PostClone;
@@ -104,6 +104,8 @@ public class KvmSecureBootManager extends AbstractService {
104104
private KvmVmHostFileFactory vmHostFileFactory;
105105
@Autowired
106106
private TimeHelper timeHelper;
107+
@Autowired
108+
private ResourceDestinationMaker resourceDestinationMaker;
107109

108110
@Override
109111
public boolean start() {
@@ -118,10 +120,28 @@ public boolean stop() {
118120

119121
@SuppressWarnings("rawtypes")
120122
private void setupCanonicalEvents() {
123+
eventFacade.on(VmCanonicalEvents.VM_LIBVIRT_REPORT_START, new EventCallback<Object>() {
124+
@Override
125+
protected void run(Map tokens, Object data) {
126+
String vmUuid = (String) data;
127+
boolean managedByMe = resourceDestinationMaker.isManagedByUs(vmUuid);
128+
if (!managedByMe) {
129+
return;
130+
}
131+
markVmHostFilesChanged(vmUuid);
132+
}
133+
});
134+
121135
eventFacade.on(VmCanonicalEvents.VM_LIBVIRT_REPORT_SHUTDOWN, new EventCallback<Object>() {
122136
@Override
123137
protected void run(Map tokens, Object data) {
124138
String vmUuid = (String) data;
139+
boolean managedByMe = resourceDestinationMaker.isManagedByUs(vmUuid);
140+
if (!managedByMe) {
141+
return;
142+
}
143+
markVmHostFilesChanged(vmUuid);
144+
125145
Tuple tuple = Q.New(VmInstanceVO.class)
126146
.select(VmInstanceVO_.hostUuid, VmInstanceVO_.lastHostUuid)
127147
.eq(VmInstanceVO_.uuid, vmUuid)
@@ -173,6 +193,47 @@ public void run(MessageReply reply) {
173193
});
174194
}
175195

196+
/**
197+
* Preemptive judgment: when a VM with TPM (or enabled secure boot) starts or shuts down,
198+
* the NvRam/TpmState data must have changed, so mark the corresponding
199+
* VmHostFileVO.changeDate to current time.
200+
*/
201+
private void markVmHostFilesChanged(String vmUuid) {
202+
final Set<VmHostFileType> types = vmHostFileFactory.vmHostFileTypeNeedRegisterForVm(vmUuid);
203+
if (types.isEmpty()) {
204+
return;
205+
}
206+
207+
Tuple tuple = Q.New(VmInstanceVO.class)
208+
.select(VmInstanceVO_.hostUuid, VmInstanceVO_.lastHostUuid)
209+
.eq(VmInstanceVO_.uuid, vmUuid)
210+
.findTuple();
211+
if (tuple == null) {
212+
return;
213+
}
214+
215+
String hostUuid = (String) tuple.get(0);
216+
if (hostUuid == null) {
217+
hostUuid = (String) tuple.get(1);
218+
}
219+
if (hostUuid == null) {
220+
return;
221+
}
222+
223+
Timestamp now = new Timestamp(timeHelper.getCurrentTimeMillis());
224+
long updated = SQL.New(VmHostFileVO.class)
225+
.eq(VmHostFileVO_.vmInstanceUuid, vmUuid)
226+
.eq(VmHostFileVO_.hostUuid, hostUuid)
227+
.in(VmHostFileVO_.type, types)
228+
.set(VmHostFileVO_.changeDate, now)
229+
.update();
230+
231+
if (updated > 0) {
232+
logger.debug(String.format("preemptively marked VmHostFiles as changed for VM[uuid:%s] on host[uuid:%s], %d records updated",
233+
vmUuid, hostUuid, updated));
234+
}
235+
}
236+
176237
@Override
177238
public String getId() {
178239
return bus.makeLocalServiceId(VmInstanceConstant.SECURE_BOOT_SERVICE_ID);
@@ -473,22 +534,21 @@ protected void scripts() {
473534
private void handle(CloneVmHostFileMsg msg) {
474535
CloneVmHostFileReply reply = new CloneVmHostFileReply();
475536

476-
boolean hasTpm = Q.New(TpmVO.class)
477-
.eq(TpmVO_.vmInstanceUuid, msg.getSrcVmUuid())
478-
.isExists();
479-
ResourceConfig resourceConfig = resourceConfigFacade.getResourceConfig(ENABLE_UEFI_SECURE_BOOT.getIdentity());
480-
boolean secureBoot = resourceConfig.getResourceConfigValue(msg.getSrcVmUuid(), Boolean.class);
481-
if (!hasTpm && !secureBoot) {
537+
final Set<VmHostFileType> types = vmHostFileFactory.vmHostFileTypeNeedRegisterForVm(msg.getSrcVmUuid());
538+
if (types.isEmpty()) {
482539
bus.reply(msg, reply);
483540
return;
484541
}
485542

486543
CloneVmHostFileContext context = new CloneVmHostFileContext();
487-
context.typesNeedClone.add(VmHostFileType.NvRam);
488-
if (hasTpm) {
544+
if (types.contains(VmHostFileType.NvRam)) {
545+
context.typesNeedClone.add(VmHostFileType.NvRam);
546+
}
547+
548+
if (types.contains(VmHostFileType.TpmState)) {
489549
boolean resetTpm;
490550
if (msg.getResetTpm() == null) {
491-
resourceConfig = resourceConfigFacade.getResourceConfig(RESET_TPM_AFTER_VM_CLONE.getIdentity());
551+
ResourceConfig resourceConfig = resourceConfigFacade.getResourceConfig(RESET_TPM_AFTER_VM_CLONE.getIdentity());
492552
resetTpm = resourceConfig.getResourceConfigValue(msg.getSrcVmUuid(), Boolean.class);
493553
} else {
494554
resetTpm = msg.getResetTpm();

plugin/kvm/src/main/java/org/zstack/kvm/efi/KvmVmHostFileFactory.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,32 @@
11
package org.zstack.kvm.efi;
22

3+
import org.springframework.beans.factory.annotation.Autowired;
4+
import org.zstack.core.db.Q;
5+
import org.zstack.header.tpm.entity.TpmVO;
6+
import org.zstack.header.tpm.entity.TpmVO_;
37
import org.zstack.header.vm.additions.VmHostBackupFileVO;
8+
import org.zstack.header.vm.additions.VmHostFileType;
49
import org.zstack.header.vm.additions.VmHostFileVO;
510
import org.zstack.kvm.tpm.TpmStateVmHostBackupFileBase;
611
import org.zstack.kvm.tpm.TpmStateVmHostFileBase;
712
import org.zstack.kvm.vmfiles.AbstractVmHostBackupFileBase;
813
import org.zstack.kvm.vmfiles.AbstractVmHostFileBase;
14+
import org.zstack.resourceconfig.ResourceConfig;
15+
import org.zstack.resourceconfig.ResourceConfigFacade;
916

17+
import java.util.Collections;
18+
import java.util.HashSet;
19+
import java.util.Set;
20+
21+
import static org.zstack.compute.vm.VmGlobalConfig.ENABLE_UEFI_SECURE_BOOT;
1022
import static org.zstack.core.Platform.operr;
23+
import static org.zstack.header.vm.additions.VmHostFileType.*;
24+
import static org.zstack.utils.CollectionDSL.list;
1125

1226
public class KvmVmHostFileFactory {
27+
@Autowired
28+
private ResourceConfigFacade resourceConfigFacade;
29+
1330
public AbstractVmHostFileBase createBase(VmHostFileVO file) {
1431
switch (file.getType()) {
1532
case NvRam: return new NvRamVmHostFileBase(file);
@@ -25,4 +42,20 @@ public AbstractVmHostBackupFileBase createBackupBase(VmHostBackupFileVO backupFi
2542
default: throw operr("invalid VM host file type: " + backupFile.getType()).toException();
2643
}
2744
}
45+
46+
public Set<VmHostFileType> vmHostFileTypeNeedRegisterForVm(String vmUuid) {
47+
boolean hasTpm = Q.New(TpmVO.class)
48+
.eq(TpmVO_.vmInstanceUuid, vmUuid)
49+
.isExists();
50+
if (hasTpm) {
51+
return new HashSet<>(list(NvRam, TpmState));
52+
}
53+
ResourceConfig resourceConfig = resourceConfigFacade.getResourceConfig(ENABLE_UEFI_SECURE_BOOT.getIdentity());
54+
return resourceConfig.getResourceConfigValue(vmUuid, Boolean.class) == Boolean.TRUE ?
55+
new HashSet<>(list(NvRam)) : Collections.emptySet();
56+
}
57+
58+
public boolean needRegister(VmHostFileType type, String vmUuid) {
59+
return vmHostFileTypeNeedRegisterForVm(vmUuid).contains(type);
60+
}
2861
}

0 commit comments

Comments
 (0)