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

Reply via email to