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 757696c6c1d..b486fe09379 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,20 +1,28 @@ 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.header.vm.additions.VmHostFileType; import org.zstack.resourceconfig.ResourceConfig; import org.zstack.resourceconfig.ResourceConfigFacade; import org.zstack.utils.Utils; import org.zstack.utils.logging.CLogger; +import java.util.Collections; +import java.util.HashSet; import java.util.Objects; +import java.util.Set; import static org.zstack.compute.vm.VmGlobalConfig.ENABLE_UEFI_SECURE_BOOT; +import static org.zstack.header.vm.additions.VmHostFileType.NvRam; +import static org.zstack.header.vm.additions.VmHostFileType.TpmState; +import static org.zstack.utils.CollectionDSL.list; public class VmTpmManager { private static final CLogger logger = Utils.getLogger(VmTpmManager.class); @@ -58,13 +66,27 @@ public static boolean isUefiBootMode(String bootMode) { } public boolean needRegisterNvRam(String vmUuid) { - boolean tpmExists = Q.New(TpmVO.class) + return needRegister(NvRam, vmUuid); + } + + public Set vmHostFileTypeNeedRegisterForVm(String vmUuid) { + String bootMode = VmSystemTags.BOOT_MODE.getTokenByResourceUuid(vmUuid, VmSystemTags.BOOT_MODE_TOKEN); + if (!isUefiBootMode(bootMode)) { + return Collections.emptySet(); + } + + boolean hasTpm = Q.New(TpmVO.class) .eq(TpmVO_.vmInstanceUuid, vmUuid) .isExists(); - if (tpmExists) { - return true; + if (hasTpm) { + return new HashSet<>(list(NvRam, TpmState)); } ResourceConfig resourceConfig = resourceConfigFacade.getResourceConfig(ENABLE_UEFI_SECURE_BOOT.getIdentity()); - return resourceConfig.getResourceConfigValue(vmUuid, Boolean.class); + return resourceConfig.getResourceConfigValue(vmUuid, Boolean.class) == Boolean.TRUE ? + new HashSet<>(list(NvRam)) : Collections.emptySet(); + } + + public boolean needRegister(VmHostFileType type, String vmUuid) { + return vmHostFileTypeNeedRegisterForVm(vmUuid).contains(type); } } 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 35ff290762a..a30a6691f93 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 @@ -3,7 +3,6 @@ import org.apache.commons.lang.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.zstack.compute.vm.VmGlobalConfig; -import org.zstack.compute.vm.VmSystemTags; import org.zstack.compute.vm.devices.VmTpmManager; import org.zstack.core.Platform; import org.zstack.core.cloudbus.CloudBus; @@ -23,8 +22,6 @@ import org.zstack.header.errorcode.ErrorCode; import org.zstack.header.exception.CloudRuntimeException; import org.zstack.header.message.MessageReply; -import org.zstack.header.tpm.entity.TpmVO; -import org.zstack.header.tpm.entity.TpmVO_; import org.zstack.header.vm.DiskAO; import org.zstack.header.vm.PreVmInstantiateResourceExtensionPoint; import org.zstack.header.vm.VmInstanceVO; @@ -79,7 +76,6 @@ import java.util.Map; import java.util.Objects; -import static org.zstack.compute.vm.VmGlobalConfig.ENABLE_UEFI_SECURE_BOOT; import static org.zstack.core.Platform.operr; import static org.zstack.header.vm.additions.VmHostFileSyncReason.PostMigration; import static org.zstack.header.vm.additions.VmHostFileSyncReason.BeforeHaStart; @@ -87,6 +83,7 @@ import static org.zstack.header.vm.additions.VmHostFileSyncReason.PrepareRead; import static org.zstack.header.vm.additions.VmHostFileSyncReason.ResourceRelease; import static org.zstack.header.vm.additions.VmHostFileSyncReason.SnapshotGroupOnlineBackup; +import static org.zstack.header.vm.additions.VmHostFileType.NvRam; import static org.zstack.kvm.KVMConstant.*; import static org.zstack.utils.CollectionDSL.list; @@ -107,6 +104,8 @@ public class KvmSecureBootExtensions implements KVMStartVmExtensionPoint, private ResourceConfigFacade resourceConfigFacade; @Autowired private DatabaseFacade databaseFacade; + @Autowired + private KvmVmHostFileFactory vmHostFileFactory; private final Object hostFileLock = new Object(); @@ -141,7 +140,7 @@ private void prepareNvRamToStartVmCmd(KVMAgentCommands.StartVmCmd cmd, NvRamSpec final Timestamp now = Timestamp.from(Instant.now()); VmHostFileVO nvRamFile = Q.New(VmHostFileVO.class) .eq(VmHostFileVO_.vmInstanceUuid, cmd.getVmInstanceUuid()) - .eq(VmHostFileVO_.type, VmHostFileType.NvRam) + .eq(VmHostFileVO_.type, NvRam) .eq(VmHostFileVO_.hostUuid, host.getUuid()) .find(); if (nvRamFile == null) { @@ -149,7 +148,7 @@ private void prepareNvRamToStartVmCmd(KVMAgentCommands.StartVmCmd cmd, NvRamSpec nvRamFile.setUuid(Platform.getUuid()); nvRamFile.setHostUuid(host.getUuid()); nvRamFile.setVmInstanceUuid(cmd.getVmInstanceUuid()); - nvRamFile.setType(VmHostFileType.NvRam); + nvRamFile.setType(NvRam); nvRamFile.setPath(volume.getInstallPath()); nvRamFile.setCreateDate(now); nvRamFile.setResourceName("NvRam file for " + cmd.getVmInstanceUuid()); @@ -180,29 +179,17 @@ private void prepareNvRamBeforeMigration(VmInstanceInventory vm, String dstHostU return; } - String tpmUuid = Q.New(TpmVO.class) - .eq(TpmVO_.vmInstanceUuid, vm.getUuid()) - .select(TpmVO_.uuid) - .findValue(); - boolean needRegisterNvRam = tpmUuid != null; + boolean needRegisterNvRam = vmHostFileFactory.needRegister(NvRam, vm.getUuid()); if (!needRegisterNvRam) { - String bootMode = VmSystemTags.BOOT_MODE.getTokenByResourceUuid(vm.getUuid(), VmSystemTags.BOOT_MODE_TOKEN); - if (isUefiBootMode(bootMode)) { - ResourceConfig resourceConfig = resourceConfigFacade.getResourceConfig(ENABLE_UEFI_SECURE_BOOT.getIdentity()); - needRegisterNvRam = resourceConfig.getResourceConfigValue(vm.getUuid(), Boolean.class) == Boolean.TRUE; - } - - if (!needRegisterNvRam) { - completion.success(); - return; - } + completion.success(); + return; } SimpleFlowChain.of("prepare-nvram-before-vm-" + vm.getUuid() + "-migrate") .then("prepare-nvram-folder-on-dest-host", trigger -> { VmHostFileTO to = new VmHostFileTO(); to.setPath(buildNvramFilePath(vm.getUuid())); - to.setType(VmHostFileType.NvRam.toString()); + to.setType(NvRam.toString()); to.setOperation(VmHostFileOperation.Prepare.toString()); RewriteVmHostFilesContext context = new RewriteVmHostFilesContext(); @@ -290,7 +277,7 @@ public void preInstantiateVmResource(VmInstanceSpec spec, Completion completion) PrepareHostFileContext context = new PrepareHostFileContext(); context.hostUuid = spec.getDestHost().getUuid(); context.vmUuid = spec.getVmInventory().getUuid(); - context.type = VmHostFileType.NvRam; + context.type = NvRam; context.backupUuid = nvRamSpec.getBackupFileUuid(); context.syncReason = "pre-instantiate VM resource"; prepareHostFileOnHost(context, completion); @@ -349,7 +336,7 @@ public void run(FlowTrigger trigger, Map data) { syncMsg.setVmUuid(context.vmUuid); syncMsg.setSyncReason(PrepareRead.reason(context.syncReason)); - if (vmHostFile.getType() == VmHostFileType.NvRam) { + if (vmHostFile.getType() == NvRam) { context.path = vmHostFile.getPath(); syncMsg.setNvRamPath(context.path); } else if (vmHostFile.getType() == VmHostFileType.TpmState) { @@ -471,7 +458,7 @@ public void run(FlowTrigger trigger, Map data) { syncMsg.setVmUuid(context.vmUuid); syncMsg.setSyncReason(PrepareReRead.reason(context.syncReason)); - if (context.type == VmHostFileType.NvRam) { + if (context.type == NvRam) { syncMsg.setNvRamPath(context.path); } else if (context.type == VmHostFileType.TpmState) { syncMsg.setTpmStateFolder(context.path); @@ -574,7 +561,7 @@ public void releaseVmResource(VmInstanceSpec spec, Completion completion) { syncMsg.setSyncReason(ResourceRelease.reason()); for (VmHostFileVO file : vmHostFiles) { - if (file.getType() == VmHostFileType.NvRam) { + if (file.getType() == NvRam) { syncMsg.setNvRamPath(file.getPath()); } else if (file.getType() == VmHostFileType.TpmState) { syncMsg.setTpmStateFolder(file.getPath()); @@ -633,7 +620,7 @@ public void beforeHaStartVmInstance(String vmUuid, String judgerClassName, List< syncMsg.setSyncReason(BeforeHaStart.reason()); for (VmHostFileVO file : vmHostFiles) { - if (file.getType() == VmHostFileType.NvRam) { + if (file.getType() == NvRam) { syncMsg.setNvRamPath(file.getPath()); } else if (file.getType() == VmHostFileType.TpmState) { syncMsg.setTpmStateFolder(file.getPath()); @@ -699,7 +686,7 @@ public void afterMigrateVm(VmInstanceInventory inv, String srcHostUuid, NoErrorC syncMsg.setSyncReason(PostMigration.reason()); for (VmHostFileVO file : vmHostFiles) { - if (file.getType() == VmHostFileType.NvRam) { + if (file.getType() == NvRam) { syncMsg.setNvRamPath(file.getPath()); } else if (file.getType() == VmHostFileType.TpmState) { syncMsg.setTpmStateFolder(file.getPath()); @@ -759,7 +746,7 @@ public void afterVolumeLiveSnapshotGroupCreatedOnBackend(CreateVolumesSnapshotOv syncMsg.setHostUuid(hostUuid); for (VmHostFileVO file : hostFiles) { - if (file.getType() == VmHostFileType.NvRam) { + if (file.getType() == NvRam) { syncMsg.setNvRamPath(buildNvramSnapshotBackupFilePath(vmUuid)); } else if (file.getType() == VmHostFileType.TpmState) { syncMsg.setTpmStateFolder(buildTpmStateSnapshotBackupFilePath(vmUuid)); diff --git a/plugin/kvm/src/main/java/org/zstack/kvm/efi/KvmSecureBootManager.java b/plugin/kvm/src/main/java/org/zstack/kvm/efi/KvmSecureBootManager.java index a9a8b152b2c..5a62cc6dc6d 100644 --- a/plugin/kvm/src/main/java/org/zstack/kvm/efi/KvmSecureBootManager.java +++ b/plugin/kvm/src/main/java/org/zstack/kvm/efi/KvmSecureBootManager.java @@ -8,6 +8,7 @@ import org.zstack.core.cloudbus.EventCallback; import org.zstack.core.cloudbus.EventFacadeImpl; import org.zstack.core.cloudbus.MessageSafe; +import org.zstack.core.cloudbus.ResourceDestinationMaker; import org.zstack.core.db.DatabaseFacade; import org.zstack.core.db.Q; import org.zstack.core.db.SQL; @@ -27,8 +28,6 @@ import org.zstack.header.exception.CloudRuntimeException; import org.zstack.header.message.Message; import org.zstack.header.message.MessageReply; -import org.zstack.header.tpm.entity.TpmVO; -import org.zstack.header.tpm.entity.TpmVO_; import org.zstack.header.vm.VmCanonicalEvents; import org.zstack.header.vm.VmInstanceConstant; import org.zstack.header.vm.VmInstanceVO; @@ -76,7 +75,6 @@ import java.util.Set; import java.util.function.Function; -import static org.zstack.compute.vm.VmGlobalConfig.ENABLE_UEFI_SECURE_BOOT; import static org.zstack.compute.vm.VmGlobalConfig.RESET_TPM_AFTER_VM_CLONE; import static org.zstack.core.Platform.operr; import static org.zstack.header.vm.additions.VmHostFileSyncReason.PostClone; @@ -104,6 +102,8 @@ public class KvmSecureBootManager extends AbstractService { private KvmVmHostFileFactory vmHostFileFactory; @Autowired private TimeHelper timeHelper; + @Autowired + private ResourceDestinationMaker resourceDestinationMaker; @Override public boolean start() { @@ -118,10 +118,27 @@ public boolean stop() { @SuppressWarnings("rawtypes") private void setupCanonicalEvents() { + eventFacade.on(VmCanonicalEvents.VM_LIBVIRT_REPORT_START, new EventCallback() { + @Override + protected void run(Map tokens, Object data) { + String vmUuid = (String) data; + boolean managedByMe = resourceDestinationMaker.isManagedByUs(vmUuid); + if (!managedByMe) { + return; + } + markVmHostFilesChanged(vmUuid); + } + }); + eventFacade.on(VmCanonicalEvents.VM_LIBVIRT_REPORT_SHUTDOWN, new EventCallback() { @Override protected void run(Map tokens, Object data) { String vmUuid = (String) data; + boolean managedByMe = resourceDestinationMaker.isManagedByUs(vmUuid); + if (!managedByMe) { + return; + } + Tuple tuple = Q.New(VmInstanceVO.class) .select(VmInstanceVO_.hostUuid, VmInstanceVO_.lastHostUuid) .eq(VmInstanceVO_.uuid, vmUuid) @@ -134,6 +151,7 @@ protected void run(Map tokens, Object data) { if (hostUuid == null) { hostUuid = (String) tuple.get(1); } + markVmHostFilesChanged(vmUuid, hostUuid); List hostFiles = Q.New(VmHostFileVO.class) .eq(VmHostFileVO_.vmInstanceUuid, vmUuid) @@ -173,6 +191,55 @@ public void run(MessageReply reply) { }); } + /** + * Preemptive judgment: when a VM with TPM (or enabled secure boot) starts or shuts down, + * the NvRam/TpmState data must have changed, so mark the corresponding + * VmHostFileVO.changeDate to current time. + */ + private void markVmHostFilesChanged(String vmUuid) { + Tuple tuple = Q.New(VmInstanceVO.class) + .select(VmInstanceVO_.hostUuid, VmInstanceVO_.lastHostUuid) + .eq(VmInstanceVO_.uuid, vmUuid) + .findTuple(); + if (tuple == null) { + return; + } + + String hostUuid = (String) tuple.get(0); + if (hostUuid == null) { + hostUuid = (String) tuple.get(1); + } + if (hostUuid == null) { + return; + } + + markVmHostFilesChanged(vmUuid, hostUuid); + } + + private void markVmHostFilesChanged(String vmUuid, String hostUuid) { + if (hostUuid == null) { + return; + } + + final Set types = vmHostFileFactory.vmHostFileTypeNeedRegisterForVm(vmUuid); + if (types.isEmpty()) { + return; + } + + Timestamp now = new Timestamp(timeHelper.getCurrentTimeMillis()); + long updated = SQL.New(VmHostFileVO.class) + .eq(VmHostFileVO_.vmInstanceUuid, vmUuid) + .eq(VmHostFileVO_.hostUuid, hostUuid) + .in(VmHostFileVO_.type, types) + .set(VmHostFileVO_.changeDate, now) + .update(); + + if (updated > 0) { + logger.debug(String.format("preemptively marked VmHostFiles as changed for VM[uuid:%s] on host[uuid:%s], %d records updated", + vmUuid, hostUuid, updated)); + } + } + @Override public String getId() { return bus.makeLocalServiceId(VmInstanceConstant.SECURE_BOOT_SERVICE_ID); @@ -473,22 +540,21 @@ protected void scripts() { private void handle(CloneVmHostFileMsg msg) { CloneVmHostFileReply reply = new CloneVmHostFileReply(); - boolean hasTpm = Q.New(TpmVO.class) - .eq(TpmVO_.vmInstanceUuid, msg.getSrcVmUuid()) - .isExists(); - ResourceConfig resourceConfig = resourceConfigFacade.getResourceConfig(ENABLE_UEFI_SECURE_BOOT.getIdentity()); - boolean secureBoot = resourceConfig.getResourceConfigValue(msg.getSrcVmUuid(), Boolean.class); - if (!hasTpm && !secureBoot) { + final Set types = vmHostFileFactory.vmHostFileTypeNeedRegisterForVm(msg.getSrcVmUuid()); + if (types.isEmpty()) { bus.reply(msg, reply); return; } CloneVmHostFileContext context = new CloneVmHostFileContext(); - context.typesNeedClone.add(VmHostFileType.NvRam); - if (hasTpm) { + if (types.contains(VmHostFileType.NvRam)) { + context.typesNeedClone.add(VmHostFileType.NvRam); + } + + if (types.contains(VmHostFileType.TpmState)) { boolean resetTpm; if (msg.getResetTpm() == null) { - resourceConfig = resourceConfigFacade.getResourceConfig(RESET_TPM_AFTER_VM_CLONE.getIdentity()); + ResourceConfig resourceConfig = resourceConfigFacade.getResourceConfig(RESET_TPM_AFTER_VM_CLONE.getIdentity()); resetTpm = resourceConfig.getResourceConfigValue(msg.getSrcVmUuid(), Boolean.class); } else { resetTpm = msg.getResetTpm(); diff --git a/plugin/kvm/src/main/java/org/zstack/kvm/efi/KvmVmHostFileFactory.java b/plugin/kvm/src/main/java/org/zstack/kvm/efi/KvmVmHostFileFactory.java index 0aaaad0cc9a..c4e13957cdf 100644 --- a/plugin/kvm/src/main/java/org/zstack/kvm/efi/KvmVmHostFileFactory.java +++ b/plugin/kvm/src/main/java/org/zstack/kvm/efi/KvmVmHostFileFactory.java @@ -1,15 +1,26 @@ package org.zstack.kvm.efi; +import org.springframework.beans.factory.annotation.Autowired; +import org.zstack.compute.vm.devices.VmTpmManager; import org.zstack.header.vm.additions.VmHostBackupFileVO; +import org.zstack.header.vm.additions.VmHostFileType; import org.zstack.header.vm.additions.VmHostFileVO; import org.zstack.kvm.tpm.TpmStateVmHostBackupFileBase; import org.zstack.kvm.tpm.TpmStateVmHostFileBase; import org.zstack.kvm.vmfiles.AbstractVmHostBackupFileBase; import org.zstack.kvm.vmfiles.AbstractVmHostFileBase; +import org.zstack.resourceconfig.ResourceConfigFacade; + +import java.util.Set; import static org.zstack.core.Platform.operr; public class KvmVmHostFileFactory { + @Autowired + private ResourceConfigFacade resourceConfigFacade; + @Autowired + private VmTpmManager vmTpmManager; + public AbstractVmHostFileBase createBase(VmHostFileVO file) { switch (file.getType()) { case NvRam: return new NvRamVmHostFileBase(file); @@ -25,4 +36,12 @@ public AbstractVmHostBackupFileBase createBackupBase(VmHostBackupFileVO backupFi default: throw operr("invalid VM host file type: " + backupFile.getType()).toException(); } } + + public Set vmHostFileTypeNeedRegisterForVm(String vmUuid) { + return vmTpmManager.vmHostFileTypeNeedRegisterForVm(vmUuid); + } + + public boolean needRegister(VmHostFileType type, String vmUuid) { + return vmTpmManager.needRegister(type, vmUuid); + } }