diff --git a/api/src/main/java/com/cloud/vm/VmDetailConstants.java b/api/src/main/java/com/cloud/vm/VmDetailConstants.java index 29803d5271b4..a6c9b6eba16b 100644 --- a/api/src/main/java/com/cloud/vm/VmDetailConstants.java +++ b/api/src/main/java/com/cloud/vm/VmDetailConstants.java @@ -101,4 +101,13 @@ public interface VmDetailConstants { 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_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"; } diff --git a/api/src/main/java/org/apache/cloudstack/query/QueryService.java b/api/src/main/java/org/apache/cloudstack/query/QueryService.java index 88081494320c..0a5721abdc1f 100644 --- a/api/src/main/java/org/apache/cloudstack/query/QueryService.java +++ b/api/src/main/java/org/apache/cloudstack/query/QueryService.java @@ -16,6 +16,7 @@ // under the License. package org.apache.cloudstack.query; +import java.util.Arrays; import java.util.List; import org.apache.cloudstack.affinity.AffinityGroupResponse; @@ -97,6 +98,7 @@ import org.apache.cloudstack.framework.config.ConfigKey; import com.cloud.exception.PermissionDeniedException; +import com.cloud.vm.VmDetailConstants; /** * Service used for list api query. @@ -104,6 +106,8 @@ */ public interface QueryService { + List RootAdminOnlyVmSettings = Arrays.asList(VmDetailConstants.GUEST_CPU_MODE, VmDetailConstants.GUEST_CPU_MODEL); + // Config keys ConfigKey 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); diff --git a/engine/schema/src/main/resources/META-INF/db/schema-42000to42010.sql b/engine/schema/src/main/resources/META-INF/db/schema-42000to42010.sql index bf13e5eee1ac..9c1b1eac6384 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-42000to42010.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-42000to42010.sql @@ -19,6 +19,12 @@ -- 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 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" '); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java index ce40dd4b6819..13518de5cb3c 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java @@ -166,6 +166,7 @@ import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.SCSIDef; import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.SerialDef; 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.WatchDogDef; import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.WatchDogDef.WatchDogAction; @@ -2660,6 +2661,11 @@ protected DevicesDef createDevicesDef(VirtualMachineTO vmTO, GuestDef guest, int devices.addDevice(createGraphicDef(vmTO)); devices.addDevice(createTabletInputDef()); + TpmDef tpmDef = createTpmDef(vmTO); + if (tpmDef != null) { + devices.addDevice(tpmDef); + } + if (isGuestAarch64()) { createArm64UsbDef(devices); } @@ -2840,8 +2846,11 @@ public int calculateCpuShares(VirtualMachineTO vmTO) { private CpuModeDef createCpuModeDef(VirtualMachineTO vmTO, int vcpus) { final CpuModeDef cmd = new CpuModeDef(); - cmd.setMode(guestCpuMode); - cmd.setModel(guestCpuModel); + Map details = vmTO.getDetails(); + 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())) { cmd.setFeatures(cpuFeatures); } @@ -2850,6 +2859,19 @@ private CpuModeDef createCpuModeDef(VirtualMachineTO vmTO, int vcpus) { return cmd; } + protected TpmDef createTpmDef(VirtualMachineTO vmTO) { + Map 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) { setGuestLoader(bootMode, SECURE, guest, GuestDef.GUEST_LOADER_SECURE); setGuestLoader(bootMode, LEGACY, guest, GuestDef.GUEST_LOADER_LEGACY); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java index c34c151aad28..93ad084b4379 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java @@ -18,6 +18,7 @@ import java.io.File; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -2358,6 +2359,82 @@ public String toString() { } } + 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("\n"); + tpmBuidler.append("\n"); + tpmBuidler.append("\n"); + } + return tpmBuidler.toString(); + } + } + public void setHvsType(String hvs) { _hvsType = hvs; } diff --git a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java index 753bce31c258..88e0983b63e6 100644 --- a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java +++ b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java @@ -6541,4 +6541,28 @@ public void testGetDiskModelFromVMDetailVirtioBlk() { DiskDef.DiskBus diskBus = libvirtComputingResourceSpy.getDiskModelFromVMDetail(virtualMachineTO); assertEquals(DiskDef.DiskBus.VIRTIOBLK, diskBus); } + + @Test + public void testCreateTpmDef() { + VirtualMachineTO virtualMachineTO = Mockito.mock(VirtualMachineTO.class); + Map 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 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()); + } } diff --git a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDefTest.java b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDefTest.java index bcbf6a2238b8..856dc0be9dcf 100644 --- a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDefTest.java +++ b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDefTest.java @@ -566,9 +566,20 @@ public void testTopology() { assertEquals("", cpuModeDef.toString()); } + @Test public void testTopologyNoInfo() { LibvirtVMDef.CpuModeDef cpuModeDef = new LibvirtVMDef.CpuModeDef(); cpuModeDef.setTopology(-1, -1, 4); assertEquals("", 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("\n" + + "\n" + + "\n", tpmDef.toString()); + } } diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java index 08fa4b438f63..24dda4f3ae47 100644 --- a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -51,6 +51,7 @@ import com.cloud.capacity.CapacityManager; import com.cloud.hypervisor.vmware.mo.HostDatastoreBrowserMO; +import com.vmware.vim25.Description; import com.vmware.vim25.FileInfo; import com.vmware.vim25.FileQueryFlags; import com.vmware.vim25.FolderFileInfo; @@ -58,6 +59,7 @@ import com.vmware.vim25.HostDatastoreBrowserSearchSpec; import com.vmware.vim25.VirtualCdromIsoBackingInfo; import com.vmware.vim25.VirtualMachineConfigSummary; +import com.vmware.vim25.VirtualTPM; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.backup.PrepareForBackupRestorationCommand; import org.apache.cloudstack.storage.command.CopyCommand; @@ -2597,12 +2599,16 @@ protected StartAnswer execute(StartCommand cmd) { setBootOptions(vmSpec, bootMode, vmConfigSpec); + // Config vTPM + configureVirtualTPM(vmMo, vmSpec, vmConfigSpec); + if (StringUtils.isNotEmpty(vmStoragePolicyId)) { vmConfigSpec.getVmProfile().add(vmProfileSpec); if (logger.isTraceEnabled()) { logger.trace(String.format("Configuring the VM %s with storage policy: %s", vmInternalCSName, vmStoragePolicyId)); } } + // // Configure VM // @@ -3203,6 +3209,57 @@ protected void configureSpecVideoCardNewVRamSize(VirtualMachineVideoCard videoCa 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 { if (vmMo == null) diff --git a/plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/vmware/resource/VmwareResourceTest.java b/plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/vmware/resource/VmwareResourceTest.java index 58d8e5ef254a..45457756e55a 100644 --- a/plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/vmware/resource/VmwareResourceTest.java +++ b/plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/vmware/resource/VmwareResourceTest.java @@ -48,6 +48,8 @@ import com.vmware.vim25.FileInfo; import com.vmware.vim25.HostDatastoreBrowserSearchResults; 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.browser.ListDataStoreObjectsAnswer; import org.apache.cloudstack.storage.command.browser.ListDataStoreObjectsCommand; @@ -842,4 +844,41 @@ public void testExecuteWithEmptyPath() throws Exception { assertEquals(Collections.singletonList(1L), answer.getSizes()); 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 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 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 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 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)); + } } 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 26c5cd4e10f1..5cb24894d9ce 100644 --- a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java +++ b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java @@ -5012,6 +5012,7 @@ public DetailOptionsResponse listDetailOptions(final ListDetailOptionsCmd cmd) { final List userDenyListedSettings = Stream.of(QueryService.UserVMDeniedDetails.value().split(",")) .map(item -> (item).trim()) .collect(Collectors.toList()); + userDenyListedSettings.addAll(QueryService.RootAdminOnlyVmSettings); for (final String detail : userDenyListedSettings) { if (options.containsKey(detail)) { options.remove(detail); @@ -5062,6 +5063,10 @@ private void fillVMOrTemplateDetailOptions(final Map> optio options.put(VmDetailConstants.IOTHREADS, Arrays.asList("enabled")); options.put(VmDetailConstants.NIC_MULTIQUEUE_NUMBER, Collections.emptyList()); 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)) { @@ -5071,6 +5076,7 @@ private void fillVMOrTemplateDetailOptions(final Map> optio options.put(VmDetailConstants.NESTED_VIRTUALIZATION_FLAG, Arrays.asList("true", "false")); options.put(VmDetailConstants.SVGA_VRAM_SIZE, Collections.emptyList()); options.put(VmDetailConstants.RAM_RESERVATION, Collections.emptyList()); + options.put(VmDetailConstants.VIRTUAL_TPM_ENABLED, Arrays.asList("true", "false")); } } diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 021c6ff62267..cfc88de6484f 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -2840,6 +2840,7 @@ public UserVm updateVirtualMachine(UpdateVMCmd cmd) throws ResourceUnavailableEx final List userDenyListedSettings = Stream.of(QueryService.UserVMDeniedDetails.value().split(",")) .map(item -> (item).trim()) .collect(Collectors.toList()); + userDenyListedSettings.addAll(QueryService.RootAdminOnlyVmSettings); final List userReadOnlySettings = Stream.of(QueryService.UserVMReadOnlyDetails.value().split(",")) .map(item -> (item).trim()) .collect(Collectors.toList()); diff --git a/ui/src/views/compute/DeployVM.vue b/ui/src/views/compute/DeployVM.vue index 57dbe8eeabcf..eb5e4a6a9a09 100644 --- a/ui/src/views/compute/DeployVM.vue +++ b/ui/src/views/compute/DeployVM.vue @@ -559,7 +559,7 @@
+ v-if="['KVM', 'VMware', 'XenServer'].includes(hypervisor) && ((vm.templateid && !template.deployasis) || vm.isoid)">