From 8b1fc3a5f3bc187fbcaaabbcc4e404ade5e1946d Mon Sep 17 00:00:00 2001 From: Pearl Dsilva Date: Mon, 15 Dec 2025 15:18:06 -0500 Subject: [PATCH 1/4] XenServer 8.4/XCP-ng 8.3: Support vTPM --- .../resource/CitrixResourceBase.java | 75 +++++++++++++++++++ .../xenbase/CitrixStartCommandWrapper.java | 8 ++ .../vm/hypervisor/xenserver/xenserver84/vmops | 40 +++++++++- .../com/cloud/api/query/QueryManagerImpl.java | 4 + 4 files changed, 126 insertions(+), 1 deletion(-) diff --git a/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java b/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java index d904e9a975bb..1f5cf6f64e8e 100644 --- a/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java +++ b/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java @@ -51,6 +51,7 @@ import javax.naming.ConfigurationException; import javax.xml.parsers.ParserConfigurationException; +import com.xensource.xenapi.VTPM; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.diagnostics.CopyToSecondaryStorageAnswer; import org.apache.cloudstack.diagnostics.CopyToSecondaryStorageCommand; @@ -5826,4 +5827,78 @@ public void destroyVm(VM vm, Connection connection, boolean forced) throws XenAP public void destroyVm(VM vm, Connection connection) throws XenAPIException, XmlRpcException { destroyVm(vm, connection, false); } + + /** + * Configure vTPM (Virtual Trusted Platform Module) support for a VM. + * vTPM provides a virtual TPM 2.0 device for VMs, enabling features like Secure Boot and disk encryption. + * + * Requirements: + * - XenServer/XCP-ng 8.3 (and above) + * - UEFI Secure Boot enabled + * - VM in halted state + * + * @param conn XenServer connection + * @param vm The VM to configure + * @param vmSpec VM specification containing vTPM settings + */ + public void configureVTPM(Connection conn, VM vm, VirtualMachineTO vmSpec) throws XenAPIException, XmlRpcException { + if (vmSpec == null || vmSpec.getDetails() == null) { + return; + } + + String vtpmEnabled = vmSpec.getDetails().getOrDefault(VmDetailConstants.VIRTUAL_TPM_ENABLED, null); + if (!Boolean.parseBoolean(vtpmEnabled)) { + return; + } + + String bootMode = StringUtils.defaultIfEmpty(vmSpec.getDetails().get(ApiConstants.BootType.UEFI.toString()), null); + String bootType = (bootMode == null) ? ApiConstants.BootType.BIOS.toString() : ApiConstants.BootType.UEFI.toString(); + + if (!ApiConstants.BootType.UEFI.toString().equals(bootType)) { + logger.warn("vTPM requires UEFI boot mode. Skipping vTPM configuration for VM: {}", vmSpec.getName()); + return; + } + + if (!ApiConstants.BootMode.SECURE.name().equals(bootMode)) { + logger.warn("PEARL - bootMode=" + bootMode); + logger.warn("vTPM requires UEFI Secure Boot to be enabled. Skipping vTPM configuration for VM: {}", vmSpec.getName()); + return; + } + + try { + Set existingVtpms = vm.getVTPMs(conn); + if (!existingVtpms.isEmpty()) { + logger.debug("vTPM already exists for VM: {}", vmSpec.getName()); + return; + } + + // Creates vTPM using: xe vtpm-create vm-uuid= + String vmUuid = vm.getUuid(conn); + String result = callHostPlugin(conn, "vmops", "create_vtpm", "vm_uuid", vmUuid); + + if (result == null || result.isEmpty() || result.startsWith("ERROR:") || result.startsWith("EXCEPTION:")) { + throw new CloudRuntimeException("Failed to create vTPM, result: " + result); + } + + logger.info("Successfully created vTPM {} for VM: {}", result.trim(), vmSpec.getName()); + } catch (Exception e) { + logger.warn("Failed to configure vTPM for VM: {}, continuing without vTPM", vmSpec.getName(), e); + } + } + + public boolean isVTPMSupported(Connection conn, Host host) { + try { + Host.Record hostRecord = host.getRecord(conn); + String productVersion = hostRecord.softwareVersion.get("product_version"); + if (productVersion == null) { + return false; + } + ComparableVersion currentVersion = new ComparableVersion(productVersion); + ComparableVersion minVersion = new ComparableVersion("8.2.0"); + return currentVersion.compareTo(minVersion) >= 0; + } catch (Exception e) { + logger.warn("Failed to check vTPM support on host", e); + return false; + } + } } diff --git a/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/CitrixStartCommandWrapper.java b/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/CitrixStartCommandWrapper.java index 155cf983548b..327a11a6ac9b 100644 --- a/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/CitrixStartCommandWrapper.java +++ b/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/CitrixStartCommandWrapper.java @@ -97,6 +97,14 @@ public Answer execute(final StartCommand command, final CitrixResourceBase citri citrixResourceBase.createVGPU(conn, command, vm, gpuDevice); } + try { + if (citrixResourceBase.isVTPMSupported(conn, host)) { + citrixResourceBase.configureVTPM(conn, vm, vmSpec); + } + } catch (Exception e) { + logger.warn("Failed to configure vTPM for VM " + vmName + ", continuing without vTPM", e); + } + Host.Record record = host.getRecord(conn); String xenBrand = record.softwareVersion.get("product_brand"); String xenVersion = record.softwareVersion.get("product_version"); diff --git a/scripts/vm/hypervisor/xenserver/xenserver84/vmops b/scripts/vm/hypervisor/xenserver/xenserver84/vmops index cf6e6325d688..c5c82d6321ff 100755 --- a/scripts/vm/hypervisor/xenserver/xenserver84/vmops +++ b/scripts/vm/hypervisor/xenserver/xenserver84/vmops @@ -1587,6 +1587,43 @@ def network_rules(session, args): except: logging.exception("Failed to network rule!") +@echo +def create_vtpm(session, args): + util.SMlog("create_vtpm called with args: %s" % str(args)) + + try: + vm_uuid = args.get('vm_uuid') + if not vm_uuid: + return "ERROR: vm_uuid parameter is required" + + # Check if vTPM already exists for this VM + cmd = ['xe', 'vtpm-list', 'vm-uuid=' + vm_uuid, '--minimal'] + result = util.pread2(cmd) + existing_vtpms = result.strip() + + if existing_vtpms: + util.SMlog("vTPM already exists for VM %s: %s" % (vm_uuid, existing_vtpms)) + return existing_vtpms.split(',')[0] # Return first vTPM UUID + + cmd = ['xe', 'vtpm-create', 'vm-uuid=' + vm_uuid] + result = util.pread2(cmd) + vtpm_uuid = result.strip() + + if vtpm_uuid: + util.SMlog("Successfully created vTPM %s for VM %s" % (vtpm_uuid, vm_uuid)) + return vtpm_uuid + else: + return "ERROR: Failed to create vTPM, empty result" + + except CommandException as e: + error_msg = "xe command failed: %s" % str(e) + util.SMlog("ERROR: %s" % error_msg) + return "ERROR: " + error_msg + except Exception as e: + error_msg = str(e) + util.SMlog("ERROR: %s" % error_msg) + return "ERROR: " + error_msg + if __name__ == "__main__": XenAPIPlugin.dispatch({"pingtest": pingtest, "setup_iscsi":setup_iscsi, "preparemigration": preparemigration, @@ -1604,4 +1641,5 @@ if __name__ == "__main__": "createFileInDomr":createFileInDomr, "kill_copy_process":kill_copy_process, "secureCopyToHost":secureCopyToHost, - "runPatchScriptInDomr": runPatchScriptInDomr}) + "runPatchScriptInDomr": runPatchScriptInDomr, + "create_vtpm": create_vtpm}) diff --git a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java index 4b469abe1fc2..02acb686f63b 100644 --- a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java +++ b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java @@ -5400,6 +5400,10 @@ private void fillVMOrTemplateDetailOptions(final Map> optio options.put(VmDetailConstants.RAM_RESERVATION, Collections.emptyList()); options.put(VmDetailConstants.VIRTUAL_TPM_ENABLED, Arrays.asList("true", "false")); } + + if (HypervisorType.XenServer.equals(hypervisorType)) { + options.put(VmDetailConstants.VIRTUAL_TPM_ENABLED, Arrays.asList("true", "false")); + } } @Override From cc8ea1e91906dce52345bf5cb4c142874a032178 Mon Sep 17 00:00:00 2001 From: Pearl Dsilva Date: Mon, 15 Dec 2025 15:26:33 -0500 Subject: [PATCH 2/4] fix issue --- .../cloud/hypervisor/xenserver/resource/CitrixResourceBase.java | 1 - scripts/vm/hypervisor/xenserver/xenserver84/vmops | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java b/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java index 1f5cf6f64e8e..4dc98504b886 100644 --- a/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java +++ b/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java @@ -5860,7 +5860,6 @@ public void configureVTPM(Connection conn, VM vm, VirtualMachineTO vmSpec) throw } if (!ApiConstants.BootMode.SECURE.name().equals(bootMode)) { - logger.warn("PEARL - bootMode=" + bootMode); logger.warn("vTPM requires UEFI Secure Boot to be enabled. Skipping vTPM configuration for VM: {}", vmSpec.getName()); return; } diff --git a/scripts/vm/hypervisor/xenserver/xenserver84/vmops b/scripts/vm/hypervisor/xenserver/xenserver84/vmops index c5c82d6321ff..76d4571ef181 100755 --- a/scripts/vm/hypervisor/xenserver/xenserver84/vmops +++ b/scripts/vm/hypervisor/xenserver/xenserver84/vmops @@ -1603,7 +1603,7 @@ def create_vtpm(session, args): if existing_vtpms: util.SMlog("vTPM already exists for VM %s: %s" % (vm_uuid, existing_vtpms)) - return existing_vtpms.split(',')[0] # Return first vTPM UUID + return existing_vtpms.split(',')[0] cmd = ['xe', 'vtpm-create', 'vm-uuid=' + vm_uuid] result = util.pread2(cmd) From bb56411604ec52b2f5aa2363b962a7528f426167 Mon Sep 17 00:00:00 2001 From: Pearl Dsilva Date: Thu, 18 Dec 2025 18:16:12 -0500 Subject: [PATCH 3/4] add log for windows 11 or other such guests OSs that require vtpm --- .../resource/CitrixResourceBase.java | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java b/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java index 4dc98504b886..114a324c3c84 100644 --- a/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java +++ b/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java @@ -5847,6 +5847,16 @@ public void configureVTPM(Connection conn, VM vm, VirtualMachineTO vmSpec) throw } String vtpmEnabled = vmSpec.getDetails().getOrDefault(VmDetailConstants.VIRTUAL_TPM_ENABLED, null); + + final Map platform = vm.getPlatform(conn); + if (platform != null) { + final String guestRequiresVtpm = platform.get("vtpm"); + if (guestRequiresVtpm != null && Boolean.parseBoolean(guestRequiresVtpm) && !Boolean.parseBoolean(vtpmEnabled)) { + logger.warn("Guest OS requires vTPM by default, even if VM details doesn't have the setting: {}", vmSpec.getName()); + return; + } + } + if (!Boolean.parseBoolean(vtpmEnabled)) { return; } @@ -5858,11 +5868,10 @@ public void configureVTPM(Connection conn, VM vm, VirtualMachineTO vmSpec) throw logger.warn("vTPM requires UEFI boot mode. Skipping vTPM configuration for VM: {}", vmSpec.getName()); return; } - - if (!ApiConstants.BootMode.SECURE.name().equals(bootMode)) { - logger.warn("vTPM requires UEFI Secure Boot to be enabled. Skipping vTPM configuration for VM: {}", vmSpec.getName()); - return; - } +// if (!ApiConstants.BootMode.SECURE.name().equals(bootMode)) { +// logger.warn("vTPM requires UEFI Secure Boot to be enabled. Skipping vTPM configuration for VM: {}", vmSpec.getName()); +// return; +// } try { Set existingVtpms = vm.getVTPMs(conn); From b12036bc5568da2c4f0fa62554d9b5bc5dd42af1 Mon Sep 17 00:00:00 2001 From: Pearl Dsilva Date: Fri, 19 Dec 2025 13:17:20 -0500 Subject: [PATCH 4/4] remove secure bootmode requirement --- .../hypervisor/xenserver/resource/CitrixResourceBase.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java b/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java index 114a324c3c84..f494a13d87df 100644 --- a/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java +++ b/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java @@ -5868,10 +5868,6 @@ public void configureVTPM(Connection conn, VM vm, VirtualMachineTO vmSpec) throw logger.warn("vTPM requires UEFI boot mode. Skipping vTPM configuration for VM: {}", vmSpec.getName()); return; } -// if (!ApiConstants.BootMode.SECURE.name().equals(bootMode)) { -// logger.warn("vTPM requires UEFI Secure Boot to be enabled. Skipping vTPM configuration for VM: {}", vmSpec.getName()); -// return; -// } try { Set existingVtpms = vm.getVTPMs(conn);