vTPM: support KVM and VMware (#10543)

* KVM: add Virtual TPM model and version

* KVM: add admin-only VM setting GUEST.CPU.MODE and GUEST.CPU.MODEL

* VMware: add vTPM

* vTPM: do not set Key due to 'Cannot add multiple devices using the same device key..'

* vTPM: add unit test testTpmModel

* engine/schema: remove user vm details for guest CPU mode/model

* vTPM: extra methods as Daan's requests

* vTPM: add unit tests in VmwareResourceTest

* vTPM: update unit tests in VmwareResourceTest

* vTPM: add unit test in LibvirtComputingResourceTest

* vTPM: use the default TPM version if an invalid version is passed

* vTPM: requires UEFI on vmware and do nothing if it is not enabled/disabled

* vTPM: let uses to add UEFI on vmware

* Update plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java

Co-authored-by: Suresh Kumar Anaparti <sureshkumar.anaparti@gmail.com>

* Update plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java

Co-authored-by: Suresh Kumar Anaparti <sureshkumar.anaparti@gmail.com>

* vTPM: remove template details for guest CPU mode/model

* UI: boot vm from ISO into UEFI/SECURE mode

---------

Co-authored-by: Suresh Kumar Anaparti <sureshkumar.anaparti@gmail.com>
This commit is contained in:
Wei Zhou 2025-04-26 11:05:36 +02:00 committed by GitHub
parent 12c077d704
commit 5d28e66b52
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 259 additions and 3 deletions

View File

@ -101,4 +101,13 @@ public interface VmDetailConstants {
String VMWARE_HOST_NAME = String.format("%s-host", VMWARE_TO_KVM_PREFIX); String VMWARE_HOST_NAME = String.format("%s-host", VMWARE_TO_KVM_PREFIX);
String VMWARE_DISK = String.format("%s-disk", VMWARE_TO_KVM_PREFIX); String VMWARE_DISK = String.format("%s-disk", VMWARE_TO_KVM_PREFIX);
String VMWARE_MAC_ADDRESSES = String.format("%s-mac-addresses", VMWARE_TO_KVM_PREFIX); String VMWARE_MAC_ADDRESSES = String.format("%s-mac-addresses", VMWARE_TO_KVM_PREFIX);
// TPM
String VIRTUAL_TPM_ENABLED = "virtual.tpm.enabled";
String VIRTUAL_TPM_MODEL = "virtual.tpm.model";
String VIRTUAL_TPM_VERSION = "virtual.tpm.version";
// CPU mode and model, ADMIN only
String GUEST_CPU_MODE = "guest.cpu.mode";
String GUEST_CPU_MODEL = "guest.cpu.model";
} }

View File

