Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -5826,4 +5827,82 @@ 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);

final Map<String, String> 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;
}

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;
}

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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
40 changes: 39 additions & 1 deletion scripts/vm/hypervisor/xenserver/xenserver84/vmops
Original file line number Diff line number Diff line change
Expand Up @@ -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]

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,
Expand All @@ -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})
Original file line number Diff line number Diff line change
Expand Up @@ -5400,6 +5400,10 @@ private void fillVMOrTemplateDetailOptions(final Map<String, List<String>> 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
Expand Down
Loading