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