@ -16,6 +16,7 @@
// under the License. // under the License.
package org.apache.cloudstack.query; package org.apache.cloudstack.query;
import java.util.Arrays;
import java.util.List; import java.util.List;
import org.apache.cloudstack.affinity.AffinityGroupResponse; import org.apache.cloudstack.affinity.AffinityGroupResponse;
@ -97,6 +98,7 @@ import org.apache.cloudstack.api.response.ZoneResponse;
import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.ConfigKey;
import com.cloud.exception.PermissionDeniedException; import com.cloud.exception.PermissionDeniedException;
import com.cloud.vm.VmDetailConstants;
/** /**
* Service used for list api query. * Service used for list api query.
@ -104,6 +106,8 @@ import com.cloud.exception.PermissionDeniedException;
*/ */
public interface QueryService { public interface QueryService {
List<String> RootAdminOnlyVmSettings = Arrays.asList(VmDetailConstants.GUEST_CPU_MODE, VmDetailConstants.GUEST_CPU_MODEL);
// Config keys // Config keys
ConfigKey<Boolean> AllowUserViewDestroyedVM = new ConfigKey<>("Advanced", Boolean.class, "allow.user.view.destroyed.vm", "false", ConfigKey<Boolean> AllowUserViewDestroyedVM = new ConfigKey<>("Advanced", Boolean.class, "allow.user.view.destroyed.vm", "false",
"Determines whether users can view their destroyed or expunging vm ", true, ConfigKey.Scope.Account); "Determines whether users can view their destroyed or expunging vm ", true, ConfigKey.Scope.Account);

View File

@ -19,6 +19,12 @@
-- Schema upgrade from 4.20.0.0 to 4.20.1.0 -- Schema upgrade from 4.20.0.0 to 4.20.1.0
--; --;
-- Delete user vm details for guest CPU mode/model which are root admin only
DELETE FROM `cloud`.`user_vm_details` WHERE `name` IN ('guest.cpu.mode','guest.cpu.model');
-- Delete template details for guest CPU mode/model which are root admin only
DELETE FROM `cloud`.`vm_template_details` WHERE `name` IN ('guest.cpu.mode','guest.cpu.model');
-- Add column api_key_access to user and account tables -- Add column api_key_access to user and account tables
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.user', 'api_key_access', 'boolean DEFAULT NULL COMMENT "is api key access allowed for the user" AFTER `secret_key`'); CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.user', 'api_key_access', 'boolean DEFAULT NULL COMMENT "is api key access allowed for the user" AFTER `secret_key`');
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.account', 'api_key_access', 'boolean DEFAULT NULL COMMENT "is api key access allowed for the account" '); CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.account', 'api_key_access', 'boolean DEFAULT NULL COMMENT "is api key access allowed for the account" ');

View File

