Skip to content

Commit f9d3327

Browse files
Pearl1594dhslove
authored andcommitted
Add support for vTPM for XenServer and XCP-ng 8.3/8.4 (apache#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 07c5b20 commit f9d3327

5 files changed

Lines changed: 139 additions & 2 deletions

File tree

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;
@@ -5903,4 +5904,82 @@ public void destroyVm(VM vm, Connection connection, boolean forced) throws XenAP
59035904
public void destroyVm(VM vm, Connection connection) throws XenAPIException, XmlRpcException {
59045905
destroyVm(vm, connection, false);
59055906
}
5907+
5908+
/**
5909+
* Configure vTPM (Virtual Trusted Platform Module) support for a VM.
5910+
* vTPM provides a virtual TPM 2.0 device for VMs, enabling features like Secure Boot and disk encryption.
5911+
*
5912+
* Requirements:
5913+
* - XenServer/XCP-ng 8.3 (and above)
5914+
* - UEFI Secure Boot enabled
5915+
* - VM in halted state
5916+
*
5917+
* @param conn XenServer connection
5918+
* @param vm The VM to configure
5919+
* @param vmSpec VM specification containing vTPM settings
5920+
*/
5921+
public void configureVTPM(Connection conn, VM vm, VirtualMachineTO vmSpec) throws XenAPIException, XmlRpcException {
5922+
if (vmSpec == null || vmSpec.getDetails() == null) {
5923+
return;
5924+
}
5925+
5926+
String vtpmEnabled = vmSpec.getDetails().getOrDefault(VmDetailConstants.VIRTUAL_TPM_ENABLED, null);
5927+
5928+
final Map<String, String> platform = vm.getPlatform(conn);
5929+
if (platform != null) {
5930+
final String guestRequiresVtpm = platform.get("vtpm");
5931+
if (guestRequiresVtpm != null && Boolean.parseBoolean(guestRequiresVtpm) && !Boolean.parseBoolean(vtpmEnabled)) {
5932+
logger.warn("Guest OS requires vTPM by default, even if VM details doesn't have the setting: {}", vmSpec.getName());
5933+
return;
5934+
}
5935+
}
5936+
5937+
if (!Boolean.parseBoolean(vtpmEnabled)) {
5938+
return;
5939+
}
5940+
5941+
String bootMode = StringUtils.defaultIfEmpty(vmSpec.getDetails().get(ApiConstants.BootType.UEFI.toString()), null);
5942+
String bootType = (bootMode == null) ? ApiConstants.BootType.BIOS.toString() : ApiConstants.BootType.UEFI.toString();
5943+
5944+
if (!ApiConstants.BootType.UEFI.toString().equals(bootType)) {
5945+
logger.warn("vTPM requires UEFI boot mode. Skipping vTPM configuration for VM: {}", vmSpec.getName());
5946+
return;
5947+
}
5948+
5949+
try {
5950+
Set<VTPM> existingVtpms = vm.getVTPMs(conn);
5951+
if (!existingVtpms.isEmpty()) {
5952+
logger.debug("vTPM already exists for VM: {}", vmSpec.getName());
5953+
return;
5954+
}
5955+
5956+
// Creates vTPM using: xe vtpm-create vm-uuid=<uuid>
5957+
String vmUuid = vm.getUuid(conn);
5958+
String result = callHostPlugin(conn, "vmops", "create_vtpm", "vm_uuid", vmUuid);
5959+
5960+
if (result == null || result.isEmpty() || result.startsWith("ERROR:") || result.startsWith("EXCEPTION:")) {
5961+
throw new CloudRuntimeException("Failed to create vTPM, result: " + result);
5962+
}
5963+
5964+
logger.info("Successfully created vTPM {} for VM: {}", result.trim(), vmSpec.getName());
5965+
} catch (Exception e) {
5966+
logger.warn("Failed to configure vTPM for VM: {}, continuing without vTPM", vmSpec.getName(), e);
5967+
}
5968+
}
5969+
5970+
public boolean isVTPMSupported(Connection conn, Host host) {
5971+
try {
5972+
Host.Record hostRecord = host.getRecord(conn);
5973+
String productVersion = hostRecord.softwareVersion.get("product_version");
5974+
if (productVersion == null) {
5975+
return false;
5976+
}
5977+
ComparableVersion currentVersion = new ComparableVersion(productVersion);
5978+
ComparableVersion minVersion = new ComparableVersion("8.2.0");
5979+
return currentVersion.compareTo(minVersion) >= 0;
5980+
} catch (Exception e) {
5981+
logger.warn("Failed to check vTPM support on host", e);
5982+
return false;
5983+
}
5984+
}
59065985
}

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
@@ -5537,6 +5537,10 @@ private void fillVMOrTemplateDetailOptions(final Map<String, List<String>> optio
55375537
options.put(VmDetailConstants.RAM_RESERVATION, Collections.emptyList());
55385538
options.put(VmDetailConstants.VIRTUAL_TPM_ENABLED, Arrays.asList("true", "false"));
55395539
}
5540+
5541+
if (HypervisorType.XenServer.equals(hypervisorType)) {
5542+
options.put(VmDetailConstants.VIRTUAL_TPM_ENABLED, Arrays.asList("true", "false"));
5543+
}
55405544
}
55415545

55425546
private void fillDisasterRecoveryClusterDetailOptions(final Map<String, List<String>> options) {

0 commit comments

Comments
 (0)