This is an automated email from the ASF dual-hosted git repository. pearl11594 pushed a commit to branch support-vtpm-xen-xcp in repository https://gitbox.apache.org/repos/asf/cloudstack.git
commit 8b1fc3a5f3bc187fbcaaabbcc4e404ade5e1946d Author: Pearl Dsilva <[email protected]> AuthorDate: Mon Dec 15 15:18:06 2025 -0500 XenServer 8.4/XCP-ng 8.3: Support vTPM --- .../xenserver/resource/CitrixResourceBase.java | 75 ++++++++++++++++++++++ .../wrapper/xenbase/CitrixStartCommandWrapper.java | 8 +++ scripts/vm/hypervisor/xenserver/xenserver84/vmops | 40 +++++++++++- .../java/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 d904e9a975b..1f5cf6f64e8 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 java.util.concurrent.TimeoutException; 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 abstract class CitrixResourceBase extends ServerResourceBase implements S 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<VTPM> 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=<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 155cf983548..327a11a6ac9 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 final class CitrixStartCommandWrapper extends CommandWrapper<StartCommand 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 cf6e6325d68..c5c82d6321f 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 4b469abe1fc..02acb686f63 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 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q 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