@ -166,6 +166,7 @@ import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.RngDef.RngBackendModel;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.SCSIDef; import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.SCSIDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.SerialDef; import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.SerialDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.TermPolicy; import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.TermPolicy;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.TpmDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.VideoDef; import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.VideoDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.WatchDogDef; import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.WatchDogDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.WatchDogDef.WatchDogAction; import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.WatchDogDef.WatchDogAction;
@ -2660,6 +2661,11 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
devices.addDevice(createGraphicDef(vmTO)); devices.addDevice(createGraphicDef(vmTO));
devices.addDevice(createTabletInputDef()); devices.addDevice(createTabletInputDef());
TpmDef tpmDef = createTpmDef(vmTO);
if (tpmDef != null) {
devices.addDevice(tpmDef);
}
if (isGuestAarch64()) { if (isGuestAarch64()) {
createArm64UsbDef(devices); createArm64UsbDef(devices);
} }
@ -2840,8 +2846,11 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
private CpuModeDef createCpuModeDef(VirtualMachineTO vmTO, int vcpus) { private CpuModeDef createCpuModeDef(VirtualMachineTO vmTO, int vcpus) {
final CpuModeDef cmd = new CpuModeDef(); final CpuModeDef cmd = new CpuModeDef();
cmd.setMode(guestCpuMode); Map<String, String> details = vmTO.getDetails();
cmd.setModel(guestCpuModel); String cpuMode = MapUtils.isNotEmpty(details) && details.get(VmDetailConstants.GUEST_CPU_MODE) != null ? details.get(VmDetailConstants.GUEST_CPU_MODE) : guestCpuMode;
String cpuModel = MapUtils.isNotEmpty(details) && details.get(VmDetailConstants.GUEST_CPU_MODEL) != null ? details.get(VmDetailConstants.GUEST_CPU_MODEL) : guestCpuModel;
cmd.setMode(cpuMode);
cmd.setModel(cpuModel);
if (VirtualMachine.Type.User.equals(vmTO.getType())) { if (VirtualMachine.Type.User.equals(vmTO.getType())) {
cmd.setFeatures(cpuFeatures); cmd.setFeatures(cpuFeatures);
} }
@ -2850,6 +2859,19 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
return cmd; return cmd;
} }
protected TpmDef createTpmDef(VirtualMachineTO vmTO) {
Map<String, String> details = vmTO.getDetails();
if (MapUtils.isEmpty(details)) {
return null;
}
String tpmModel = details.get(VmDetailConstants.VIRTUAL_TPM_MODEL);
if (tpmModel == null) {
return null;
}
String tpmVersion = details.get(VmDetailConstants.VIRTUAL_TPM_VERSION);
return new TpmDef(tpmModel, tpmVersion);
}
private void configureGuestIfUefiEnabled(boolean isSecureBoot, String bootMode, GuestDef guest) { private void configureGuestIfUefiEnabled(boolean isSecureBoot, String bootMode, GuestDef guest) {
setGuestLoader(bootMode, SECURE, guest, GuestDef.GUEST_LOADER_SECURE); setGuestLoader(bootMode, SECURE, guest, GuestDef.GUEST_LOADER_SECURE);
setGuestLoader(bootMode, LEGACY, guest, GuestDef.GUEST_LOADER_LEGACY); setGuestLoader(bootMode, LEGACY, guest, GuestDef.GUEST_LOADER_LEGACY);

View File

@ -18,6 +18,7 @@ package com.cloud.hypervisor.kvm.resource;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -2358,6 +2359,82 @@ public class LibvirtVMDef {
} }
} }
public static class TpmDef {
enum TpmModel {
TIS("tpm-tis"), // TPM Interface Specification (TIS)
CRB("tpm-crb"); // Command-Response Buffer (CRB)
final String model;
TpmModel(String model) {
this.model = model;
}
@Override
public String toString() {
return model;
}
}
enum TpmVersion {
V1_2("1.2"), // 1.2
V2_0("2.0"); // 2.0. Default version. The CRB model is only supported with version 2.0.
final String version;
TpmVersion(String version) {
this.version = version;
}
@Override
public String toString() {
return version;
}
}
private TpmModel model;
private TpmVersion version = TpmVersion.V2_0;
public TpmDef(TpmModel model, TpmVersion version) {
this.model = model;
if (version != null) {
this.version = version;
}
}
public TpmDef(String model, String version) {
this.model = Arrays.stream(TpmModel.values())
.filter(tpmModel -> tpmModel.toString().equals(model))
.findFirst()
.orElse(null);
if (version != null) {
this.version = Arrays.stream(TpmVersion.values())
.filter(tpmVersion -> tpmVersion.toString().equals(version))
.findFirst()
.orElse(this.version);;
}
}
public TpmModel getModel() {
return model;
}
public TpmVersion getVersion() {
return version;
}
@Override
public String toString() {
StringBuilder tpmBuidler = new StringBuilder();
if (model != null) {
tpmBuidler.append("<tpm model='").append(model).append("'>\n");
tpmBuidler.append("<backend type='emulator' version='").append(version).append("'/>\n");
tpmBuidler.append("</tpm>\n");
}
return tpmBuidler.toString();
}
}
public void setHvsType(String hvs) { public void setHvsType(String hvs) {
_hvsType = hvs; _hvsType = hvs;
} }

View File

@ -6541,4 +6541,28 @@ public class LibvirtComputingResourceTest {
DiskDef.DiskBus diskBus = libvirtComputingResourceSpy.getDiskModelFromVMDetail(virtualMachineTO); DiskDef.DiskBus diskBus = libvirtComputingResourceSpy.getDiskModelFromVMDetail(virtualMachineTO);
assertEquals(DiskDef.DiskBus.VIRTIOBLK, diskBus); assertEquals(DiskDef.DiskBus.VIRTIOBLK, diskBus);
} }
@Test
public void testCreateTpmDef() {
VirtualMachineTO virtualMachineTO = Mockito.mock(VirtualMachineTO.class);
Map<String, String> details = new HashMap<>();
details.put(VmDetailConstants.VIRTUAL_TPM_MODEL, "tpm-tis");
details.put(VmDetailConstants.VIRTUAL_TPM_VERSION, "2.0");
Mockito.when(virtualMachineTO.getDetails()).thenReturn(details);
LibvirtVMDef.TpmDef tpmDef = libvirtComputingResourceSpy.createTpmDef(virtualMachineTO);
assertEquals(LibvirtVMDef.TpmDef.TpmModel.TIS, tpmDef.getModel());
assertEquals(LibvirtVMDef.TpmDef.TpmVersion.V2_0, tpmDef.getVersion());
}
@Test
public void testCreateTpmDefWithInvalidVersion() {
VirtualMachineTO virtualMachineTO = Mockito.mock(VirtualMachineTO.class);
Map<String, String> details = new HashMap<>();
details.put(VmDetailConstants.VIRTUAL_TPM_MODEL, "tpm-crb");
details.put(VmDetailConstants.VIRTUAL_TPM_VERSION, "3.0");
Mockito.when(virtualMachineTO.getDetails()).thenReturn(details);
LibvirtVMDef.TpmDef tpmDef = libvirtComputingResourceSpy.createTpmDef(virtualMachineTO);
assertEquals(LibvirtVMDef.TpmDef.TpmModel.CRB, tpmDef.getModel());
assertEquals(LibvirtVMDef.TpmDef.TpmVersion.V2_0, tpmDef.getVersion());
}
} }

