This is an automated email from the ASF dual-hosted git repository.

rohit pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


The following commit(s) were added to refs/heads/master by this push:
     new 9f4f2c5  api: instance and template details are free text (#3240)
9f4f2c5 is described below

commit 9f4f2c5348af93eeb790fbf4908cdde4f0b7806a
Author: Rohit Yadav <[email protected]>
AuthorDate: Thu Jun 27 09:14:47 2019 +0530

    api: instance and template details are free text (#3240)
    
    Problem: Users don't know what keys/values to enter for template and VM 
details.
    Root Cause: The feature does not exist that can list possible details and 
options.
    Solution: Based on the possible VM and template details handled by the
    codebase, those details were refactored and a list API is introduced
    that can return users those details along with possible values. When
    users add details now, they will be presented with a list of key details
    and their possible options if any.
    
    Signed-off-by: Rohit Yadav <[email protected]>
---
 .../main/java/com/cloud/vm/VmDetailConstants.java  |  47 +++--
 .../user/resource/ListDetailOptionsCmd.java        |  91 ++++++++++
 .../api/response/DetailOptionsResponse.java}       |  38 +++--
 .../org/apache/cloudstack/query/QueryService.java  |   6 +-
 .../com/cloud/vm/VirtualMachineManagerImpl.java    |  36 ++--
 .../hypervisor/vmware/resource/VmwareResource.java |   2 +-
 .../xenserver/resource/CitrixResourceBase.java     |  11 +-
 server/src/main/java/com/cloud/api/ApiDBUtils.java |   3 +-
 .../java/com/cloud/api/query/QueryManagerImpl.java |  67 +++++++-
 .../com/cloud/capacity/CapacityManagerImpl.java    |  13 +-
 .../network/element/ConfigDriveNetworkElement.java |   5 +-
 .../network/element/VirtualRouterElement.java      |   3 +-
 .../com/cloud/resource/ResourceManagerImpl.java    |  22 +--
 .../com/cloud/server/ManagementServerImpl.java     |   2 +
 .../com/cloud/servlet/ConsoleProxyServlet.java     |   7 +-
 .../com/cloud/template/TemplateManagerImpl.java    |   3 +-
 .../main/java/com/cloud/vm/UserVmManagerImpl.java  |  77 ++++++---
 .../java/com/cloud/vm/UserVmManagerImplTest.java   |  17 ++
 ui/css/cloudstack3.css                             |  23 +++
 ui/scripts/globalSettings.js                       |   1 -
 ui/scripts/instances.js                            |   2 +-
 ui/scripts/roles.js                                |  41 ++---
 ui/scripts/templates.js                            |   1 +
 ui/scripts/ui-custom/granularSettings.js           | 189 +++++++++++++++------
 24 files changed, 530 insertions(+), 177 deletions(-)

diff --git a/api/src/main/java/com/cloud/vm/VmDetailConstants.java 
b/api/src/main/java/com/cloud/vm/VmDetailConstants.java
index f24c4f5..84de8c9 100644
--- a/api/src/main/java/com/cloud/vm/VmDetailConstants.java
+++ b/api/src/main/java/com/cloud/vm/VmDetailConstants.java
@@ -17,14 +17,41 @@
 package com.cloud.vm;
 
 public interface VmDetailConstants {
-    public static final String KEYBOARD = "keyboard";
-    public static final String NIC_ADAPTER = "nicAdapter";
-    public static final String ROOT_DISK_CONTROLLER = "rootDiskController";
-    public static final String NESTED_VIRTUALIZATION_FLAG = 
"nestedVirtualizationFlag";
-    public static final String HYPERVISOR_TOOLS_VERSION = 
"hypervisortoolsversion";
-    public static final String DATA_DISK_CONTROLLER = "dataDiskController";
-    public static final String SVGA_VRAM_SIZE = "svga.vramSize";
-    public static final String CPU_NUMBER = "cpuNumber";
-    public static final String CPU_SPEED = "cpuSpeed";
-    public static final String MEMORY = "memory";
+    String KEYBOARD = "keyboard";
+    String CPU_CORE_PER_SOCKET = "cpu.corespersocket";
+    String ROOT_DISK_SIZE = "rootdisksize";
+
+    // VMware specific
+    String NIC_ADAPTER = "nicAdapter";
+    String ROOT_DISK_CONTROLLER = "rootDiskController";
+    String DATA_DISK_CONTROLLER = "dataDiskController";
+    String SVGA_VRAM_SIZE = "svga.vramSize";
+    String NESTED_VIRTUALIZATION_FLAG = "nestedVirtualizationFlag";
+
+    // XenServer specific (internal)
+    String HYPERVISOR_TOOLS_VERSION = "hypervisortoolsversion";
+    String PLATFORM = "platform";
+    String TIME_OFFSET = "timeoffset";
+
+    // KVM specific (internal)
+    String KVM_VNC_PORT = "kvm.vnc.port";
+    String KVM_VNC_ADDRESS = "kvm.vnc.address";
+
+    // Mac OSX guest specific (internal)
+    String SMC_PRESENT = "smc.present";
+    String FIRMWARE = "firmware";
+
+    // VM deployment with custom compute offering params
+    String CPU_NUMBER = "cpuNumber";
+    String CPU_SPEED = "cpuSpeed";
+    String MEMORY = "memory";
+
+    // Misc details for internal usage (not to be set/changed by user or admin)
+    String CPU_OVER_COMMIT_RATIO = "cpuOvercommitRatio";
+    String MEMORY_OVER_COMMIT_RATIO = "memoryOvercommitRatio";
+    String MESSAGE_RESERVED_CAPACITY_FREED_FLAG = 
"Message.ReservedCapacityFreed.Flag";
+    String DEPLOY_VM = "deployvm";
+    String SSH_PUBLIC_KEY = "SSH.PublicKey";
+    String PASSWORD = "password";
+    String ENCRYPTED_PASSWORD = "Encrypted.Password";
 }
diff --git 
a/api/src/main/java/org/apache/cloudstack/api/command/user/resource/ListDetailOptionsCmd.java
 
b/api/src/main/java/org/apache/cloudstack/api/command/user/resource/ListDetailOptionsCmd.java
new file mode 100644
index 0000000..e53754c
--- /dev/null
+++ 
b/api/src/main/java/org/apache/cloudstack/api/command/user/resource/ListDetailOptionsCmd.java
@@ -0,0 +1,91 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.api.command.user.resource;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiArgValidator;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.response.DetailOptionsResponse;
+import org.apache.cloudstack.context.CallContext;
+
+import com.cloud.server.ResourceTag;
+import com.google.common.base.Strings;
+
+@APICommand(name = ListDetailOptionsCmd.APINAME,
+        description = "Lists all possible details and their options for a 
resource type such as a VM or a template",
+        responseObject = DetailOptionsResponse.class,
+        since = "4.13",
+        requestHasSensitiveInfo = false,
+        responseHasSensitiveInfo = false,
+        authorized = {RoleType.Admin, RoleType.ResourceAdmin, 
RoleType.DomainAdmin, RoleType.User})
+public class ListDetailOptionsCmd extends BaseCmd {
+    public final static String APINAME = "listDetailOptions";
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.RESOURCE_TYPE, type = CommandType.STRING, 
required = true,
+            description = "the resource type such as UserVm, Template etc.",
+            validations = {ApiArgValidator.NotNullOrEmpty}
+    )
+    private String resourceType;
+
+    @Parameter(name = ApiConstants.RESOURCE_ID, type = CommandType.STRING,
+            description = "the UUID of the resource (optional)")
+    private String resourceId;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public ResourceTag.ResourceObjectType getResourceType() {
+        return _taggedResourceService.getResourceType(resourceType);
+    }
+
+    public String getResourceId() {
+        if (!Strings.isNullOrEmpty(resourceId)) {
+            return _taggedResourceService.getUuid(resourceId, 
getResourceType());
+        }
+        return null;
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////////// Implementation //////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public String getCommandName() {
+        return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        return CallContext.current().getCallingAccountId();
+    }
+
+    @Override
+    public void execute() {
+        final DetailOptionsResponse response = 
_queryService.listDetailOptions(this);
+        response.setResponseName(getCommandName());
+        response.setObjectName("detailoptions");
+        setResponseObject(response);
+    }
+}
diff --git a/api/src/main/java/com/cloud/vm/VmDetailConstants.java 
b/api/src/main/java/org/apache/cloudstack/api/response/DetailOptionsResponse.java
similarity index 50%
copy from api/src/main/java/com/cloud/vm/VmDetailConstants.java
copy to 
api/src/main/java/org/apache/cloudstack/api/response/DetailOptionsResponse.java
index f24c4f5..5f6bff3 100644
--- a/api/src/main/java/com/cloud/vm/VmDetailConstants.java
+++ 
b/api/src/main/java/org/apache/cloudstack/api/response/DetailOptionsResponse.java
@@ -14,17 +14,31 @@
 // KIND, either express or implied.  See the License for the
 // specific language governing permissions and limitations
 // under the License.
-package com.cloud.vm;
+package org.apache.cloudstack.api.response;
 
