Skip to content

Commit 38e30a1

Browse files
authored
Add support for vTPM for XenServer and XCP-ng 8.3/8.4 (#12263)
* XenServer 8.4/XCP-ng 8.3: Support vTPM * fix issue * add log for windows 11 or other such guests OSs that require vtpm * remove secure bootmode requirement * Fix uefi setting on host for xenserver 8.4
1 parent 0e7f748 commit 38e30a1

File tree

5 files changed

+139
-2
lines changed

5 files changed

+139
-2
lines changed

plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import javax.naming.ConfigurationException;
5252
import javax.xml.parsers.ParserConfigurationException;
5353

54+
import com.xensource.xenapi.VTPM;
5455
import org.apache.cloudstack.api.ApiConstants;
5556
import org.apache.cloudstack.diagnostics.CopyToSecondaryStorageAnswer;
5657
import org.apache.cloudstack.diagnostics.CopyToSecondaryStorageCommand;
@@ -5826,4 +5827,82 @@ public void destroyVm(VM vm, Connection connection, boolean forced) throws XenAP
58265827
public void destroyVm(VM vm, Connection connection) throws XenAPIException, XmlRpcException {
58275828
destroyVm(vm, connection, false);
58285829
}
5830+
5831+
/**
5832+
* Configure vTPM (Virtual Trusted Platform Module) support for a VM.
5833+
* vTPM provides a virtual TPM 2.0 device for VMs, enabling features like Secure Boot and disk encryption.
5834+
*
5835+
* Requirements:
5836+
* - XenServer/XCP-ng 8.3 (and above)
5837+
* - UEFI Secure Boot enabled
5838+
* - VM in halted state
5839+
*
5840+
* @param conn XenServer connection
5841+
* @param vm The VM to configure
5842+
* @param vmSpec VM specification containing vTPM settings
5843+
*/
5844+
public void configureVTPM(Connection conn, VM vm, VirtualMachineTO vmSpec) throws XenAPIException, XmlRpcException {
5845+
if (vmSpec == null || vmSpec.getDetails() == null) {
5846+
return;
5847+
}
5848+
5849+
String vtpmEnabled = vmSpec.getDetails().getOrDefault(VmDetailConstants.VIRTUAL_TPM_ENABLED, null);
5850+
5851+
final Map<String, String> platform = vm.getPlatform(conn);
5852+
if (platform != null) {
5853+
final String guestRequiresVtpm = platform.get("vtpm");
5854+
if (guestRequiresVtpm != null && Boolean.parseBoolean(guestRequiresVtpm) && !Boolean.parseBoolean(vtpmEnabled)) {
5855+
logger.warn("Guest OS requires vTPM by default, even if VM details doesn't have the setting: {}", vmSpec.getName());
5856+
return;
5857+
}
5858+
}
5859+
5860+
if (!Boolean.parseBoolean(vtpmEnabled)) {
5861+
return;
5862+
}
5863+
5864+
String bootMode = StringUtils.defaultIfEmpty(vmSpec.getDetails().get(ApiConstants.BootType.UEFI.toString()), null);
5865+
String bootType = (bootMode == null) ? ApiConstants.BootType.BIOS.toString() : ApiConstants.BootType.UEFI.toString();
5866+
5867+
if (!ApiConstants.BootType.UEFI.toString().equals(bootType)) {
5868+
logger.warn("vTPM requires UEFI boot mode. Skipping vTPM configuration for VM: {}", vmSpec.getName());
5869+
return;
5870+
}
5871+
5872+
try {
5873+
Set<VTPM> existingVtpms = vm.getVTPMs(conn);
5874+
if (!existingVtpms.isEmpty()) {
5875+
logger.debug("vTPM already exists for VM: {}", vmSpec.getName());
5876+
return;
5877+
}
5878+
5879+
// Creates vTPM using: xe vtpm-create vm-uuid=<uuid>
5880+
String vmUuid = vm.getUuid(conn);
5881+
String result = callHostPlugin(conn, "vmops", "create_vtpm", "vm_uuid", vmUuid);
5882+
5883+
if (result == null || result.isEmpty() || result.startsWith("ERROR:") || result.startsWith("EXCEPTION:")) {
5884+
throw new CloudRuntimeException("Failed to create vTPM, result: " + result);
5885+
}
5886+
5887+
logger.info("Successfully created vTPM {} for VM: {}", result.trim(), vmSpec.getName());
5888+
} catch (Exception e) {
5889+
logger.warn("Failed to configure vTPM for VM: {}, continuing without vTPM", vmSpec.getName(), e);
5890+
}
5891+
}
5892+
5893+
public boolean isVTPMSupported(Connection conn, Host host) {
5894+
try {
5895+
Host.Record hostRecord = host.getRecord(conn);
5896+
String productVersion = hostRecord.softwareVersion.get("product_version");
5897+
if (productVersion == null) {
5898+
return false;
5899+
}
5900+
ComparableVersion currentVersion = new ComparableVersion(productVersion);
5901+
ComparableVersion minVersion = new ComparableVersion("8.2.0");
5902+
return currentVersion.compareTo(minVersion) >= 0;
5903+
} catch (Exception e) {
5904+
logger.warn("Failed to check vTPM support on host", e);
5905+
return false;
5906+
}
5907+
}
58295908
}

plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/CitrixReadyCommandWrapper.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,20 @@ public Answer execute(final ReadyCommand command, final CitrixResourceBase citri
6060
final Set<VM> vms = host.getResidentVMs(conn);
6161
citrixResourceBase.destroyPatchVbd(conn, vms);
6262

63+
} catch (final Exception e) {
64+
logger.warn("Unable to destroy CD-ROM device for system VMs", e);
65+
}
66+
67+
try {
68+
final Host host = Host.getByUuid(conn, citrixResourceBase.getHost().getUuid());
6369
final Host.Record hr = host.getRecord(conn);
6470
if (isUefiSupported(CitrixHelper.getProductVersion(hr))) {
6571
hostDetails.put(com.cloud.host.Host.HOST_UEFI_ENABLE, Boolean.TRUE.toString());
6672
}
67-
} catch (final Exception e) {
73+
} catch (Exception e) {
74+
logger.warn("Unable to get UEFI support info", e);
6875
}
76+
6977
try {
7078
final boolean result = citrixResourceBase.cleanupHaltedVms(conn);
7179
if (!result) {

plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/CitrixStartCommandWrapper.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,14 @@ public Answer execute(final StartCommand command, final CitrixResourceBase citri
9797
citrixResourceBase.createVGPU(conn, command, vm, gpuDevice);
9898
}
9999

100+
try {
101+
if (citrixResourceBase.isVTPMSupported(conn, host)) {
102+
citrixResourceBase.configureVTPM(conn, vm, vmSpec);
103+
}
104+
} catch (Exception e) {
105+
logger.warn("Failed to configure vTPM for VM " + vmName + ", continuing without vTPM", e);
106+
}
107+
100108
Host.Record record = host.getRecord(conn);
101109
String xenBrand = record.softwareVersion.get("product_brand");
102110
String xenVersion = record.softwareVersion.get("product_version");

scripts/vm/hypervisor/xenserver/xenserver84/vmops

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1587,6 +1587,43 @@ def network_rules(session, args):
15871587
except:
15881588
logging.exception("Failed to network rule!")
15891589

1590+
@echo
1591+
def create_vtpm(session, args):
1592+
util.SMlog("create_vtpm called with args: %s" % str(args))
1593+
1594+
try:
1595+
vm_uuid = args.get('vm_uuid')
1596+
if not vm_uuid:
1597+
return "ERROR: vm_uuid parameter is required"
1598+
1599+
# Check if vTPM already exists for this VM
1600+
cmd = ['xe', 'vtpm-list', 'vm-uuid=' + vm_uuid, '--minimal']
1601+
result = util.pread2(cmd)
1602+
existing_vtpms = result.strip()
1603+
1604+
if existing_vtpms:
1605+
util.SMlog("vTPM already exists for VM %s: %s" % (vm_uuid, existing_vtpms))
1606+
return existing_vtpms.split(',')[0]
1607+
1608+
cmd = ['xe', 'vtpm-create', 'vm-uuid=' + vm_uuid]
1609+
result = util.pread2(cmd)
1610+
vtpm_uuid = result.strip()
1611+
1612+
if vtpm_uuid:
1613+
util.SMlog("Successfully created vTPM %s for VM %s" % (vtpm_uuid, vm_uuid))
1614+
return vtpm_uuid
1615+
else:
1616+
return "ERROR: Failed to create vTPM, empty result"
1617+
1618+
except CommandException as e:
1619+
error_msg = "xe command failed: %s" % str(e)
1620+
util.SMlog("ERROR: %s" % error_msg)
1621+
return "ERROR: " + error_msg
1622+
except Exception as e:
1623+
error_msg = str(e)
1624+
util.SMlog("ERROR: %s" % error_msg)
1625+
return "ERROR: " + error_msg
1626+
15901627
if __name__ == "__main__":
15911628
XenAPIPlugin.dispatch({"pingtest": pingtest, "setup_iscsi":setup_iscsi,
15921629
"preparemigration": preparemigration,
@@ -1604,4 +1641,5 @@ if __name__ == "__main__":
16041641
"createFileInDomr":createFileInDomr,
16051642
"kill_copy_process":kill_copy_process,
16061643
"secureCopyToHost":secureCopyToHost,
1607-
"runPatchScriptInDomr": runPatchScriptInDomr})
1644+
"runPatchScriptInDomr": runPatchScriptInDomr,
1645+
"create_vtpm": create_vtpm})

server/src/main/java/com/cloud/api/query/QueryManagerImpl.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5440,6 +5440,10 @@ private void fillVMOrTemplateDetailOptions(final Map<String, List<String>> optio
54405440
options.put(VmDetailConstants.RAM_RESERVATION, Collections.emptyList());
54415441
options.put(VmDetailConstants.VIRTUAL_TPM_ENABLED, Arrays.asList("true", "false"));
54425442
}
5443+
5444+
if (HypervisorType.XenServer.equals(hypervisorType)) {
5445+
options.put(VmDetailConstants.VIRTUAL_TPM_ENABLED, Arrays.asList("true", "false"));
5446+
}
54435447
}
54445448

54455449
@Override

0 commit comments

Comments
 (0)