View File

@ -566,9 +566,20 @@ public class LibvirtVMDefTest extends TestCase {
assertEquals("<cpu><topology sockets='4' cores='2' threads='1' /></cpu>", cpuModeDef.toString()); assertEquals("<cpu><topology sockets='4' cores='2' threads='1' /></cpu>", cpuModeDef.toString());
} }
@Test
public void testTopologyNoInfo() { public void testTopologyNoInfo() {
LibvirtVMDef.CpuModeDef cpuModeDef = new LibvirtVMDef.CpuModeDef(); LibvirtVMDef.CpuModeDef cpuModeDef = new LibvirtVMDef.CpuModeDef();
cpuModeDef.setTopology(-1, -1, 4); cpuModeDef.setTopology(-1, -1, 4);
assertEquals("<cpu></cpu>", cpuModeDef.toString()); assertEquals("<cpu></cpu>", cpuModeDef.toString());
} }
@Test
public void testTpmModel() {
LibvirtVMDef.TpmDef tpmDef = new LibvirtVMDef.TpmDef("tpm-tis", "2.0");
assertEquals(LibvirtVMDef.TpmDef.TpmModel.TIS, tpmDef.getModel());
assertEquals(LibvirtVMDef.TpmDef.TpmVersion.V2_0, tpmDef.getVersion());
assertEquals("<tpm model='tpm-tis'>\n" +
"<backend type='emulator' version='2.0'/>\n" +
"</tpm>\n", tpmDef.toString());
}
} }

View File