-public interface VmDetailConstants {
-    public static final String KEYBOARD = "keyboard";
-    public static final String NIC_ADAPTER = "nicAdapter";
-    public static final String ROOT_DISK_CONTROLLER = "rootDiskController";
-    public static final String NESTED_VIRTUALIZATION_FLAG = 
"nestedVirtualizationFlag";
-    public static final String HYPERVISOR_TOOLS_VERSION = 
"hypervisortoolsversion";
-    public static final String DATA_DISK_CONTROLLER = "dataDiskController";
-    public static final String SVGA_VRAM_SIZE = "svga.vramSize";
-    public static final String CPU_NUMBER = "cpuNumber";
-    public static final String CPU_SPEED = "cpuSpeed";
-    public static final String MEMORY = "memory";
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseResponse;
+
+import com.cloud.serializer.Param;
+import com.google.gson.annotations.SerializedName;
+
+public class DetailOptionsResponse extends BaseResponse {
+    @SerializedName(ApiConstants.DETAILS)
+    @Param(description = "Map of all possible details and their possible list 
of values")
+    private Map<String, List<String>> details;
+
+    public DetailOptionsResponse(Map<String, List<String>> details) {
+        this.details = details;
+    }
+
+    public void setDetails(Map<String, List<String>> details) {
+        this.details = details;
+    }
+
+    public Map<String, List<String>> getDetails() {
+        return details;
+    }
 }
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 1f0f933..618a8f6 100644
--- a/api/src/main/java/org/apache/cloudstack/query/QueryService.java
+++ b/api/src/main/java/org/apache/cloudstack/query/QueryService.java
@@ -20,8 +20,8 @@ import java.util.List;
 
 import org.apache.cloudstack.affinity.AffinityGroupResponse;
 import org.apache.cloudstack.api.command.admin.domain.ListDomainsCmd;
-import org.apache.cloudstack.api.command.admin.host.ListHostsCmd;
 import org.apache.cloudstack.api.command.admin.host.ListHostTagsCmd;
+import org.apache.cloudstack.api.command.admin.host.ListHostsCmd;
 import org.apache.cloudstack.api.command.admin.internallb.ListInternalLBVMsCmd;
 import org.apache.cloudstack.api.command.admin.management.ListMgmtsCmd;
 import org.apache.cloudstack.api.command.admin.router.ListRoutersCmd;
@@ -40,6 +40,7 @@ import 
org.apache.cloudstack.api.command.user.offering.ListDiskOfferingsCmd;
 import org.apache.cloudstack.api.command.user.offering.ListServiceOfferingsCmd;
 import 
org.apache.cloudstack.api.command.user.project.ListProjectInvitationsCmd;
 import org.apache.cloudstack.api.command.user.project.ListProjectsCmd;
+import org.apache.cloudstack.api.command.user.resource.ListDetailOptionsCmd;
 import 
org.apache.cloudstack.api.command.user.securitygroup.ListSecurityGroupsCmd;
 import org.apache.cloudstack.api.command.user.tag.ListTagsCmd;
 import org.apache.cloudstack.api.command.user.template.ListTemplatesCmd;
@@ -50,6 +51,7 @@ import 
org.apache.cloudstack.api.command.user.volume.ListVolumesCmd;
 import org.apache.cloudstack.api.command.user.zone.ListZonesCmd;
 import org.apache.cloudstack.api.response.AccountResponse;
 import org.apache.cloudstack.api.response.AsyncJobResponse;
+import org.apache.cloudstack.api.response.DetailOptionsResponse;
 import org.apache.cloudstack.api.response.DiskOfferingResponse;
 import org.apache.cloudstack.api.response.DomainResponse;
 import org.apache.cloudstack.api.response.DomainRouterResponse;
@@ -147,6 +149,8 @@ public interface QueryService {
 
     ListResponse<TemplateResponse> listIsos(ListIsosCmd cmd);
 
+    DetailOptionsResponse listDetailOptions(ListDetailOptionsCmd cmd);
+
     ListResponse<AffinityGroupResponse> 
searchForAffinityGroups(ListAffinityGroupsCmd cmd);
 
     List<ResourceDetailResponse> listResourceDetails(ListResourceDetailsCmd 
cmd);
diff --git 
a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
 
b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
index 7957053..1cc925b 100755
--- 
a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
+++ 
b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
@@ -1071,16 +1071,16 @@ public class VirtualMachineManagerImpl extends 
ManagerBase implements VirtualMac
                 long destHostId = dest.getHost().getId();
                 vm.setPodIdToDeployIn(dest.getPod().getId());
                 final Long cluster_id = dest.getCluster().getId();
-                final ClusterDetailsVO cluster_detail_cpu = 
_clusterDetailsDao.findDetail(cluster_id, "cpuOvercommitRatio");
-                final ClusterDetailsVO cluster_detail_ram = 
_clusterDetailsDao.findDetail(cluster_id, "memoryOvercommitRatio");
-                //storing the value of overcommit in the vm_details table for 
doing a capacity check in case the cluster overcommit ratio is changed.
-                if (userVmDetailsDao.findDetail(vm.getId(), 
"cpuOvercommitRatio") == null &&
+                final ClusterDetailsVO cluster_detail_cpu = 
_clusterDetailsDao.findDetail(cluster_id, 
VmDetailConstants.CPU_OVER_COMMIT_RATIO);
+                final ClusterDetailsVO cluster_detail_ram = 
_clusterDetailsDao.findDetail(cluster_id, 
VmDetailConstants.MEMORY_OVER_COMMIT_RATIO);
+                //storing the value of overcommit in the user_vm_details table 
for doing a capacity check in case the cluster overcommit ratio is changed.
+                if (userVmDetailsDao.findDetail(vm.getId(), 
VmDetailConstants.CPU_OVER_COMMIT_RATIO) == null &&
                         (Float.parseFloat(cluster_detail_cpu.getValue()) > 1f 
|| Float.parseFloat(cluster_detail_ram.getValue()) > 1f)) {
-                    userVmDetailsDao.addDetail(vm.getId(), 
"cpuOvercommitRatio", cluster_detail_cpu.getValue(), true);
-                    userVmDetailsDao.addDetail(vm.getId(), 
"memoryOvercommitRatio", cluster_detail_ram.getValue(), true);
-                } else if (userVmDetailsDao.findDetail(vm.getId(), 
"cpuOvercommitRatio") != null) {
-                    userVmDetailsDao.addDetail(vm.getId(), 
"cpuOvercommitRatio", cluster_detail_cpu.getValue(), true);
-                    userVmDetailsDao.addDetail(vm.getId(), 
"memoryOvercommitRatio", cluster_detail_ram.getValue(), true);
+                    userVmDetailsDao.addDetail(vm.getId(), 
VmDetailConstants.CPU_OVER_COMMIT_RATIO, cluster_detail_cpu.getValue(), true);
+                    userVmDetailsDao.addDetail(vm.getId(), 
VmDetailConstants.MEMORY_OVER_COMMIT_RATIO, cluster_detail_ram.getValue(), 
true);
+                } else if (userVmDetailsDao.findDetail(vm.getId(), 
VmDetailConstants.CPU_OVER_COMMIT_RATIO) != null) {
+                    userVmDetailsDao.addDetail(vm.getId(), 
VmDetailConstants.CPU_OVER_COMMIT_RATIO, cluster_detail_cpu.getValue(), true);
+                    userVmDetailsDao.addDetail(vm.getId(), 
VmDetailConstants.MEMORY_OVER_COMMIT_RATIO, cluster_detail_ram.getValue(), 
true);
                 }
 
                 
vmProfile.setCpuOvercommitRatio(Float.parseFloat(cluster_detail_cpu.getValue()));
@@ -1160,8 +1160,8 @@ public class VirtualMachineManagerImpl extends 
ManagerBase implements VirtualMac
                             // Remove the information on whether it was a 
deploy vm request.The deployvm=true information
                             // is set only when the vm is being deployed. When 
a vm is started from a stop state the
                             // information isn't set,
-                            if (userVmDetailsDao.findDetail(vm.getId(), 
"deployvm") != null) {
-                                userVmDetailsDao.removeDetail(vm.getId(), 
"deployvm");
+                            if (userVmDetailsDao.findDetail(vm.getId(), 
VmDetailConstants.DEPLOY_VM) != null) {
+                                userVmDetailsDao.removeDetail(vm.getId(), 
VmDetailConstants.DEPLOY_VM);
                             }
 
                             startedVm = vm;
@@ -1455,7 +1455,7 @@ public class VirtualMachineManagerImpl extends 
ManagerBase implements VirtualMac
                     if (platform != null) {
                         final UserVmVO userVm = 
_userVmDao.findById(vm.getId());
                         _userVmDao.loadDetails(userVm);
-                        userVm.setDetail("platform", platform);
+                        userVm.setDetail(VmDetailConstants.PLATFORM, platform);
                         _userVmDao.saveDetails(userVm);
                     }
                 }
@@ -1731,7 +1731,7 @@ public class VirtualMachineManagerImpl extends 
ManagerBase implements VirtualMac
                         if (platform != null) {
                             final UserVmVO userVm = 
_userVmDao.findById(vm.getId());
                             _userVmDao.loadDetails(userVm);
-                            userVm.setDetail("platform", platform);
+                            userVm.setDetail(VmDetailConstants.PLATFORM, 
platform);
                             _userVmDao.saveDetails(userVm);
                         }
                     }
@@ -3174,16 +3174,16 @@ public class VirtualMachineManagerImpl extends 
ManagerBase implements VirtualMac
     private void updateVmMetaData(Long vmId, String platform) {
         UserVmVO userVm = _userVmDao.findById(vmId);
         _userVmDao.loadDetails(userVm);
-        if ( userVm.details.containsKey("timeoffset")) {
-            userVm.details.remove("timeoffset");
+        if ( userVm.details.containsKey(VmDetailConstants.TIME_OFFSET)) {
+            userVm.details.remove(VmDetailConstants.TIME_OFFSET);
         }
-        userVm.setDetail("platform",  platform);
+        userVm.setDetail(VmDetailConstants.PLATFORM,  platform);
         String pvdriver = "xenserver56";
         if ( platform.contains("device_id")) {
             pvdriver = "xenserver61";
         }
-        if (!userVm.details.containsKey("hypervisortoolsversion") || 
!userVm.details.get("hypervisortoolsversion").equals(pvdriver)) {
-            userVm.setDetail("hypervisortoolsversion", pvdriver);
+        if 
(!userVm.details.containsKey(VmDetailConstants.HYPERVISOR_TOOLS_VERSION) || 
!userVm.details.get(VmDetailConstants.HYPERVISOR_TOOLS_VERSION).equals(pvdriver))
 {
+            userVm.setDetail(VmDetailConstants.HYPERVISOR_TOOLS_VERSION, 
pvdriver);
         }
         _userVmDao.saveDetails(userVm);
     }
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 abbe324..3d6732c 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
@@ -1874,7 +1874,7 @@ public class VmwareResource implements 
StoragePoolResource, ServerResource, Vmwa
 
             // Check for multi-cores per socket settings
             int numCoresPerSocket = 1;
-            String coresPerSocket = 
vmSpec.getDetails().get("cpu.corespersocket");
+            String coresPerSocket = 
vmSpec.getDetails().get(VmDetailConstants.CPU_CORE_PER_SOCKET);
             if (coresPerSocket != null) {
                 String apiVersion = 
HypervisorHostHelper.getVcenterApiVersion(vmMo.getContext());
                 // Property 'numCoresPerSocket' is supported since vSphere API 
5.0
diff --git 
a/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java
 
b/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java
index ff7b0a3..79a9fb2 100644
--- 
a/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java
+++ 
b/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java
@@ -134,6 +134,7 @@ import com.cloud.utils.ssh.SSHCmdHelper;
 import com.cloud.utils.ssh.SshHelper;
 import com.cloud.vm.VirtualMachine;
 import com.cloud.vm.VirtualMachine.PowerState;
+import com.cloud.vm.VmDetailConstants;
 import com.trilead.ssh2.SCPClient;
 import com.xensource.xenapi.Bond;
 import com.xensource.xenapi.Connection;
@@ -1862,18 +1863,18 @@ public abstract class CitrixResourceBase implements 
ServerResource, HypervisorRe
 
         final Map<String, String> details = vmSpec.getDetails();
         if (details != null) {
-            final String platformstring = details.get("platform");
+            final String platformstring = 
details.get(VmDetailConstants.PLATFORM);
             if (platformstring != null && !platformstring.isEmpty()) {
                 final Map<String, String> platform = 
StringUtils.stringToMap(platformstring);
                 vm.setPlatform(conn, platform);
             } else {
-                final String timeoffset = details.get("timeoffset");
+                final String timeoffset = 
details.get(VmDetailConstants.TIME_OFFSET);
                 if (timeoffset != null) {
                     final Map<String, String> platform = vm.getPlatform(conn);
-                    platform.put("timeoffset", timeoffset);
+                    platform.put(VmDetailConstants.TIME_OFFSET, timeoffset);
                     vm.setPlatform(conn, platform);
                 }
-                final String coresPerSocket = 
details.get("cpu.corespersocket");
+                final String coresPerSocket = 
details.get(VmDetailConstants.CPU_CORE_PER_SOCKET);
                 if (coresPerSocket != null) {
                     final Map<String, String> platform = vm.getPlatform(conn);
                     platform.put("cores-per-socket", coresPerSocket);
@@ -1881,7 +1882,7 @@ public abstract class CitrixResourceBase implements 
ServerResource, HypervisorRe
                 }
             }
             if (!BootloaderType.CD.equals(vmSpec.getBootloader())) {
-                final String xenservertoolsversion = 
details.get("hypervisortoolsversion");
+                final String xenservertoolsversion = 
details.get(VmDetailConstants.HYPERVISOR_TOOLS_VERSION);
                 if ((xenservertoolsversion == null || 
!xenservertoolsversion.equalsIgnoreCase("xenserver61")) && 
vmSpec.getGpuDevice() == null) {
                     final Map<String, String> platform = vm.getPlatform(conn);
                     platform.remove("device_id");
diff --git a/server/src/main/java/com/cloud/api/ApiDBUtils.java 
b/server/src/main/java/com/cloud/api/ApiDBUtils.java
index 22ed243..40ff827 100644
--- a/server/src/main/java/com/cloud/api/ApiDBUtils.java
+++ b/server/src/main/java/com/cloud/api/ApiDBUtils.java
@@ -302,6 +302,7 @@ import com.cloud.vm.UserVmManager;
 import com.cloud.vm.UserVmVO;
 import com.cloud.vm.VMInstanceVO;
 import com.cloud.vm.VirtualMachine;
+import com.cloud.vm.VmDetailConstants;
 import com.cloud.vm.VmStats;
 import com.cloud.vm.dao.ConsoleProxyDao;
 import com.cloud.vm.dao.DomainRouterDao;
@@ -1454,7 +1455,7 @@ public class ApiDBUtils {
     }
 
     public static UserVmDetailVO  findPublicKeyByVmId(long vmId) {
-        return s_userVmDetailsDao.findDetail(vmId, "SSH.PublicKey");
+        return s_userVmDetailsDao.findDetail(vmId, 
VmDetailConstants.SSH_PUBLIC_KEY);
     }
 
     public static void getAutoScaleVmGroupPolicies(long vmGroupId, 
List<AutoScalePolicy> scaleUpPolicies, List<AutoScalePolicy> scaleDownPolicies) 
{
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 8f00318..f0596a4 100644
--- a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java
+++ b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java
@@ -17,11 +17,16 @@
 package com.cloud.api.query;
 
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 import javax.inject.Inject;
 
@@ -61,6 +66,7 @@ import 
org.apache.cloudstack.api.command.user.offering.ListDiskOfferingsCmd;
 import org.apache.cloudstack.api.command.user.offering.ListServiceOfferingsCmd;
 import 
org.apache.cloudstack.api.command.user.project.ListProjectInvitationsCmd;
 import org.apache.cloudstack.api.command.user.project.ListProjectsCmd;
+import org.apache.cloudstack.api.command.user.resource.ListDetailOptionsCmd;
 import 
org.apache.cloudstack.api.command.user.securitygroup.ListSecurityGroupsCmd;
 import org.apache.cloudstack.api.command.user.tag.ListTagsCmd;
 import org.apache.cloudstack.api.command.user.template.ListTemplatesCmd;
@@ -71,6 +77,7 @@ import 
org.apache.cloudstack.api.command.user.volume.ListVolumesCmd;
 import org.apache.cloudstack.api.command.user.zone.ListZonesCmd;
 import org.apache.cloudstack.api.response.AccountResponse;
 import org.apache.cloudstack.api.response.AsyncJobResponse;
+import org.apache.cloudstack.api.response.DetailOptionsResponse;
 import org.apache.cloudstack.api.response.DiskOfferingResponse;
 import org.apache.cloudstack.api.response.DomainResponse;
 import org.apache.cloudstack.api.response.DomainRouterResponse;
@@ -212,13 +219,16 @@ import com.cloud.utils.db.SearchBuilder;
 import com.cloud.utils.db.SearchCriteria;
 import com.cloud.utils.db.SearchCriteria.Func;
 import com.cloud.utils.db.SearchCriteria.Op;
+import com.cloud.utils.exception.CloudRuntimeException;
 import com.cloud.vm.DomainRouterVO;
 import com.cloud.vm.UserVmVO;
 import com.cloud.vm.VMInstanceVO;
 import com.cloud.vm.VirtualMachine;
+import com.cloud.vm.VmDetailConstants;
 import com.cloud.vm.dao.DomainRouterDao;
 import com.cloud.vm.dao.UserVmDao;
 import com.cloud.vm.dao.VMInstanceDao;
+import com.google.common.base.Strings;
 
 @Component
 public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements 
QueryService, Configurable {
@@ -3382,6 +3392,61 @@ public class QueryManagerImpl extends 
MutualExclusiveIdsManagerBase implements Q
     }
 
     @Override
+    public DetailOptionsResponse listDetailOptions(final ListDetailOptionsCmd 
cmd) {
+        final ResourceObjectType type = cmd.getResourceType();
+        final String resourceUuid = cmd.getResourceId();
+        final Map<String, List<String>> options = new HashMap<>();
+        switch (type) {
+            case Template:
+            case UserVm:
+                HypervisorType hypervisorType = HypervisorType.None;
+                if (!Strings.isNullOrEmpty(resourceUuid) && 
ResourceObjectType.Template.equals(type)) {
+                    hypervisorType = 
_templateDao.findByUuid(resourceUuid).getHypervisorType();
+                }
+                if (!Strings.isNullOrEmpty(resourceUuid) && 
ResourceObjectType.UserVm.equals(type)) {
+                    hypervisorType = 
_vmInstanceDao.findByUuid(resourceUuid).getHypervisorType();
+                }
+                fillVMOrTemplateDetailOptions(options, hypervisorType);
+                break;
+            default:
+                throw new CloudRuntimeException("Resource type not 
supported.");
+        }
+        if (CallContext.current().getCallingAccount().getType() != 
Account.ACCOUNT_TYPE_ADMIN) {
+            final List<String> userBlacklistedSettings = 
Stream.of(QueryService.UserVMBlacklistedDetails.value().split(","))
+                    .map(item -> (item).trim())
+                    .collect(Collectors.toList());
+            for (final String detail : userBlacklistedSettings) {
+                if (options.containsKey(detail)) {
+                    options.remove(detail);
+                }
+            }
+        }
+        return new DetailOptionsResponse(options);
+    }
+
+    private void fillVMOrTemplateDetailOptions(final Map<String, List<String>> 
options, final HypervisorType hypervisorType) {
+        if (options == null) {
+            throw new CloudRuntimeException("Invalid/null detail-options 
response object passed");
+        }
+
+        options.put(VmDetailConstants.KEYBOARD, Arrays.asList("uk", "us", 
"jp", "fr"));
+        options.put(VmDetailConstants.CPU_CORE_PER_SOCKET, 
Collections.emptyList());
+        options.put(VmDetailConstants.ROOT_DISK_SIZE, Collections.emptyList());
+
+        if (HypervisorType.KVM.equals(hypervisorType)) {
+            options.put(VmDetailConstants.ROOT_DISK_CONTROLLER, 
Arrays.asList("osdefault", "ide", "scsi", "virtio"));
+        }
+
+        if (HypervisorType.VMware.equals(hypervisorType)) {
+            options.put(VmDetailConstants.NIC_ADAPTER, Arrays.asList("E1000", 
"PCNet32", "Vmxnet2", "Vmxnet3"));
+            options.put(VmDetailConstants.ROOT_DISK_CONTROLLER, 
Arrays.asList("osdefault", "ide", "scsi", "lsilogic", "lsisas1068", "buslogic", 
"pvscsi"));
+            options.put(VmDetailConstants.DATA_DISK_CONTROLLER, 
Arrays.asList("osdefault", "ide", "scsi", "lsilogic", "lsisas1068", "buslogic", 
"pvscsi"));
+            options.put(VmDetailConstants.NESTED_VIRTUALIZATION_FLAG, 
Arrays.asList("true", "false"));
+            options.put(VmDetailConstants.SVGA_VRAM_SIZE, 
Collections.emptyList());
+        }
+    }
+
+    @Override
     public ListResponse<AffinityGroupResponse> 
searchForAffinityGroups(ListAffinityGroupsCmd cmd) {
         Pair<List<AffinityGroupJoinVO>, Integer> result = 
searchForAffinityGroupsInternal(cmd);
         ListResponse<AffinityGroupResponse> response = new 
ListResponse<AffinityGroupResponse>();
@@ -3687,7 +3752,7 @@ public class QueryManagerImpl extends 
MutualExclusiveIdsManagerBase implements Q
         }
         response.setResponses(result);
         return response;
-     }
+    }
 
     @Override
     public String getConfigComponentName() {
diff --git a/server/src/main/java/com/cloud/capacity/CapacityManagerImpl.java 
b/server/src/main/java/com/cloud/capacity/CapacityManagerImpl.java
index 6775bfc..a7fee96 100644
--- a/server/src/main/java/com/cloud/capacity/CapacityManagerImpl.java
+++ b/server/src/main/java/com/cloud/capacity/CapacityManagerImpl.java
@@ -89,6 +89,7 @@ import com.cloud.vm.VMInstanceVO;
 import com.cloud.vm.VirtualMachine;
 import com.cloud.vm.VirtualMachine.Event;
 import com.cloud.vm.VirtualMachine.State;
+import com.cloud.vm.VmDetailConstants;
 import com.cloud.vm.dao.UserVmDao;
 import com.cloud.vm.dao.UserVmDetailsDao;
 import com.cloud.vm.dao.VMInstanceDao;
@@ -138,8 +139,6 @@ public class CapacityManagerImpl extends ManagerBase 
implements CapacityManager,
     @Inject
     MessageBus _messageBus;
 
-    private static final String MESSAGE_RESERVED_CAPACITY_FREED_FLAG = 
"Message.ReservedCapacityFreed.Flag";
-
     @Override
     public boolean configure(String name, Map<String, Object> params) throws 
ConfigurationException {
         _vmCapacityReleaseInterval = 
NumbersUtil.parseInt(_configDao.getValue(Config.CapacitySkipcountingHours.key()),
 3600);
@@ -638,8 +637,8 @@ public class CapacityManagerImpl extends ManagerBase 
implements CapacityManager,
             Float ramOvercommitRatio = 1.0f;
             long secondsSinceLastUpdate = (DateUtil.currentGMTTime().getTime() 
- vm.getUpdateTime().getTime()) / 1000;
             if (secondsSinceLastUpdate < _vmCapacityReleaseInterval) {
-                UserVmDetailVO vmDetailCpu = 
_userVmDetailsDao.findDetail(vm.getId(), "cpuOvercommitRatio");
-                UserVmDetailVO vmDetailRam = 
_userVmDetailsDao.findDetail(vm.getId(), "memoryOvercommitRatio");
+                UserVmDetailVO vmDetailCpu = 
_userVmDetailsDao.findDetail(vm.getId(), 
VmDetailConstants.CPU_OVER_COMMIT_RATIO);
+                UserVmDetailVO vmDetailRam = 
_userVmDetailsDao.findDetail(vm.getId(), 
VmDetailConstants.MEMORY_OVER_COMMIT_RATIO);
                 if (vmDetailCpu != null) {
                     //if vmDetail_cpu is not null it means it is running in a 
overcommited cluster.
                     cpuOvercommitRatio = 
Float.parseFloat(vmDetailCpu.getValue());
@@ -669,14 +668,14 @@ public class CapacityManagerImpl extends ManagerBase 
implements CapacityManager,
             } else {
                 // signal if not done already, that the VM has been stopped 
for skip.counting.hours,
                 // hence capacity will not be reserved anymore.
-                UserVmDetailVO messageSentFlag = 
_userVmDetailsDao.findDetail(vm.getId(), MESSAGE_RESERVED_CAPACITY_FREED_FLAG);
+                UserVmDetailVO messageSentFlag = 
_userVmDetailsDao.findDetail(vm.getId(), 
VmDetailConstants.MESSAGE_RESERVED_CAPACITY_FREED_FLAG);
                 if (messageSentFlag == null || 
!Boolean.valueOf(messageSentFlag.getValue())) {
                     _messageBus.publish(_name, "VM_ReservedCapacity_Free", 
PublishScope.LOCAL, vm);
 
                     if (vm.getType() == VirtualMachine.Type.User) {
                         UserVmVO userVM = _userVMDao.findById(vm.getId());
                         _userVMDao.loadDetails(userVM);
-                        userVM.setDetail(MESSAGE_RESERVED_CAPACITY_FREED_FLAG, 
"true");
+                        
userVM.setDetail(VmDetailConstants.MESSAGE_RESERVED_CAPACITY_FREED_FLAG, 
"true");
                         _userVMDao.saveDetails(userVM);
                     }
                 }
@@ -903,7 +902,7 @@ public class CapacityManagerImpl extends ManagerBase 
implements CapacityManager,
           UserVmVO userVM = _userVMDao.findById(vm.getId());
           _userVMDao.loadDetails(userVM);
           // free the message sent flag if it exists
-          userVM.setDetail(MESSAGE_RESERVED_CAPACITY_FREED_FLAG, "false");
+          
userVM.setDetail(VmDetailConstants.MESSAGE_RESERVED_CAPACITY_FREED_FLAG, 
"false");
           _userVMDao.saveDetails(userVM);
 
         }
diff --git 
a/server/src/main/java/com/cloud/network/element/ConfigDriveNetworkElement.java 
b/server/src/main/java/com/cloud/network/element/ConfigDriveNetworkElement.java
index 4d2452d..76e4fc0 100644
--- 
a/server/src/main/java/com/cloud/network/element/ConfigDriveNetworkElement.java
+++ 
b/server/src/main/java/com/cloud/network/element/ConfigDriveNetworkElement.java
@@ -77,6 +77,7 @@ import com.cloud.vm.UserVmVO;
 import com.cloud.vm.VirtualMachine;
 import com.cloud.vm.VirtualMachineManager;
 import com.cloud.vm.VirtualMachineProfile;
+import com.cloud.vm.VmDetailConstants;
 import com.cloud.vm.dao.UserVmDao;
 import com.cloud.vm.dao.UserVmDetailsDao;
 
@@ -195,7 +196,7 @@ public class ConfigDriveNetworkElement extends AdapterBase 
implements NetworkEle
     }
 
     private String getSshKey(VirtualMachineProfile profile) {
-        final UserVmDetailVO vmDetailSshKey = 
_userVmDetailsDao.findDetail(profile.getId(), "SSH.PublicKey");
+        final UserVmDetailVO vmDetailSshKey = 
_userVmDetailsDao.findDetail(profile.getId(), VmDetailConstants.SSH_PUBLIC_KEY);
         return (vmDetailSshKey!=null ? vmDetailSshKey.getValue() : null);
     }
 
@@ -262,7 +263,7 @@ public class ConfigDriveNetworkElement extends AdapterBase 
implements NetworkEle
         final String password_encrypted = DBEncryptionUtil.encrypt(password);
         final UserVmVO userVmVO = _userVmDao.findById(vm.getId());
 
-        _userVmDetailsDao.addDetail(vm.getId(), "password", 
password_encrypted, false);
+        _userVmDetailsDao.addDetail(vm.getId(), VmDetailConstants.PASSWORD,  
password_encrypted, false);
 
         userVmVO.setUpdateParameters(true);
         _userVmDao.update(userVmVO.getId(), userVmVO);
diff --git 
a/server/src/main/java/com/cloud/network/element/VirtualRouterElement.java 
b/server/src/main/java/com/cloud/network/element/VirtualRouterElement.java
index b78dcfd..aefa528 100644
--- a/server/src/main/java/com/cloud/network/element/VirtualRouterElement.java
+++ b/server/src/main/java/com/cloud/network/element/VirtualRouterElement.java
@@ -31,6 +31,7 @@ import 
org.cloud.network.router.deployment.RouterDeploymentDefinitionBuilder;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
 
+import com.cloud.vm.VmDetailConstants;
 import com.google.gson.Gson;
 
 import org.apache.cloudstack.api.command.admin.router.ConfigureOvsElementCmd;
@@ -718,7 +719,7 @@ NetworkMigrationResponder, AggregatedCommandExecutor, 
RedundantResource, DnsServ
         final UserVmVO userVmVO = _userVmDao.findById(vm.getId());
 
         _userVmDao.loadDetails(userVmVO);
-        userVmVO.setDetail("password", password_encrypted);
+        userVmVO.setDetail(VmDetailConstants.PASSWORD, password_encrypted);
         _userVmDao.saveDetails(userVmVO);
 
         userVmVO.setUpdateParameters(true);
diff --git a/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java 
b/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java
index 27fa42c..d07a438 100755
--- a/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java
+++ b/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java
@@ -31,13 +31,6 @@ import java.util.concurrent.ConcurrentHashMap;
 import javax.inject.Inject;
 import javax.naming.ConfigurationException;
 
-import com.cloud.utils.Pair;
-import com.cloud.vm.dao.UserVmDetailsDao;
-import org.apache.cloudstack.framework.config.ConfigKey;
-import org.apache.commons.lang.ObjectUtils;
-import org.apache.log4j.Logger;
-import org.springframework.stereotype.Component;
-
 import org.apache.cloudstack.api.ApiConstants;
 import org.apache.cloudstack.api.command.admin.cluster.AddClusterCmd;
 import org.apache.cloudstack.api.command.admin.cluster.DeleteClusterCmd;
@@ -49,21 +42,25 @@ import 
org.apache.cloudstack.api.command.admin.host.ReconnectHostCmd;
 import org.apache.cloudstack.api.command.admin.host.UpdateHostCmd;
 import org.apache.cloudstack.api.command.admin.host.UpdateHostPasswordCmd;
 import org.apache.cloudstack.context.CallContext;
+import org.apache.cloudstack.framework.config.ConfigKey;
 import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
 import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
 import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
 import org.apache.cloudstack.utils.identity.ManagementServerNode;
 import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang.ObjectUtils;
+import org.apache.log4j.Logger;
+import org.springframework.stereotype.Component;
 
 import com.cloud.agent.AgentManager;
 import com.cloud.agent.api.Answer;
 import com.cloud.agent.api.Command;
-import com.cloud.agent.api.GetVncPortCommand;
-import com.cloud.agent.api.GetVncPortAnswer;
 import com.cloud.agent.api.GetGPUStatsAnswer;
 import com.cloud.agent.api.GetGPUStatsCommand;
 import com.cloud.agent.api.GetHostStatsAnswer;
 import com.cloud.agent.api.GetHostStatsCommand;
+import com.cloud.agent.api.GetVncPortAnswer;
+import com.cloud.agent.api.GetVncPortCommand;
 import com.cloud.agent.api.MaintainAnswer;
 import com.cloud.agent.api.MaintainCommand;
 import com.cloud.agent.api.PropagateResourceEventCommand;
@@ -148,6 +145,7 @@ import com.cloud.storage.dao.StoragePoolHostDao;
 import com.cloud.storage.dao.VMTemplateDao;
 import com.cloud.user.Account;
 import com.cloud.user.AccountManager;
+import com.cloud.utils.Pair;
 import com.cloud.utils.StringUtils;
 import com.cloud.utils.UriUtils;
 import com.cloud.utils.component.Manager;
@@ -177,6 +175,8 @@ import com.cloud.vm.VMInstanceVO;
 import com.cloud.vm.VirtualMachine;
 import com.cloud.vm.VirtualMachine.State;
 import com.cloud.vm.VirtualMachineManager;
+import com.cloud.vm.VmDetailConstants;
+import com.cloud.vm.dao.UserVmDetailsDao;
 import com.cloud.vm.dao.VMInstanceDao;
 import com.google.gson.Gson;
 
@@ -1314,8 +1314,8 @@ public class ResourceManagerImpl extends ManagerBase 
implements ResourceManager,
         for (VMInstanceVO vm : vms) {
             GetVncPortAnswer vmVncPortAnswer = (GetVncPortAnswer) 
_agentMgr.easySend(hostId, new GetVncPortCommand(vm.getId(), 
vm.getInstanceName()));
             if (vmVncPortAnswer != null) {
-                userVmDetailsDao.addDetail(vm.getId(), "kvm.vnc.address", 
vmVncPortAnswer.getAddress(), true);
-                userVmDetailsDao.addDetail(vm.getId(), "kvm.vnc.port", 
String.valueOf(vmVncPortAnswer.getPort()), true);
+                userVmDetailsDao.addDetail(vm.getId(), 
VmDetailConstants.KVM_VNC_ADDRESS, vmVncPortAnswer.getAddress(), true);
+                userVmDetailsDao.addDetail(vm.getId(), 
VmDetailConstants.KVM_VNC_PORT, String.valueOf(vmVncPortAnswer.getPort()), 
true);
             }
         }
     }
diff --git a/server/src/main/java/com/cloud/server/ManagementServerImpl.java 
b/server/src/main/java/com/cloud/server/ManagementServerImpl.java
index 2c3e715..4beddcc 100644
--- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java
+++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java
@@ -411,6 +411,7 @@ import 
org.apache.cloudstack.api.command.user.region.ha.gslb.ListGlobalLoadBalan
 import 
org.apache.cloudstack.api.command.user.region.ha.gslb.RemoveFromGlobalLoadBalancerRuleCmd;
 import 
org.apache.cloudstack.api.command.user.region.ha.gslb.UpdateGlobalLoadBalancerRuleCmd;
 import org.apache.cloudstack.api.command.user.resource.GetCloudIdentifierCmd;
+import org.apache.cloudstack.api.command.user.resource.ListDetailOptionsCmd;
 import org.apache.cloudstack.api.command.user.resource.ListHypervisorsCmd;
 import org.apache.cloudstack.api.command.user.resource.ListResourceLimitsCmd;
 import org.apache.cloudstack.api.command.user.resource.UpdateResourceCountCmd;
@@ -2900,6 +2901,7 @@ public class ManagementServerImpl extends ManagerBase 
implements ManagementServe
         cmdList.add(ExtractTemplateCmd.class);
         cmdList.add(ListTemplatePermissionsCmd.class);
         cmdList.add(ListTemplatesCmd.class);
+        cmdList.add(ListDetailOptionsCmd.class);
         cmdList.add(RegisterTemplateCmd.class);
         cmdList.add(UpdateTemplateCmd.class);
         cmdList.add(UpdateTemplatePermissionsCmd.class);
diff --git a/server/src/main/java/com/cloud/servlet/ConsoleProxyServlet.java 
b/server/src/main/java/com/cloud/servlet/ConsoleProxyServlet.java
index 8cfaa9f..5a6c84f 100644
--- a/server/src/main/java/com/cloud/servlet/ConsoleProxyServlet.java
+++ b/server/src/main/java/com/cloud/servlet/ConsoleProxyServlet.java
@@ -41,6 +41,7 @@ import org.apache.log4j.Logger;
 import org.springframework.stereotype.Component;
 import org.springframework.web.context.support.SpringBeanAutowiringSupport;
 
+import com.cloud.vm.VmDetailConstants;
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
 
@@ -421,8 +422,8 @@ public class ConsoleProxyServlet extends HttpServlet {
 
         Pair<String, Integer> portInfo;
         if 
(hostVo.getResourceState().equals(ResourceState.ErrorInMaintenance)) {
-            UserVmDetailVO detailAddress = 
_userVmDetailsDao.findDetail(vm.getId(), "kvm.vnc.address");
-            UserVmDetailVO detailPort = 
_userVmDetailsDao.findDetail(vm.getId(), "kvm.vnc.port");
+            UserVmDetailVO detailAddress = 
_userVmDetailsDao.findDetail(vm.getId(), VmDetailConstants.KVM_VNC_ADDRESS);
+            UserVmDetailVO detailPort = 
_userVmDetailsDao.findDetail(vm.getId(), VmDetailConstants.KVM_VNC_PORT);
             portInfo = new Pair<>(detailAddress.getValue(), 
Integer.valueOf(detailPort.getValue()));
         } else {
             portInfo = _ms.getVncPort(vm);
@@ -441,7 +442,7 @@ public class ConsoleProxyServlet extends HttpServlet {
         }
 
         String sid = vm.getVncPassword();
-        UserVmDetailVO details = _userVmDetailsDao.findDetail(vm.getId(), 
"keyboard");
+        UserVmDetailVO details = _userVmDetailsDao.findDetail(vm.getId(), 
VmDetailConstants.KEYBOARD);
 
         String tag = vm.getUuid();
 
diff --git a/server/src/main/java/com/cloud/template/TemplateManagerImpl.java 
b/server/src/main/java/com/cloud/template/TemplateManagerImpl.java
index 9beeb7b..5eb96aa 100755
--- a/server/src/main/java/com/cloud/template/TemplateManagerImpl.java
+++ b/server/src/main/java/com/cloud/template/TemplateManagerImpl.java
@@ -39,6 +39,7 @@ import com.cloud.utils.EncryptionUtil;
 import com.cloud.utils.DateUtil;
 import com.cloud.utils.Pair;
 import com.cloud.utils.EnumUtils;
+import com.cloud.vm.VmDetailConstants;
 import com.google.common.base.Joiner;
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
@@ -1918,7 +1919,7 @@ public class TemplateManagerImpl extends ManagerBase 
implements TemplateManager,
                 }
             }
             if (cmd.getDetails() != null) {
-                details.remove("Encrypted.Password"); // new password will be 
generated during vm deployment from password enabled template
+                details.remove(VmDetailConstants.ENCRYPTED_PASSWORD); // new 
password will be generated during vm deployment from password enabled template
                 details.putAll(cmd.getDetails());
             }
             if (!details.isEmpty()) {
diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java 
b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
index 1064415..0727a2a 100644
--- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
+++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
@@ -35,6 +35,7 @@ import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 import javax.inject.Inject;
 import javax.naming.ConfigurationException;
@@ -85,6 +86,7 @@ import org.apache.cloudstack.framework.config.ConfigKey;
 import org.apache.cloudstack.framework.config.Configurable;
 import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
 import org.apache.cloudstack.managed.context.ManagedContextRunnable;
+import org.apache.cloudstack.query.QueryService;
 import org.apache.cloudstack.storage.command.DeleteCommand;
 import org.apache.cloudstack.storage.command.DettachCommand;
 import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
@@ -847,7 +849,7 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
         } else {
             final UserVmVO userVm = _vmDao.findById(vmId);
             _vmDao.loadDetails(userVm);
-            userVm.setDetail("SSH.PublicKey", sshPublicKey);
+            userVm.setDetail(VmDetailConstants.SSH_PUBLIC_KEY, sshPublicKey);
             if (template.isEnablePassword()) {
                 userVm.setPassword(password);
                 //update the encrypted password in vm_details table too
@@ -2426,11 +2428,36 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
         if (isDisplayVm != null && isDisplayVm != vmInstance.isDisplay()) {
             updateDisplayVmFlag(isDisplayVm, id, vmInstance);
         }
+        final Account caller = CallContext.current().getCallingAccount();
+        final List<String> userBlacklistedSettings = 
Stream.of(QueryService.UserVMBlacklistedDetails.value().split(","))
+                .map(item -> (item).trim())
+                .collect(Collectors.toList());
         if (cleanupDetails){
-            userVmDetailsDao.removeDetails(id);
-        }
-        else {
+            if (caller != null && caller.getType() == 
Account.ACCOUNT_TYPE_ADMIN) {
+                userVmDetailsDao.removeDetails(id);
+            } else {
+                for (final UserVmDetailVO detail : 
userVmDetailsDao.listDetails(id)) {
+                    if (detail != null && 
!userBlacklistedSettings.contains(detail.getName())) {
+                        userVmDetailsDao.removeDetail(id, detail.getName());
+                    }
+                }
+            }
+        } else {
             if (MapUtils.isNotEmpty(details)) {
+                if (caller != null && caller.getType() != 
Account.ACCOUNT_TYPE_ADMIN) {
+                    // Ensure blacklisted detail is not passed by 
non-root-admin user
+                    for (final String detailName : details.keySet()) {
+                        if (userBlacklistedSettings.contains(detailName)) {
+                            throw new InvalidParameterValueException("You're 
not allowed to add or edit the restricted setting: " + detailName);
+                        }
+                    }
+                    // Add any hidden/blacklisted detail
+                    for (final UserVmDetailVO detail : 
userVmDetailsDao.listDetails(id)) {
+                        if 
(userBlacklistedSettings.contains(detail.getName())) {
+                            details.put(detail.getName(), detail.getValue());
+                        }
+                    }
+                }
                 vmInstance.setDetails(details);
                 _vmDao.saveDetails(vmInstance);
             }
@@ -3379,13 +3406,13 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
         boolean isIso = Storage.ImageFormat.ISO == template.getFormat();
         long size = 0;
         // custom root disk size, resizes base template to larger size
-        if (customParameters.containsKey("rootdisksize")) {
+        if (customParameters.containsKey(VmDetailConstants.ROOT_DISK_SIZE)) {
             // only KVM, XenServer and VMware supports rootdisksize override
             if (!(hypervisorType == HypervisorType.KVM || hypervisorType == 
HypervisorType.XenServer || hypervisorType == HypervisorType.VMware || 
hypervisorType == HypervisorType.Simulator)) {
                 throw new InvalidParameterValueException("Hypervisor " + 
hypervisorType + " does not support rootdisksize override");
             }
 
-            Long rootDiskSize = 
NumbersUtil.parseLong(customParameters.get("rootdisksize"), -1);
+            Long rootDiskSize = 
NumbersUtil.parseLong(customParameters.get(VmDetailConstants.ROOT_DISK_SIZE), 
-1);
             if (rootDiskSize <= 0) {
                 throw new InvalidParameterValueException("Root disk size 
should be a positive number.");
             }
@@ -3768,7 +3795,7 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
                 }
 
                 if (sshPublicKey != null) {
-                    vm.setDetail("SSH.PublicKey", sshPublicKey);
+                    vm.setDetail(VmDetailConstants.SSH_PUBLIC_KEY, 
sshPublicKey);
                 }
 
                 if (keyboard != null && !keyboard.isEmpty()) {
@@ -3780,9 +3807,9 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
                 }
                 Long rootDiskSize = null;
                 // custom root disk size, resizes base template to larger size
-                if (customParameters.containsKey("rootdisksize")) {
+                if 
(customParameters.containsKey(VmDetailConstants.ROOT_DISK_SIZE)) {
                     // already verified for positive number
-                    rootDiskSize = 
Long.parseLong(customParameters.get("rootdisksize"));
+                    rootDiskSize = 
Long.parseLong(customParameters.get(VmDetailConstants.ROOT_DISK_SIZE));
 
                     VMTemplateVO templateVO = 
_templateDao.findById(template.getId());
                     if (templateVO == null) {
@@ -3806,10 +3833,10 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
                 // If hypervisor is vSphere and OS is OS X, set special 
settings.
                 if (hypervisorType.equals(HypervisorType.VMware)) {
                     if (guestOS.getDisplayName().toLowerCase().contains("apple 
mac os")) {
-                        vm.setDetail("smc.present", "TRUE");
+                        vm.setDetail(VmDetailConstants.SMC_PRESENT, "TRUE");
                         vm.setDetail(VmDetailConstants.ROOT_DISK_CONTROLLER, 
"scsi");
                         vm.setDetail(VmDetailConstants.DATA_DISK_CONTROLLER, 
"scsi");
-                        vm.setDetail("firmware", "efi");
+                        vm.setDetail(VmDetailConstants.FIRMWARE, "efi");
                         s_logger.info("guestOS is OSX : overwrite root disk 
controller to scsi, use smc and efi");
                     } else {
                         String controllerSetting = 
_configDao.getValue("vmware.root.disk.controller");
@@ -3838,7 +3865,7 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
                         vm.setDetail(key, customParameters.get(key));
                     }
                 }
-                vm.setDetail("deployvm", "true");
+                vm.setDetail(VmDetailConstants.DEPLOY_VM, "true");
                 _vmDao.saveDetails(vm);
 
                 s_logger.debug("Allocating in the DB for vm");
@@ -3888,10 +3915,10 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
             s_logger.error(error);
             throw new InvalidParameterValueException(error);
         } else if ((rootDiskSize << 30) > templateVO.getSize()) {
-            if (hypervisorType == HypervisorType.VMware && (vm.getDetails() == 
null || vm.getDetails().get("rootDiskController") == null)) {
+            if (hypervisorType == HypervisorType.VMware && (vm.getDetails() == 
null || vm.getDetails().get(VmDetailConstants.ROOT_DISK_CONTROLLER) == null)) {
                 s_logger.warn("If Root disk controller parameter is not 
overridden, then Root disk resize may fail because current Root disk controller 
value is NULL.");
-            } else if (hypervisorType == HypervisorType.VMware && 
!vm.getDetails().get("rootDiskController").toLowerCase().contains("scsi")) {
-                String error = "Found unsupported root disk controller: " + 
vm.getDetails().get("rootDiskController");
+            } else if (hypervisorType == HypervisorType.VMware && 
!vm.getDetails().get(VmDetailConstants.ROOT_DISK_CONTROLLER).toLowerCase().contains("scsi"))
 {
+                String error = "Found unsupported root disk controller: " + 
vm.getDetails().get(VmDetailConstants.ROOT_DISK_CONTROLLER);
                 s_logger.error(error);
                 throw new InvalidParameterValueException(error);
             } else {
@@ -3899,7 +3926,7 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
             }
         } else {
             s_logger.debug("Root disk size specified is " + (rootDiskSize << 
30) + "B and Template root disk size is " + templateVO.getSize() + "B. Both are 
equal so no need to override");
-            customParameters.remove("rootdisksize");
+            customParameters.remove(VmDetailConstants.ROOT_DISK_SIZE);
         }
     }
 
@@ -4182,7 +4209,7 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
                 boolean isWindows = 
_guestOSCategoryDao.findById(_guestOSDao.findById(vm.getGuestOSId()).getCategoryId()).getName().equalsIgnoreCase("Windows");
 
                 List<String[]> vmData = 
_networkModel.generateVmData(vm.getUserData(), serviceOffering, 
vm.getDataCenterId(), vm.getInstanceName(), vm.getHostName(), vm.getId(),
-                        vm.getUuid(), defaultNic.getIPv4Address(), 
vm.getDetail("SSH.PublicKey"), (String) 
profile.getParameter(VirtualMachineProfile.Param.VmPassword), isWindows);
+                        vm.getUuid(), defaultNic.getIPv4Address(), 
vm.getDetail(VmDetailConstants.SSH_PUBLIC_KEY), (String) 
profile.getParameter(VirtualMachineProfile.Param.VmPassword), isWindows);
                 String vmName = vm.getInstanceName();
                 String configDriveIsoRootFolder = "/tmp";
                 String isoFile = configDriveIsoRootFolder + "/" + vmName + 
"/configDrive/" + vmName + ".iso";
@@ -4574,8 +4601,8 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
             // this value is not being sent to the backend; need only for api
             // display purposes
             if (template.isEnablePassword()) {
-                if (vm.getDetail("password") != null) {
-                    userVmDetailsDao.removeDetail(vm.getId(), "password");
+                if (vm.getDetail(VmDetailConstants.PASSWORD) != null) {
+                    userVmDetailsDao.removeDetail(vm.getId(), 
VmDetailConstants.PASSWORD);
                 }
                 vm.setUpdateParameters(false);
                 _vmDao.update(vm.getId(), vm);
@@ -6334,7 +6361,7 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
 
             if (needRestart) {
                 try {
-                    if (vm.getDetail("password") != null) {
+                    if (vm.getDetail(VmDetailConstants.PASSWORD) != null) {
                         params = new HashMap<VirtualMachineProfile.Param, 
Object>();
                         params.put(VirtualMachineProfile.Param.VmPassword, 
password);
                     }
@@ -6347,8 +6374,8 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
                         if (vm.isUpdateParameters()) {
                             vm.setUpdateParameters(false);
                             _vmDao.loadDetails(vm);
-                            if (vm.getDetail("password") != null) {
-                                userVmDetailsDao.removeDetail(vm.getId(), 
"password");
+                            if (vm.getDetail(VmDetailConstants.PASSWORD) != 
null) {
+                                userVmDetailsDao.removeDetail(vm.getId(), 
VmDetailConstants.PASSWORD);
                             }
                             _vmDao.update(vm.getId(), vm);
                         }
@@ -6526,7 +6553,7 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
     }
 
     private void encryptAndStorePassword(UserVmVO vm, String password) {
-        String sshPublicKey = vm.getDetail("SSH.PublicKey");
+        String sshPublicKey = vm.getDetail(VmDetailConstants.SSH_PUBLIC_KEY);
         if (sshPublicKey != null && !sshPublicKey.equals("") && password != 
null && !password.equals("saved_password")) {
             if (!sshPublicKey.startsWith("ssh-rsa")) {
                 s_logger.warn("Only RSA public keys can be used to encrypt a 
vm password.");
@@ -6537,7 +6564,7 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
                 throw new CloudRuntimeException("Error encrypting password");
             }
 
-            vm.setDetail("Encrypted.Password", encryptedPasswd);
+            vm.setDetail(VmDetailConstants.ENCRYPTED_PASSWORD, 
encryptedPasswd);
             _vmDao.saveDetails(vm);
         }
     }
@@ -6569,7 +6596,7 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
     public String getVmUserData(long vmId) {
         UserVmVO vm = _vmDao.findById(vmId);
         if (vm == null) {
-            throw new InvalidParameterValueException("Unable to find virual 
machine with id " + vmId);
+            throw new InvalidParameterValueException("Unable to find virtual 
machine with id " + vmId);
         }
 
         _accountMgr.checkAccess(CallContext.current().getCallingAccount(), 
null, true, vm);
diff --git a/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java 
b/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java
index 2744714..965377b 100644
--- a/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java
+++ b/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java
@@ -22,6 +22,7 @@ import java.util.HashMap;
 import org.apache.cloudstack.api.BaseCmd.HTTPMethod;
 import org.apache.cloudstack.api.command.user.vm.UpdateVMCmd;
 import org.apache.cloudstack.context.CallContext;
+import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
@@ -44,6 +45,8 @@ import com.cloud.storage.GuestOSVO;
 import com.cloud.storage.dao.GuestOSDao;
 import com.cloud.user.Account;
 import com.cloud.user.AccountManager;
+import com.cloud.user.AccountVO;
+import com.cloud.user.UserVO;
 import com.cloud.uservm.UserVm;
 import com.cloud.vm.dao.UserVmDao;
 import com.cloud.vm.dao.UserVmDetailsDao;
@@ -76,6 +79,12 @@ public class UserVmManagerImplTest {
     @Mock
     private NetworkModel networkModel;
 
+    @Mock
+    private AccountVO callerAccount;
+
+    @Mock
+    private UserVO callerUser;
+
     private long vmId = 1l;
 
     @Before
@@ -83,6 +92,14 @@ public class UserVmManagerImplTest {
         Mockito.when(updateVmCommand.getId()).thenReturn(vmId);
 
         
Mockito.when(userVmDao.findById(Mockito.eq(vmId))).thenReturn(userVmVoMock);
+
+        
Mockito.when(callerAccount.getType()).thenReturn(Account.ACCOUNT_TYPE_ADMIN);
+        CallContext.register(callerUser, callerAccount);
+    }
+
+    @After
+    public void afterTest() {
+        CallContext.unregister();
     }
 
     @Test
diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css
index a85dee1..0d2aca6 100644
--- a/ui/css/cloudstack3.css
+++ b/ui/css/cloudstack3.css
@@ -13015,6 +13015,29 @@ div.gpugroups div.list-view {
   background: transparent url("../images/icons.png") no-repeat -626px -209px;
 }
 
+ul.ui-autocomplete.ui-menu {
+  width: 250px;
+  max-height: 100px;
+  background: #eee;
+  padding: 5px;
+  text-align: left;
+  overflow-y: auto;
+  overflow-x: hidden;
+  z-index: 100;
+}
+
+.ui-menu .ui-menu-item {
+  cursor: pointer;
+}
+
+.ui-menu .ui-menu-item .ui-state-active {
+  background: #CBDDF3;
+}
+
+.ui-helper-hidden-accessible {
+  display: none;
+}
+
 .copy-template-destination-list div.text-search {
   right: 5px;
 }
diff --git a/ui/scripts/globalSettings.js b/ui/scripts/globalSettings.js
index ab03978..3e926ea 100644
--- a/ui/scripts/globalSettings.js
+++ b/ui/scripts/globalSettings.js
@@ -168,7 +168,6 @@
                                 }],
                                 dataProvider: function(args) {
                                     var items = [];
-                                    console.log(args);
                                     $.ajax({
                                         url: 
createURL("listLdapConfigurations&hostname=" + 
args.context.ldapConfiguration[0].hostname),
                                         dataType: "json",
diff --git a/ui/scripts/instances.js b/ui/scripts/instances.js
index 4cc1dbb..0a89fdc 100644
--- a/ui/scripts/instances.js
+++ b/ui/scripts/instances.js
@@ -3329,6 +3329,7 @@
                                        settings: {
                                                title: 'label.settings',
                                                custom: 
cloudStack.uiCustom.granularDetails({
+                        resourceType: 'UserVm',
                                                        dataProvider: 
function(args) {
                                                                $.ajax({
                                                                        url: 
createURL('listVirtualMachines&id=' + args.context.instances[0].id),
@@ -3388,7 +3389,6 @@
                                         // It could happen that a stale web 
page has been opened up when VM was stopped but
                                         // vm was turned on through another 
route - UI or API. so we should check again.
                                         var existingDetails = 
virtualMachine.details;
-                                        console.log(existingDetails);
                                         var newDetails = {};
                                         for (d in existingDetails) {
                                             if (d != data.name) {
diff --git a/ui/scripts/roles.js b/ui/scripts/roles.js
index 59072bd..ff89dc5 100644
--- a/ui/scripts/roles.js
+++ b/ui/scripts/roles.js
@@ -327,31 +327,24 @@
                                             });
                                         }
                                     });
-                                    var setupAutocompletion = function() {
-                                        var $target = 
$($.find('input[name="rule"]'));
-                                        if 
($target.hasClass('ui-autocomplete')) {
-                                            $target.autocomplete('destroy');
+                                    $.ajax({
+                                        url: createURL("listApis"),
+                                        dataType: "json",
+                                        success: function(json) {
+                                            var apis = [];
+                                            var response = 
json.listapisresponse.api;
+                                            $.each(response, function(idx, 
api) {
+                                                apis.push(api.name);
+                                            });
+                                            
$($.find('input[name="rule"]')).autocomplete({
+                                                minLength: 0,
+                                                delay: 0,
+                                                source: apis.sort()
+                                            }).focus(function() {
+                                                
$(this).data("uiAutocomplete").search($(this).val());
+                                            });
                                         }
-                                        
$($.find('input[name="rule"]')).autocomplete({
-                                            source: apiList,
-                                            autoFocus:true
-                                        });
-                                    };
-                                    if (apiList.length == 0) {
-                                        $.ajax({
-                                            url: createURL("listApis"),
-                                            dataType: "json",
-                                            success: function(json) {
-                                                var response = 
json.listapisresponse.api;
-                                                $.each(response, function(idx, 
api) {
-                                                    apiList.push(api.name);
-                                                });
-                                                setupAutocompletion();
-                                            }
-                                        });
-                                    } else {
-                                        setupAutocompletion();
-                                    }
+                                    });
                                 }
                             });
                         }
diff --git a/ui/scripts/templates.js b/ui/scripts/templates.js
index 28af9d3..df04000 100755
--- a/ui/scripts/templates.js
+++ b/ui/scripts/templates.js
@@ -2170,6 +2170,7 @@
                                                        settings: {
                                                                title: 
'label.settings',
                                                                custom: 
cloudStack.uiCustom.granularDetails({
+                                    resourceType: 'Template',
                                                                        
dataProvider: function(args) {
                                                                                
$.ajax({
                                                                                
        url: createURL('listTemplates'),
diff --git a/ui/scripts/ui-custom/granularSettings.js 
b/ui/scripts/ui-custom/granularSettings.js
index 5312394..42177e3 100644
--- a/ui/scripts/ui-custom/granularSettings.js
+++ b/ui/scripts/ui-custom/granularSettings.js
@@ -54,9 +54,10 @@
             return $listView;
         }
     };
-       cloudStack.uiCustom.granularDetails = function(args) {
+    cloudStack.uiCustom.granularDetails = function(args) {
         var dataProvider = args.dataProvider;
         var actions = args.actions;
+        var resourceType = args.resourceType;
 
         return function(args) {
             var context = args.context;
@@ -77,57 +78,141 @@
                         label: 'label.change.value',
                         action: actions.edit
                     },
-                                       remove: {
-                                               label: 'Remove Setting',
-                                               messages: {
-                                                       confirm: function(args) 
{
-                                                               return 'Delete 
Setting';
-                                                       },
-                                                       notification: 
function(args) {
-                                                               return 'Setting 
deleted';
-                                                       }
-                                               },
-                                               action: actions.remove,
-                                               notification: {
-                                                       poll: function(args) {
-                                                               args.complete();
-                                                       }
-                                               }
-                                       },
-                                       add : {
-                                               label: 'Add Setting',
-                                               messages: {
-                                                       confirm: function(args) 
{
-                                                               return 'Add 
Setting';
-                                                       },
-                                                       notification: 
function(args) {
-                                                               return 'Setting 
added';
-                                                       }
-                                               },
-                                               preFilter: function(args) {
-                                                       return true;
-                                               },
-                                               createForm: {
-                                                       title: 'Add New 
Setting',
-                                                       fields: {
-                                                               name: {
-                                                                       label: 
'label.name',
-                                                                       
validation: {
-                                                                               
required: true
-                                                                       }
-                                                               },
-                                                               value: {
-                                                                       label: 
'label.value',
-                                                                       
validation: {
-                                                                               
required: true
-                                                                       }
-                                                               }
-                                                       }
-                                               },
-                                               action: actions.add
-                                       }
+                    remove: {
+                        label: 'Remove Setting',
+                        messages: {
+                            confirm: function(args) {
+                                return 'Delete Setting';
+                            },
+                            notification: function(args) {
+                                return 'Setting deleted';
+                            }
+                        },
+                        action: actions.remove,
+                        notification: {
+                            poll: function(args) {
+                                args.complete();
+                            }
+                        }
+                    },
+                    add : {
+                        label: 'Add Setting',
+                        messages: {
+                            confirm: function(args) {
+                                return 'Add Setting';
+                            },
+                            notification: function(args) {
+                                return 'Setting added';
+                            }
+                        },
+                        preFilter: function(args) {
+                            return true;
+                        },
+                        createForm: {
+                            title: 'Add New Setting',
+                            preFilter: function(args) {
+                                var data = {
+                                    resourcetype: resourceType
+                                };
+                                if (resourceType === 'UserVm') {
+                                    data.resourceid = 
args.context.instances[0].id;
+                                }
+                                if (resourceType === 'Template') {
+                                    data.resourceid = 
args.context.templates[0].id;
+                                }
+
+                                $.ajax({
+                                    url: createURL("listDetailOptions"),
+                                    data: data,
+                                    dataType: "json",
+                                    success: function(json) {
+                                        var details = 
json.listdetailoptionsresponse.detailoptions.details;
+                                        var keys = [];
+                                        
Object.keys(details).forEach(function(key,index) {
+                                            keys.push(key);
+                                        });
+                                        
$(args.$form.find('input[name="name"]')).blur();
+                                        
$(args.$form.find('input[name="name"]')).autocomplete({
+                                            minLength: 0,
+                                            delay: 0,
+                                            source: keys.sort(),
+                                            select: function(event, ui) {
+                                                const key = ui.item.value;
+                                                const options = details[key];
+                                                
$(args.$form.find('input[name="value"]')).autocomplete({
+                                                    minLength: 0,
+                                                    delay: 0,
+                                                    autoFocus: true,
+                                                    source: options.sort()
+                                                }).focus(function() {
+                                                    
$(this).data("uiAutocomplete").search($(this).val());
+                                                });
+                                            }
+                                        }).focus(function() {
+                                            
$(this).data("uiAutocomplete").search($(this).val());
+                                        });
+                                    }
+                                });
+
+                                return true;
+                            },
+                            fields: {
+                                name: {
+                                    label: 'label.name',
+                                    validation: {
+                                        required: true
+                                    }
+                                },
+                                value: {
+                                    label: 'label.value',
+                                    validation: {
+                                        required: true
+                                    }
+                                }
+                            }
+                        },
+                        action: actions.add
+                    }
                 },
-                dataProvider: dataProvider
+                dataProvider: function(args) {
+                    var data = {
+                        resourcetype: resourceType
+                    };
+                    if (resourceType === 'UserVm') {
+                        data.resourceid = args.context.instances[0].id;
+                    }
+                    if (resourceType === 'Template') {
+                        data.resourceid = args.context.templates[0].id;
+                    }
+
+                    $.ajax({
+                        url: createURL("listDetailOptions"),
+                        data: data,
+                        dataType: "json",
+                        success: function(json) {
+                            var details = 
json.listdetailoptionsresponse.detailoptions.details;
+                            var tbody = $.find('#details-tab-settings 
.data-table tbody');
+                            $(tbody).on('DOMNodeInserted', "tr", function() {
+                                $.each($.find('#details-tab-settings 
.data-table tbody tr'), function(idx, row) {
+                                    const key = 
$(row).find('td.name').attr('title');
+                                    const options = details[key];
+                                    if (options) {
+                                        
$($(row).find('input.edit')).autocomplete({
+                                            minLength: 0,
+                                            delay: 0,
+                                            autoFocus: true,
+                                            source: options.sort()
+                                        }).focus(function() {
+                                            
$(this).data("uiAutocomplete").search("");
+                                        });
+                                        $(row).find('input.edit').blur();
+                                    }
+                                });
+                            });
+                            dataProvider(args);
+                        }
+                    });
+                }
             };
 
             var $listView = $('<div>').listView({
@@ -138,4 +223,4 @@
             return $listView;
         }
     };
-}(jQuery, cloudStack));
\ No newline at end of file
+}(jQuery, cloudStack));

Reply via email to