@ -51,6 +51,7 @@ import javax.xml.datatype.XMLGregorianCalendar;
import com.cloud.capacity.CapacityManager; import com.cloud.capacity.CapacityManager;
import com.cloud.hypervisor.vmware.mo.HostDatastoreBrowserMO; import com.cloud.hypervisor.vmware.mo.HostDatastoreBrowserMO;
import com.vmware.vim25.Description;
import com.vmware.vim25.FileInfo; import com.vmware.vim25.FileInfo;
import com.vmware.vim25.FileQueryFlags; import com.vmware.vim25.FileQueryFlags;
import com.vmware.vim25.FolderFileInfo; import com.vmware.vim25.FolderFileInfo;
@ -58,6 +59,7 @@ import com.vmware.vim25.HostDatastoreBrowserSearchResults;
import com.vmware.vim25.HostDatastoreBrowserSearchSpec; import com.vmware.vim25.HostDatastoreBrowserSearchSpec;
import com.vmware.vim25.VirtualCdromIsoBackingInfo; import com.vmware.vim25.VirtualCdromIsoBackingInfo;
import com.vmware.vim25.VirtualMachineConfigSummary; import com.vmware.vim25.VirtualMachineConfigSummary;
import com.vmware.vim25.VirtualTPM;
import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.backup.PrepareForBackupRestorationCommand; import org.apache.cloudstack.backup.PrepareForBackupRestorationCommand;
import org.apache.cloudstack.storage.command.CopyCommand; import org.apache.cloudstack.storage.command.CopyCommand;
@ -2597,12 +2599,16 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
setBootOptions(vmSpec, bootMode, vmConfigSpec); setBootOptions(vmSpec, bootMode, vmConfigSpec);
// Config vTPM
configureVirtualTPM(vmMo, vmSpec, vmConfigSpec);
if (StringUtils.isNotEmpty(vmStoragePolicyId)) { if (StringUtils.isNotEmpty(vmStoragePolicyId)) {
vmConfigSpec.getVmProfile().add(vmProfileSpec); vmConfigSpec.getVmProfile().add(vmProfileSpec);
if (logger.isTraceEnabled()) { if (logger.isTraceEnabled()) {
logger.trace(String.format("Configuring the VM %s with storage policy: %s", vmInternalCSName, vmStoragePolicyId)); logger.trace(String.format("Configuring the VM %s with storage policy: %s", vmInternalCSName, vmStoragePolicyId));
} }
} }
// //
// Configure VM // Configure VM
// //
@ -3203,6 +3209,57 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
vmConfigSpec.getDeviceChange().add(arrayVideoCardConfigSpecs); vmConfigSpec.getDeviceChange().add(arrayVideoCardConfigSpecs);
} }
/**
* Add or Remove virtual TPM module
*
* @param vmMo virtual machine mo
* @param vmSpec virtual machine specs
* @param vmConfigSpec virtual machine config spec
* @throws Exception exception
*/
protected void configureVirtualTPM(VirtualMachineMO vmMo, VirtualMachineTO vmSpec, VirtualMachineConfigSpec vmConfigSpec) throws Exception {
String virtualTPMEnabled = vmSpec.getDetails().getOrDefault(VmDetailConstants.VIRTUAL_TPM_ENABLED, null);
if (Boolean.parseBoolean(virtualTPMEnabled)) {
for (VirtualDevice device : vmMo.getAllDeviceList()) {
if (device instanceof VirtualTPM) {
logger.debug(String.format("Virtual TPM device has already been added to VM %s, returning", vmMo.getVmName()));
return;
}
}
logger.debug(String.format("Adding Virtual TPM device to the VM %s", vmMo.getVmName()));
addVirtualTPMDevice(vmConfigSpec);
} else if (virtualTPMEnabled == null) {
logger.debug(String.format("Virtual TPM device is neither enabled nor disabled for VM %s, skipping", vmMo.getVmName()));
} else {
logger.debug(String.format("Virtual TPM device is disabled for VM %s", vmMo.getVmName()));
for (VirtualDevice device : vmMo.getAllDeviceList()) {
if (device instanceof VirtualTPM) {
logger.debug(String.format("Removing Virtual TPM device from VM %s as it is disabled", vmMo.getVmName()));
removeVirtualTPMDevice(vmConfigSpec, (VirtualTPM) device);
}
}
}
}
protected void addVirtualTPMDevice(VirtualMachineConfigSpec vmConfigSpec) {
Description description = new Description();
description.setSummary("Trusted Platform Module");
description.setLabel("Trusted Platform Module");
VirtualTPM virtualTPM = new VirtualTPM();
virtualTPM.setDeviceInfo(description);
VirtualDeviceConfigSpec deviceConfigSpec = new VirtualDeviceConfigSpec();
deviceConfigSpec.setDevice(virtualTPM);
deviceConfigSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD);
vmConfigSpec.getDeviceChange().add(deviceConfigSpec);
}
protected void removeVirtualTPMDevice(VirtualMachineConfigSpec vmConfigSpec, VirtualTPM virtualTPM) {
VirtualDeviceConfigSpec virtualDeviceConfigSpec = new VirtualDeviceConfigSpec();
virtualDeviceConfigSpec.setDevice(virtualTPM);
virtualDeviceConfigSpec.setOperation(VirtualDeviceConfigSpecOperation.REMOVE);
vmConfigSpec.getDeviceChange().add(virtualDeviceConfigSpec);
}
private void tearDownVm(VirtualMachineMO vmMo) throws Exception { private void tearDownVm(VirtualMachineMO vmMo) throws Exception {
if (vmMo == null) if (vmMo == null)

View File

@ -48,6 +48,8 @@ import com.cloud.hypervisor.vmware.util.VmwareHelper;
import com.vmware.vim25.FileInfo; import com.vmware.vim25.FileInfo;
import com.vmware.vim25.HostDatastoreBrowserSearchResults; import com.vmware.vim25.HostDatastoreBrowserSearchResults;
import com.vmware.vim25.HostDatastoreBrowserSearchSpec; import com.vmware.vim25.HostDatastoreBrowserSearchSpec;
import com.vmware.vim25.VirtualTPM;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.storage.command.CopyCommand; import org.apache.cloudstack.storage.command.CopyCommand;
import org.apache.cloudstack.storage.command.browser.ListDataStoreObjectsAnswer; import org.apache.cloudstack.storage.command.browser.ListDataStoreObjectsAnswer;
import org.apache.cloudstack.storage.command.browser.ListDataStoreObjectsCommand; import org.apache.cloudstack.storage.command.browser.ListDataStoreObjectsCommand;
@ -842,4 +844,41 @@ public class VmwareResourceTest {
assertEquals(Collections.singletonList(1L), answer.getSizes()); assertEquals(Collections.singletonList(1L), answer.getSizes());
assertEquals(Collections.singletonList(date.getTime()), answer.getLastModified()); assertEquals(Collections.singletonList(date.getTime()), answer.getLastModified());
} }
@Test
public void testAddVirtualTPMDevice() throws Exception {
VirtualMachineMO vmMo = Mockito.mock(VirtualMachineMO.class);
VirtualMachineTO vmSpec = Mockito.mock(VirtualMachineTO.class);
VirtualMachineConfigSpec vmConfigSpec = Mockito.mock(VirtualMachineConfigSpec.class);
Map<String, String> details = new HashMap<>();
details.put(ApiConstants.BootType.UEFI.toString(), "SECURE");
details.put(VmDetailConstants.VIRTUAL_TPM_ENABLED, "true");
when(vmSpec.getDetails()).thenReturn(details);
when(vmMo.getAllDeviceList()).thenReturn(new ArrayList<>());
List<VirtualDeviceConfigSpec> deviceChanges = Mockito.mock(List.class);
when(vmConfigSpec.getDeviceChange()).thenReturn(deviceChanges);
vmwareResource.configureVirtualTPM(vmMo, vmSpec, vmConfigSpec);
Mockito.verify(vmwareResource, Mockito.times(1)).addVirtualTPMDevice(vmConfigSpec);
Mockito.verify(deviceChanges, Mockito.times(1)).add(any(VirtualDeviceConfigSpec.class));
}
@Test
public void testRemoveVirtualTPMDevice() throws Exception {
VirtualMachineMO vmMo = Mockito.mock(VirtualMachineMO.class);
VirtualMachineTO vmSpec = Mockito.mock(VirtualMachineTO.class);
VirtualMachineConfigSpec vmConfigSpec = Mockito.mock(VirtualMachineConfigSpec.class);
Map<String, String> details = new HashMap<>();
details.put(ApiConstants.BootType.UEFI.toString(), "SECURE");
details.put(VmDetailConstants.VIRTUAL_TPM_ENABLED, "false");
when(vmSpec.getDetails()).thenReturn(details);
VirtualTPM tpm = new VirtualTPM();
when(vmMo.getAllDeviceList()).thenReturn(List.of(tpm));
List<VirtualDeviceConfigSpec> deviceChanges = Mockito.mock(List.class);
when(vmConfigSpec.getDeviceChange()).thenReturn(deviceChanges);
vmwareResource.configureVirtualTPM(vmMo, vmSpec, vmConfigSpec);
Mockito.verify(vmwareResource, Mockito.times(1)).removeVirtualTPMDevice(vmConfigSpec, tpm);
Mockito.verify(deviceChanges, Mockito.times(1)).add(any(VirtualDeviceConfigSpec.class));
}
} }

View File

@ -5033,6 +5033,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
final List<String> userDenyListedSettings = Stream.of(QueryService.UserVMDeniedDetails.value().split(",")) final List<String> userDenyListedSettings = Stream.of(QueryService.UserVMDeniedDetails.value().split(","))
.map(item -> (item).trim()) .map(item -> (item).trim())
.collect(Collectors.toList()); .collect(Collectors.toList());
userDenyListedSettings.addAll(QueryService.RootAdminOnlyVmSettings);
for (final String detail : userDenyListedSettings) { for (final String detail : userDenyListedSettings) {
if (options.containsKey(detail)) { if (options.containsKey(detail)) {
options.remove(detail); options.remove(detail);
@ -5083,6 +5084,10 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
options.put(VmDetailConstants.IOTHREADS, Arrays.asList("enabled")); options.put(VmDetailConstants.IOTHREADS, Arrays.asList("enabled"));
options.put(VmDetailConstants.NIC_MULTIQUEUE_NUMBER, Collections.emptyList()); options.put(VmDetailConstants.NIC_MULTIQUEUE_NUMBER, Collections.emptyList());
options.put(VmDetailConstants.NIC_PACKED_VIRTQUEUES_ENABLED, Arrays.asList("true", "false")); options.put(VmDetailConstants.NIC_PACKED_VIRTQUEUES_ENABLED, Arrays.asList("true", "false"));
options.put(VmDetailConstants.VIRTUAL_TPM_MODEL, Arrays.asList("tpm-tis", "tpm-crb"));
options.put(VmDetailConstants.VIRTUAL_TPM_VERSION, Arrays.asList("1.2", "2.0"));
options.put(VmDetailConstants.GUEST_CPU_MODE, Arrays.asList("custom", "host-model", "host-passthrough"));
options.put(VmDetailConstants.GUEST_CPU_MODEL, Collections.emptyList());
} }
if (HypervisorType.VMware.equals(hypervisorType)) { if (HypervisorType.VMware.equals(hypervisorType)) {
@ -5092,6 +5097,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
options.put(VmDetailConstants.NESTED_VIRTUALIZATION_FLAG, Arrays.asList("true", "false")); options.put(VmDetailConstants.NESTED_VIRTUALIZATION_FLAG, Arrays.asList("true", "false"));
options.put(VmDetailConstants.SVGA_VRAM_SIZE, Collections.emptyList()); options.put(VmDetailConstants.SVGA_VRAM_SIZE, Collections.emptyList());
options.put(VmDetailConstants.RAM_RESERVATION, Collections.emptyList()); options.put(VmDetailConstants.RAM_RESERVATION, Collections.emptyList());
options.put(VmDetailConstants.VIRTUAL_TPM_ENABLED, Arrays.asList("true", "false"));
} }
} }

View File

@ -2839,6 +2839,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
final List<String> userDenyListedSettings = Stream.of(QueryService.UserVMDeniedDetails.value().split(",")) final List<String> userDenyListedSettings = Stream.of(QueryService.UserVMDeniedDetails.value().split(","))
.map(item -> (item).trim()) .map(item -> (item).trim())
.collect(Collectors.toList()); .collect(Collectors.toList());
userDenyListedSettings.addAll(QueryService.RootAdminOnlyVmSettings);
final List<String> userReadOnlySettings = Stream.of(QueryService.UserVMReadOnlyDetails.value().split(",")) final List<String> userReadOnlySettings = Stream.of(QueryService.UserVMReadOnlyDetails.value().split(","))
.map(item -> (item).trim()) .map(item -> (item).trim())
.collect(Collectors.toList()); .collect(Collectors.toList());

View File

@ -559,7 +559,7 @@
</span> </span>
<div style="margin-top: 15px" v-if="showDetails"> <div style="margin-top: 15px" v-if="showDetails">
<div <div
v-if="vm.templateid && ['KVM', 'VMware', 'XenServer'].includes(hypervisor) && !template.deployasis"> v-if="['KVM', 'VMware', 'XenServer'].includes(hypervisor) && ((vm.templateid && !template.deployasis) || vm.isoid)">
<a-form-item :label="$t('label.boottype')" name="boottype" ref="boottype"> <a-form-item :label="$t('label.boottype')" name="boottype" ref="boottype">
<a-select <a-select
v-model:value="form.boottype" v-model:value="form.boottype"