diff --git a/api/src/main/java/com/cloud/offering/ServiceOffering.java b/api/src/main/java/com/cloud/offering/ServiceOffering.java index 2a80ba5bd61c..7bad77fef30e 100644 --- a/api/src/main/java/com/cloud/offering/ServiceOffering.java +++ b/api/src/main/java/com/cloud/offering/ServiceOffering.java @@ -116,4 +116,6 @@ public enum StorageType { String getDeploymentPlanner(); boolean isDynamic(); + + boolean isDynamicScalingEnabled(); } diff --git a/api/src/main/java/com/cloud/vm/UserVmService.java b/api/src/main/java/com/cloud/vm/UserVmService.java index eab9c736a377..0b48a4867c00 100644 --- a/api/src/main/java/com/cloud/vm/UserVmService.java +++ b/api/src/main/java/com/cloud/vm/UserVmService.java @@ -218,7 +218,7 @@ UserVm createBasicSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering s String userData, String sshKeyPair, Map requestedIps, IpAddresses defaultIp, Boolean displayVm, String keyboard, List affinityGroupIdList, Map customParameter, String customId, Map> dhcpOptionMap, Map dataDiskTemplateToDiskOfferingMap, - Map userVmOVFProperties) throws InsufficientCapacityException, + Map userVmOVFProperties, boolean dynamicScalingEnabled) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException; /** @@ -300,7 +300,7 @@ UserVm createAdvancedSecurityGroupVirtualMachine(DataCenter zone, ServiceOfferin HTTPMethod httpmethod, String userData, String sshKeyPair, Map requestedIps, IpAddresses defaultIps, Boolean displayVm, String keyboard, List affinityGroupIdList, Map customParameters, String customId, Map> dhcpOptionMap, Map dataDiskTemplateToDiskOfferingMap, - Map userVmOVFProperties) throws InsufficientCapacityException, + Map userVmOVFProperties, boolean dynamicScalingEnabled) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException; /** @@ -379,7 +379,7 @@ UserVm createAdvancedVirtualMachine(DataCenter zone, ServiceOffering serviceOffe String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, HTTPMethod httpmethod, String userData, String sshKeyPair, Map requestedIps, IpAddresses defaultIps, Boolean displayVm, String keyboard, List affinityGroupIdList, Map customParameters, String customId, Map> dhcpOptionMap, Map dataDiskTemplateToDiskOfferingMap, - Map templateOvfPropertiesMap) + Map templateOvfPropertiesMap, boolean dynamicScalingEnabled) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException; diff --git a/api/src/main/java/com/cloud/vm/VirtualMachine.java b/api/src/main/java/com/cloud/vm/VirtualMachine.java index 829e743df730..79154cbee196 100644 --- a/api/src/main/java/com/cloud/vm/VirtualMachine.java +++ b/api/src/main/java/com/cloud/vm/VirtualMachine.java @@ -342,4 +342,6 @@ public boolean isUsedBySystem() { @Override boolean isDisplay(); + boolean isDynamicallyScalable(); + } diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java index 605a6c006ebd..11fb44df0906 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -837,6 +837,7 @@ public class ApiConstants { public static final String CROSS_ZONES = "crossZones"; public static final String TEMPLATETYPE = "templatetype"; public static final String SOURCETEMPLATEID = "sourcetemplateid"; + public static final String DYNAMIC_SCALING_ENABLED = "dynamicscalingenabled"; public static final String POOL_TYPE ="pooltype"; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java index d2d6f387744c..b30156097f31 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java @@ -223,6 +223,10 @@ public class CreateServiceOfferingCmd extends BaseCmd { @Parameter(name = ApiConstants.STORAGE_POLICY, type = CommandType.UUID, entityType = VsphereStoragePoliciesResponse.class,required = false, description = "Name of the storage policy defined at vCenter, this is applicable only for VMware", since = "4.15") private Long storagePolicy; + @Parameter(name = ApiConstants.DYNAMIC_SCALING_ENABLED, type = CommandType.BOOLEAN, since = "4.16", + description = "true if virtual machine needs to be dynamically scalable of cpu or memory") + protected Boolean isDynamicScalingEnabled; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -441,6 +445,10 @@ public Long getStoragePolicy() { return storagePolicy; } + public boolean getDynamicScalingEnabled() { + return isDynamicScalingEnabled == null ? true : isDynamicScalingEnabled; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java index 6d7cc9c81c2f..733bddb45149 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java @@ -235,6 +235,10 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd implements SecurityG @LogLevel(LogLevel.Log4jLevel.Off) private Map vAppNetworks; + @Parameter(name = ApiConstants.DYNAMIC_SCALING_ENABLED, type = CommandType.BOOLEAN, since = "4.16", + description = "true if virtual machine needs to be dynamically scalable") + protected Boolean dynamicScalingEnabled; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -623,6 +627,10 @@ public Boolean getBootIntoSetup() { return bootIntoSetup; } + public boolean isDynamicScalingEnabled() { + return dynamicScalingEnabled == null ? true : dynamicScalingEnabled; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVMCmd.java index 3afee8ac8037..38d1a5d5dd4b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVMCmd.java @@ -89,7 +89,7 @@ public class UpdateVMCmd extends BaseCustomIdCmd implements SecurityGroupAction, @Parameter(name = ApiConstants.IS_DYNAMICALLY_SCALABLE, type = CommandType.BOOLEAN, - description = "true if VM contains XS/VMWare tools inorder to support dynamic scaling of VM cpu/memory") + description = "true if VM contains XS/VMWare tools inorder to support dynamic scaling of VM cpu/memory. This can be updated only when dynamic scaling is enabled on template, service offering and the corresponding global setting") protected Boolean isDynamicallyScalable; @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "new host name of the vm. The VM has to be stopped/started for this update to take affect", since = "4.4") diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ServiceOfferingResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ServiceOfferingResponse.java index 05fcfbdc7b2e..59bd72ce88e6 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ServiceOfferingResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ServiceOfferingResponse.java @@ -204,6 +204,10 @@ public class ServiceOfferingResponse extends BaseResponse { @Param(description = "Root disk size in GB", since = "4.15") private Long rootDiskSize; + @SerializedName(ApiConstants.DYNAMIC_SCALING_ENABLED) + @Param(description = "true if virtual machine needs to be dynamically scalable of cpu or memory", since = "4.16") + private Boolean dynamicScalingEnabled; + public ServiceOfferingResponse() { } @@ -457,7 +461,6 @@ public void setDetails(Map details) { public void setIscutomized(boolean iscutomized) { this.isCustomized = iscutomized; - } public void setCacheMode(String cacheMode) { @@ -475,4 +478,12 @@ public void setVsphereStoragePolicy(String vsphereStoragePolicy) { public void setRootDiskSize(Long rootDiskSize) { this.rootDiskSize = rootDiskSize; } + + public Boolean getDynamicScalingEnabled() { + return dynamicScalingEnabled; + } + + public void setDynamicScalingEnabled(Boolean dynamicScalingEnabled) { + this.dynamicScalingEnabled = dynamicScalingEnabled; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/SystemVmResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/SystemVmResponse.java index 934e84fe9d0a..bfc9f9aad86d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/SystemVmResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/SystemVmResponse.java @@ -170,6 +170,10 @@ public class SystemVmResponse extends BaseResponse { @Param(description = "the systemvm agent version", since = "4.13.1") private String version; + @SerializedName(ApiConstants.IS_DYNAMICALLY_SCALABLE) + @Param(description = "true if vm contains XS/VMWare tools inorder to support dynamic scaling of VM cpu/memory.") + private Boolean isDynamicallyScalable; + @Override public String getObjectId() { return this.getId(); @@ -442,4 +446,12 @@ public String getVersion() { public void setVersion(String version) { this.version = version; } + + public Boolean getDynamicallyScalable() { + return isDynamicallyScalable; + } + + public void setDynamicallyScalable(Boolean dynamicallyScalable) { + isDynamicallyScalable = dynamicallyScalable; + } } 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 3e5ddbfcbc8c..2ca7f06eb743 100755 --- a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java @@ -3916,6 +3916,9 @@ public boolean upgradeVmDb(final long vmId, final ServiceOffering newServiceOffe if (currentServiceOffering.isDynamic() && !newServiceOffering.isDynamic()) { removeCustomOfferingDetails(vmId); } + VMTemplateVO template = _templateDao.findById(vmForUpdate.getTemplateId()); + boolean dynamicScalingEnabled = _userVmMgr.checkIfDynamicScalingCanBeEnabled(vmForUpdate, newServiceOffering, template, vmForUpdate.getDataCenterId()); + vmForUpdate.setDynamicallyScalable(dynamicScalingEnabled); return _vmDao.update(vmId, vmForUpdate); } diff --git a/engine/schema/src/main/java/com/cloud/service/ServiceOfferingVO.java b/engine/schema/src/main/java/com/cloud/service/ServiceOfferingVO.java index 3a5f3182b731..44f39a1050ba 100644 --- a/engine/schema/src/main/java/com/cloud/service/ServiceOfferingVO.java +++ b/engine/schema/src/main/java/com/cloud/service/ServiceOfferingVO.java @@ -75,6 +75,9 @@ public class ServiceOfferingVO extends DiskOfferingVO implements ServiceOffering @Column(name = "deployment_planner") private String deploymentPlanner = null; + @Column(name = "dynamic_scaling_enabled") + private boolean dynamicScalingEnabled = true; + // This is a delayed load value. If the value is null, // then this field has not been loaded yet. // Call service offering dao to load it. @@ -91,7 +94,7 @@ protected ServiceOfferingVO() { } public ServiceOfferingVO(String name, Integer cpu, Integer ramSize, Integer speed, Integer rateMbps, Integer multicastRateMbps, boolean offerHA, String displayText, - ProvisioningType provisioningType, boolean useLocalStorage, boolean recreatable, String tags, boolean systemUse, VirtualMachine.Type vmType, boolean defaultUse) { + ProvisioningType provisioningType, boolean useLocalStorage, boolean recreatable, String tags, boolean systemUse, VirtualMachine.Type vmType, boolean defaultUse) { super(name, displayText, provisioningType, false, tags, recreatable, useLocalStorage, systemUse, true); this.cpu = cpu; this.ramSize = ramSize; @@ -105,8 +108,9 @@ public ServiceOfferingVO(String name, Integer cpu, Integer ramSize, Integer spee this.vmType = vmType == null ? null : vmType.toString().toLowerCase(); } - public ServiceOfferingVO(String name, Integer cpu, Integer ramSize, Integer speed, Integer rateMbps, Integer multicastRateMbps, boolean offerHA, boolean limitCpuUse, - boolean volatileVm, String displayText, ProvisioningType provisioningType, boolean useLocalStorage, boolean recreatable, String tags, boolean systemUse, VirtualMachine.Type vmType) { + public ServiceOfferingVO(String name, Integer cpu, Integer ramSize, Integer speed, Integer rateMbps, Integer multicastRateMbps, boolean offerHA, + boolean limitResourceUse, boolean volatileVm, String displayText, ProvisioningType provisioningType, boolean useLocalStorage, boolean recreatable, String tags, boolean systemUse, + VirtualMachine.Type vmType, String hostTag, String deploymentPlanner, boolean dynamicScalingEnabled) { super(name, displayText, provisioningType, false, tags, recreatable, useLocalStorage, systemUse, true); this.cpu = cpu; this.ramSize = ramSize; @@ -114,55 +118,12 @@ public ServiceOfferingVO(String name, Integer cpu, Integer ramSize, Integer spee this.rateMbps = rateMbps; this.multicastRateMbps = multicastRateMbps; this.offerHA = offerHA; - this.limitCpuUse = limitCpuUse; + this.limitCpuUse = limitResourceUse; this.volatileVm = volatileVm; this.vmType = vmType == null ? null : vmType.toString().toLowerCase(); - } - - public ServiceOfferingVO(String name, Integer cpu, Integer ramSize, Integer speed, Integer rateMbps, Integer multicastRateMbps, boolean offerHA, - boolean limitResourceUse, boolean volatileVm, String displayText, ProvisioningType provisioningType, boolean useLocalStorage, boolean recreatable, String tags, boolean systemUse, - VirtualMachine.Type vmType, String hostTag) { - this(name, - cpu, - ramSize, - speed, - rateMbps, - multicastRateMbps, - offerHA, - limitResourceUse, - volatileVm, - displayText, - provisioningType, - useLocalStorage, - recreatable, - tags, - systemUse, - vmType - ); this.hostTag = hostTag; - } - - public ServiceOfferingVO(String name, Integer cpu, Integer ramSize, Integer speed, Integer rateMbps, Integer multicastRateMbps, boolean offerHA, - boolean limitResourceUse, boolean volatileVm, String displayText, ProvisioningType provisioningType, boolean useLocalStorage, boolean recreatable, String tags, boolean systemUse, - VirtualMachine.Type vmType, String hostTag, String deploymentPlanner) { - this(name, - cpu, - ramSize, - speed, - rateMbps, - multicastRateMbps, - offerHA, - limitResourceUse, - volatileVm, - displayText, - provisioningType, - useLocalStorage, - recreatable, - tags, - systemUse, - vmType, - hostTag); this.deploymentPlanner = deploymentPlanner; + this.dynamicScalingEnabled = dynamicScalingEnabled; } public ServiceOfferingVO(ServiceOfferingVO offering) { @@ -189,6 +150,7 @@ public ServiceOfferingVO(ServiceOfferingVO offering) { volatileVm = offering.isVolatileVm(); hostTag = offering.getHostTag(); vmType = offering.getSystemVmType(); + dynamicScalingEnabled = offering.isDynamicScalingEnabled(); } @Override @@ -334,4 +296,13 @@ public void setDynamicFlag(boolean isdynamic) { public boolean isCustomCpuSpeedSupported() { return isCustomized() && getDetail("minCPU") != null; } + + @Override + public boolean isDynamicScalingEnabled() { + return dynamicScalingEnabled; + } + + public void setDynamicScalingEnabled(boolean dynamicScalingEnabled) { + this.dynamicScalingEnabled = dynamicScalingEnabled; + } } diff --git a/engine/schema/src/main/java/com/cloud/vm/VMInstanceVO.java b/engine/schema/src/main/java/com/cloud/vm/VMInstanceVO.java index 0e8dd4ee44a5..aa2e7360bf4b 100644 --- a/engine/schema/src/main/java/com/cloud/vm/VMInstanceVO.java +++ b/engine/schema/src/main/java/com/cloud/vm/VMInstanceVO.java @@ -544,7 +544,7 @@ public void setDynamicallyScalable(boolean dynamicallyScalable) { this.dynamicallyScalable = dynamicallyScalable; } - public Boolean isDynamicallyScalable() { + public boolean isDynamicallyScalable() { return dynamicallyScalable; } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/engine/cloud/entity/api/db/VMEntityVO.java b/engine/schema/src/main/java/org/apache/cloudstack/engine/cloud/entity/api/db/VMEntityVO.java index 4ab5f4275917..67af516c33b5 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/engine/cloud/entity/api/db/VMEntityVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/engine/cloud/entity/api/db/VMEntityVO.java @@ -189,6 +189,9 @@ public class VMEntityVO implements VirtualMachine, FiniteStateObject details = new HashMap(); @@ -284,4 +287,9 @@ public boolean isDynamic() { public void setDynamicFlag(boolean isdynamic) { isDynamic = isdynamic; } + + @Override + public boolean isDynamicScalingEnabled() { + return dynamicScalingEnabled; + } } diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java index 17def806c332..28f925915c95 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java @@ -374,7 +374,7 @@ protected UserVm createKubernetesNode(String joinIp, int nodeInstance) throws Ma nodeVm = userVmService.createAdvancedVirtualMachine(zone, serviceOffering, clusterTemplate, networkIds, owner, hostName, hostName, null, null, null, Hypervisor.HypervisorType.None, BaseCmd.HTTPMethod.POST, base64UserData, kubernetesCluster.getKeyPair(), - null, addrs, null, null, null, customParameterMap, null, null, null, null); + null, addrs, null, null, null, customParameterMap, null, null, null, null, true); if (LOGGER.isInfoEnabled()) { LOGGER.info(String.format("Created node VM : %s, %s in the Kubernetes cluster : %s", hostName, nodeVm.getUuid(), kubernetesCluster.getName())); } diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterStartWorker.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterStartWorker.java index 4a96b9ed2bab..307b4f13f2b7 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterStartWorker.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterStartWorker.java @@ -211,7 +211,7 @@ private UserVm createKubernetesMaster(final Network network, String serverIp) th masterVm = userVmService.createAdvancedVirtualMachine(zone, serviceOffering, clusterTemplate, networkIds, owner, hostName, hostName, null, null, null, Hypervisor.HypervisorType.None, BaseCmd.HTTPMethod.POST, base64UserData, kubernetesCluster.getKeyPair(), - requestedIps, addrs, null, null, null, customParameterMap, null, null, null, null); + requestedIps, addrs, null, null, null, customParameterMap, null, null, null, null, true); if (LOGGER.isInfoEnabled()) { LOGGER.info(String.format("Created master VM ID: %s, %s in the Kubernetes cluster : %s", masterVm.getUuid(), hostName, kubernetesCluster.getName())); } @@ -265,7 +265,7 @@ private UserVm createKubernetesAdditionalMaster(final String joinIp, final int a additionalMasterVm = userVmService.createAdvancedVirtualMachine(zone, serviceOffering, clusterTemplate, networkIds, owner, hostName, hostName, null, null, null, Hypervisor.HypervisorType.None, BaseCmd.HTTPMethod.POST, base64UserData, kubernetesCluster.getKeyPair(), - null, addrs, null, null, null, customParameterMap, null, null, null, null); + null, addrs, null, null, null, customParameterMap, null, null, null, null, true); if (LOGGER.isInfoEnabled()) { LOGGER.info(String.format("Created master VM ID : %s, %s in the Kubernetes cluster : %s", additionalMasterVm.getUuid(), hostName, kubernetesCluster.getName())); } diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index 5c5ba55f1972..1f28deae78bc 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -1470,6 +1470,7 @@ public SystemVmResponse createSystemVmResponse(VirtualMachine vm) { vmResponse.setState(vm.getState().toString()); } + vmResponse.setDynamicallyScalable(vm.isDynamicallyScalable()); // for console proxies, add the active sessions if (vm.getType() == Type.ConsoleProxy) { ConsoleProxyVO proxy = ApiDBUtils.findConsoleProxy(vm.getId()); 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 f204ead3055a..9e24e5c36c99 100644 --- a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java +++ b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java @@ -2971,11 +2971,12 @@ private Pair, Integer> searchForServiceOfferingsInte // 1. Only return offerings with the same storage type than the storage pool where the VM's root volume is allocated sc.addAnd("useLocalStorage", SearchCriteria.Op.EQ, isRootVolumeUsingLocalStorage); - // 2.In case vm is running return only offerings greater than equal to current offering compute. + // 2.In case vm is running return only offerings greater than equal to current offering compute and offering's dynamic scalability should match if (vmInstance.getState() == VirtualMachine.State.Running) { sc.addAnd("cpu", Op.GTEQ, currentVmOffering.getCpu()); sc.addAnd("speed", Op.GTEQ, currentVmOffering.getSpeed()); sc.addAnd("ramSize", Op.GTEQ, currentVmOffering.getRamSize()); + sc.addAnd("dynamicScalingEnabled", Op.EQ, currentVmOffering.isDynamicScalingEnabled()); } } diff --git a/server/src/main/java/com/cloud/api/query/dao/ServiceOfferingJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/ServiceOfferingJoinDaoImpl.java index 87b03748dbcd..55faa391df04 100644 --- a/server/src/main/java/com/cloud/api/query/dao/ServiceOfferingJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/ServiceOfferingJoinDaoImpl.java @@ -113,6 +113,7 @@ public ServiceOfferingResponse newServiceOfferingResponse(ServiceOfferingJoinVO offeringResponse.setObjectName("serviceoffering"); offeringResponse.setIscutomized(offering.isDynamic()); offeringResponse.setCacheMode(offering.getCacheMode()); + offeringResponse.setDynamicScalingEnabled(offering.isDynamicScalingEnabled()); if (offeringDetails != null && !offeringDetails.isEmpty()) { String vsphereStoragePolicyId = offeringDetails.get(ApiConstants.STORAGE_POLICY); diff --git a/server/src/main/java/com/cloud/api/query/vo/ServiceOfferingJoinVO.java b/server/src/main/java/com/cloud/api/query/vo/ServiceOfferingJoinVO.java index 4d9b2cd5c0b6..e9b2f2d79b66 100644 --- a/server/src/main/java/com/cloud/api/query/vo/ServiceOfferingJoinVO.java +++ b/server/src/main/java/com/cloud/api/query/vo/ServiceOfferingJoinVO.java @@ -193,6 +193,9 @@ public class ServiceOfferingJoinVO extends BaseViewVO implements InternalIdentit @Column(name = "root_disk_size") private Long rootDiskSize; + @Column(name = "dynamic_scaling_enabled") + private boolean dynamicScalingEnabled; + public ServiceOfferingJoinVO() { } @@ -397,4 +400,12 @@ public String getVsphereStoragePolicy() { public Long getRootDiskSize() { return rootDiskSize ; } + + public boolean isDynamicScalingEnabled() { + return dynamicScalingEnabled; + } + + public void setDynamicScalingEnabled(boolean dynamicScalingEnabled) { + this.dynamicScalingEnabled = dynamicScalingEnabled; + } } diff --git a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java index f5de35af3ed2..86bd210d3d2f 100755 --- a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java @@ -2481,7 +2481,7 @@ public ServiceOffering createServiceOffering(final CreateServiceOfferingCmd cmd) cmd.getBytesWriteRate(), cmd.getBytesWriteRateMax(), cmd.getBytesWriteRateMaxLength(), cmd.getIopsReadRate(), cmd.getIopsReadRateMax(), cmd.getIopsReadRateMaxLength(), cmd.getIopsWriteRate(), cmd.getIopsWriteRateMax(), cmd.getIopsWriteRateMaxLength(), - cmd.getHypervisorSnapshotReserve(), cmd.getCacheMode(), storagePolicyId); + cmd.getHypervisorSnapshotReserve(), cmd.getCacheMode(), storagePolicyId, cmd.getDynamicScalingEnabled()); } protected ServiceOfferingVO createServiceOffering(final long userId, final boolean isSystem, final VirtualMachine.Type vmType, @@ -2492,7 +2492,7 @@ protected ServiceOfferingVO createServiceOffering(final long userId, final boole Long bytesWriteRate, Long bytesWriteRateMax, Long bytesWriteRateMaxLength, Long iopsReadRate, Long iopsReadRateMax, Long iopsReadRateMaxLength, Long iopsWriteRate, Long iopsWriteRateMax, Long iopsWriteRateMaxLength, - final Integer hypervisorSnapshotReserve, String cacheMode, final Long storagePolicyID) { + final Integer hypervisorSnapshotReserve, String cacheMode, final Long storagePolicyID, final boolean dynamicScalingEnabled) { // Filter child domains when both parent and child domains are present List filteredDomainIds = filterChildSubDomains(domainIds); @@ -2524,7 +2524,7 @@ protected ServiceOfferingVO createServiceOffering(final long userId, final boole ServiceOfferingVO offering = new ServiceOfferingVO(name, cpu, ramSize, speed, networkRate, null, offerHA, limitResourceUse, volatileVm, displayText, typedProvisioningType, localStorageRequired, false, tags, isSystem, vmType, - hostTag, deploymentPlanner); + hostTag, deploymentPlanner, dynamicScalingEnabled); if (Boolean.TRUE.equals(isCustomizedIops) || isCustomizedIops == null) { minIops = null; diff --git a/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java b/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java index 7d6c88870c0e..2a8b13e96ca1 100644 --- a/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java +++ b/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java @@ -54,7 +54,6 @@ import com.cloud.utils.component.AdapterBase; import com.cloud.vm.NicProfile; import com.cloud.vm.NicVO; -import com.cloud.vm.UserVmManager; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineProfile; @@ -257,9 +256,7 @@ protected VirtualMachineTO toVirtualMachineTO(VirtualMachineProfile vmProfile) { // Workaround to make sure the TO has the UUID we need for Niciri integration VMInstanceVO vmInstance = _virtualMachineDao.findById(to.getId()); - // check if XStools/VMWare tools are present in the VM and dynamic scaling feature is enabled (per zone/global) - Boolean isDynamicallyScalable = vmInstance.isDynamicallyScalable() && UserVmManager.EnableDynamicallyScaleVm.valueIn(vm.getDataCenterId()); - to.setEnableDynamicallyScaleVm(isDynamicallyScalable); + to.setEnableDynamicallyScaleVm(vmInstance.isDynamicallyScalable()); to.setUuid(vmInstance.getUuid()); to.setVmData(vmProfile.getVmData()); diff --git a/server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java b/server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java index c71054951619..5e7f0a952643 100644 --- a/server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java +++ b/server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java @@ -1325,18 +1325,18 @@ private long createNewVM(AutoScaleVmGroupVO asGroup) { vm = _userVmService.createBasicSecurityGroupVirtualMachine(zone, serviceOffering, template, null, owner, "autoScaleVm-" + asGroup.getId() + "-" + getCurrentTimeStampString(), "autoScaleVm-" + asGroup.getId() + "-" + getCurrentTimeStampString(), null, null, null, HypervisorType.XenServer, HTTPMethod.GET, null, null, null, - null, true, null, null, null, null, null, null, null); + null, true, null, null, null, null, null, null, null, true); } else { if (zone.isSecurityGroupEnabled()) { vm = _userVmService.createAdvancedSecurityGroupVirtualMachine(zone, serviceOffering, template, null, null, owner, "autoScaleVm-" + asGroup.getId() + "-" + getCurrentTimeStampString(), "autoScaleVm-" + asGroup.getId() + "-" + getCurrentTimeStampString(), null, null, null, HypervisorType.XenServer, HTTPMethod.GET, null, null, - null, null, true, null, null, null, null, null, null, null); + null, null, true, null, null, null, null, null, null, null, true); } else { vm = _userVmService.createAdvancedVirtualMachine(zone, serviceOffering, template, null, owner, "autoScaleVm-" + asGroup.getId() + "-" + getCurrentTimeStampString(), "autoScaleVm-" + asGroup.getId() + "-" + getCurrentTimeStampString(), - null, null, null, HypervisorType.XenServer, HTTPMethod.GET, null, null, null, addrs, true, null, null, null, null, null, null, null); + null, null, null, HypervisorType.XenServer, HTTPMethod.GET, null, null, null, addrs, true, null, null, null, null, null, null, null, true); } } diff --git a/server/src/main/java/com/cloud/vm/UserVmManager.java b/server/src/main/java/com/cloud/vm/UserVmManager.java index e4206efe5d84..98b7cb2ad0d7 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManager.java +++ b/server/src/main/java/com/cloud/vm/UserVmManager.java @@ -20,6 +20,8 @@ import java.util.List; import java.util.Map; +import com.cloud.offering.ServiceOffering; +import com.cloud.template.VirtualMachineTemplate; import org.apache.cloudstack.api.BaseCmd.HTTPMethod; import org.apache.cloudstack.framework.config.ConfigKey; @@ -124,4 +126,7 @@ UserVm updateVirtualMachine(long id, String displayName, String group, Boolean h void persistDeviceBusInfo(UserVmVO paramUserVmVO, String paramString); HashMap> getVmNetworkStatistics(long hostId, String hostName, List vmIds); + + boolean checkIfDynamicScalingCanBeEnabled(VirtualMachine vm, ServiceOffering offering, VirtualMachineTemplate template, Long zoneId); + } diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 0fecd604995d..edd7813335a8 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -1881,6 +1881,10 @@ private boolean upgradeRunningVirtualMachine(Long vmId, Long newServiceOfferingI _itMgr.checkIfCanUpgrade(vmInstance, newServiceOffering); ServiceOfferingVO currentServiceOffering = _offeringDao.findByIdIncludingRemoved(vmInstance.getId(), vmInstance.getServiceOfferingId()); + if (newServiceOffering.isDynamicScalingEnabled() != currentServiceOffering.isDynamicScalingEnabled()) { + throw new InvalidParameterValueException("Unable to Scale VM: since dynamic scaling enabled flag is not same for new service offering and old service offering"); + } + int newCpu = newServiceOffering.getCpu(); int newMemory = newServiceOffering.getRamSize(); int newSpeed = newServiceOffering.getSpeed(); @@ -1932,7 +1936,7 @@ private boolean upgradeRunningVirtualMachine(Long vmId, Long newServiceOfferingI // Check vm flag if (!vmInstance.isDynamicallyScalable()) { - throw new CloudRuntimeException("Unable to Scale the vm: " + vmInstance.getUuid() + " as vm does not have tools to support dynamic scaling"); + throw new CloudRuntimeException("Unable to Scale the VM: " + vmInstance.getUuid() + " as VM is not configured to be dynamically scalable"); } // Check disable threshold for cluster is not crossed @@ -2764,10 +2768,6 @@ public UserVm updateVirtualMachine(long id, String displayName, String group, Bo userData = vm.getUserData(); } - if (isDynamicallyScalable == null) { - isDynamicallyScalable = vm.isDynamicallyScalable(); - } - if (osTypeId == null) { osTypeId = vm.getGuestOSId(); } @@ -2778,6 +2778,20 @@ public UserVm updateVirtualMachine(long id, String displayName, String group, Bo if (isDynamicallyScalable == null) { isDynamicallyScalable = vm.isDynamicallyScalable(); + } else { + if (isDynamicallyScalable == true) { + VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId()); + if (!template.isDynamicallyScalable()) { + throw new InvalidParameterValueException("Dynamic Scaling cannot be enabled for the VM since its template does not have dynamic scaling enabled"); + } + if (!offering.isDynamicScalingEnabled()) { + throw new InvalidParameterValueException("Dynamic Scaling cannot be enabled for the VM since its service offering does not have dynamic scaling enabled"); + } + if (!UserVmManager.EnableDynamicallyScaleVm.valueIn(vm.getDataCenterId())) { + s_logger.debug(String.format("Dynamic Scaling cannot be enabled for the VM %s since the global setting enable.dynamic.scale.vm is set to false", vm.getUuid())); + throw new InvalidParameterValueException("Dynamic Scaling cannot be enabled for the VM since corresponding global setting is set to false"); + } + } } boolean isVMware = (vm.getHypervisorType() == HypervisorType.VMware); @@ -3226,7 +3240,7 @@ public UserVm createBasicSecurityGroupVirtualMachine(DataCenter zone, ServiceOff Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, HTTPMethod httpmethod, String userData, String sshKeyPair, Map requestedIps, IpAddresses defaultIps, Boolean displayVm, String keyboard, List affinityGroupIdList, Map customParametes, String customId, Map> dhcpOptionMap, - Map dataDiskTemplateToDiskOfferingMap, Map userVmOVFProperties) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, + Map dataDiskTemplateToDiskOfferingMap, Map userVmOVFProperties, boolean dynamicScalingEnabled) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException { Account caller = CallContext.current().getCallingAccount(); @@ -3275,7 +3289,7 @@ public UserVm createBasicSecurityGroupVirtualMachine(DataCenter zone, ServiceOff return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId, diskSize, networkList, securityGroupIdList, group, httpmethod, userData, sshKeyPair, hypervisor, caller, requestedIps, defaultIps, displayVm, keyboard, affinityGroupIdList, customParametes, customId, dhcpOptionMap, - dataDiskTemplateToDiskOfferingMap, userVmOVFProperties); + dataDiskTemplateToDiskOfferingMap, userVmOVFProperties, dynamicScalingEnabled); } @@ -3285,7 +3299,7 @@ public UserVm createAdvancedSecurityGroupVirtualMachine(DataCenter zone, Service List securityGroupIdList, Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, HTTPMethod httpmethod, String userData, String sshKeyPair, Map requestedIps, IpAddresses defaultIps, Boolean displayVm, String keyboard, List affinityGroupIdList, Map customParameters, String customId, Map> dhcpOptionMap, - Map dataDiskTemplateToDiskOfferingMap, Map userVmOVFProperties) throws InsufficientCapacityException, ConcurrentOperationException, + Map dataDiskTemplateToDiskOfferingMap, Map userVmOVFProperties, boolean dynamicScalingEnabled) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException { Account caller = CallContext.current().getCallingAccount(); @@ -3386,7 +3400,7 @@ public UserVm createAdvancedSecurityGroupVirtualMachine(DataCenter zone, Service return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId, diskSize, networkList, securityGroupIdList, group, httpmethod, userData, sshKeyPair, hypervisor, caller, requestedIps, defaultIps, displayVm, keyboard, affinityGroupIdList, customParameters, customId, dhcpOptionMap, dataDiskTemplateToDiskOfferingMap, - userVmOVFProperties); + userVmOVFProperties, dynamicScalingEnabled); } @Override @@ -3395,7 +3409,7 @@ public UserVm createAdvancedVirtualMachine(DataCenter zone, ServiceOffering serv String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, HTTPMethod httpmethod, String userData, String sshKeyPair, Map requestedIps, IpAddresses defaultIps, Boolean displayvm, String keyboard, List affinityGroupIdList, Map customParametrs, String customId, Map> dhcpOptionsMap, Map dataDiskTemplateToDiskOfferingMap, - Map userVmOVFPropertiesMap) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, + Map userVmOVFPropertiesMap, boolean dynamicScalingEnabled) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException { Account caller = CallContext.current().getCallingAccount(); @@ -3447,7 +3461,7 @@ public UserVm createAdvancedVirtualMachine(DataCenter zone, ServiceOffering serv return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId, diskSize, networkList, null, group, httpmethod, userData, sshKeyPair, hypervisor, caller, requestedIps, defaultIps, displayvm, keyboard, affinityGroupIdList, customParametrs, customId, dhcpOptionsMap, - dataDiskTemplateToDiskOfferingMap, userVmOVFPropertiesMap); + dataDiskTemplateToDiskOfferingMap, userVmOVFPropertiesMap, dynamicScalingEnabled); } private NetworkVO getNetworkToAddToNetworkList(VirtualMachineTemplate template, Account owner, HypervisorType hypervisor, @@ -3562,11 +3576,11 @@ public void checkNameForRFCCompliance(String name) { @DB private UserVm createVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate tmplt, String hostName, String displayName, Account owner, - Long diskOfferingId, Long diskSize, List networkList, List securityGroupIdList, String group, HTTPMethod httpmethod, String userData, - String sshKeyPair, HypervisorType hypervisor, Account caller, Map requestedIps, IpAddresses defaultIps, Boolean isDisplayVm, String keyboard, - List affinityGroupIdList, Map customParameters, String customId, Map> dhcpOptionMap, - Map datadiskTemplateToDiskOfferringMap, - Map userVmOVFPropertiesMap) throws InsufficientCapacityException, ResourceUnavailableException, + Long diskOfferingId, Long diskSize, List networkList, List securityGroupIdList, String group, HTTPMethod httpmethod, String userData, + String sshKeyPair, HypervisorType hypervisor, Account caller, Map requestedIps, IpAddresses defaultIps, Boolean isDisplayVm, String keyboard, + List affinityGroupIdList, Map customParameters, String customId, Map> dhcpOptionMap, + Map datadiskTemplateToDiskOfferringMap, + Map userVmOVFPropertiesMap, boolean dynamicScalingEnabled) throws InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException, StorageUnavailableException, ResourceAllocationException { _accountMgr.checkAccess(caller, null, true, owner); @@ -3926,9 +3940,11 @@ private UserVm createVirtualMachine(DataCenter zone, ServiceOffering serviceOffe } } + dynamicScalingEnabled = dynamicScalingEnabled && checkIfDynamicScalingCanBeEnabled(null, offering, template, zone.getId()); + UserVmVO vm = commitUserVm(zone, template, hostName, displayName, owner, diskOfferingId, diskSize, userData, caller, isDisplayVm, keyboard, accountId, userId, offering, isIso, sshPublicKey, networkNicMap, id, instanceName, uuidName, hypervisorType, customParameters, dhcpOptionMap, - datadiskTemplateToDiskOfferringMap, userVmOVFPropertiesMap); + datadiskTemplateToDiskOfferringMap, userVmOVFPropertiesMap, dynamicScalingEnabled); // Assign instance to the group try { @@ -3952,6 +3968,16 @@ private UserVm createVirtualMachine(DataCenter zone, ServiceOffering serviceOffe return vm; } + @Override + public boolean checkIfDynamicScalingCanBeEnabled(VirtualMachine vm, ServiceOffering offering, VirtualMachineTemplate template, Long zoneId) { + boolean canEnableDynamicScaling = (vm != null ? vm.isDynamicallyScalable() : true) && offering.isDynamicScalingEnabled() && template.isDynamicallyScalable() && UserVmManager.EnableDynamicallyScaleVm.valueIn(zoneId); + if (!canEnableDynamicScaling) { + s_logger.info("VM cannot be configured to be dynamically scalable if any of the service offering's dynamic scaling property, template's dynamic scaling property or global setting is false"); + } + + return canEnableDynamicScaling; + } + /** * Configures the Root disk size via User`s custom parameters. * If the Service Offering has the Root Disk size field configured then the User`s root disk custom parameter is overwritten by the service offering. @@ -4030,14 +4056,14 @@ private UserVmVO commitUserVm(final boolean isImport, final DataCenter zone, fin final long accountId, final long userId, final ServiceOffering offering, final boolean isIso, final String sshPublicKey, final LinkedHashMap> networkNicMap, final long id, final String instanceName, final String uuidName, final HypervisorType hypervisorType, final Map customParameters, final Map> extraDhcpOptionMap, final Map dataDiskTemplateToDiskOfferingMap, - final Map userVmOVFPropertiesMap, final VirtualMachine.PowerState powerState) throws InsufficientCapacityException { + final Map userVmOVFPropertiesMap, final VirtualMachine.PowerState powerState, final boolean dynamicScalingEnabled) throws InsufficientCapacityException { return Transaction.execute(new TransactionCallbackWithException() { @Override public UserVmVO doInTransaction(TransactionStatus status) throws InsufficientCapacityException { UserVmVO vm = new UserVmVO(id, instanceName, displayName, template.getId(), hypervisorType, template.getGuestOSId(), offering.isOfferHA(), offering.getLimitCpuUse(), owner.getDomainId(), owner.getId(), userId, offering.getId(), userData, hostName, diskOfferingId); vm.setUuid(uuidName); - vm.setDynamicallyScalable(template.isDynamicallyScalable()); + vm.setDynamicallyScalable(dynamicScalingEnabled); Map details = template.getDetails(); if (details != null && !details.isEmpty()) { @@ -4221,13 +4247,13 @@ private UserVmVO commitUserVm(final DataCenter zone, final VirtualMachineTemplat final long accountId, final long userId, final ServiceOfferingVO offering, final boolean isIso, final String sshPublicKey, final LinkedHashMap> networkNicMap, final long id, final String instanceName, final String uuidName, final HypervisorType hypervisorType, final Map customParameters, final Map> extraDhcpOptionMap, final Map dataDiskTemplateToDiskOfferingMap, - Map userVmOVFPropertiesMap) throws InsufficientCapacityException { + Map userVmOVFPropertiesMap, final boolean dynamicScalingEnabled) throws InsufficientCapacityException { return commitUserVm(false, zone, null, null, template, hostName, displayName, owner, diskOfferingId, diskSize, userData, caller, isDisplayVm, keyboard, accountId, userId, offering, isIso, sshPublicKey, networkNicMap, id, instanceName, uuidName, hypervisorType, customParameters, extraDhcpOptionMap, dataDiskTemplateToDiskOfferingMap, - userVmOVFPropertiesMap, null); + userVmOVFPropertiesMap, null, dynamicScalingEnabled); } public void validateRootDiskResize(final HypervisorType hypervisorType, Long rootDiskSize, VMTemplateVO templateVO, UserVmVO vm, final Map customParameters) throws InvalidParameterValueException @@ -5312,6 +5338,8 @@ public UserVm createVirtualMachine(DeployVMCmd cmd) throws InsufficientCapacityE Long templateId = cmd.getTemplateId(); + boolean dynamicScalingEnabled = cmd.isDynamicScalingEnabled(); + VirtualMachineTemplate template = _entityMgr.findById(VirtualMachineTemplate.class, templateId); // Make sure a valid template ID was specified if (template == null) { @@ -5389,14 +5417,14 @@ public UserVm createVirtualMachine(DeployVMCmd cmd) throws InsufficientCapacityE vm = createBasicSecurityGroupVirtualMachine(zone, serviceOffering, template, getSecurityGroupIdList(cmd), owner, name, displayName, diskOfferingId, size , group , cmd.getHypervisor(), cmd.getHttpMethod(), userData , sshKeyPairName , cmd.getIpToNetworkMap(), addrs, displayVm , keyboard , cmd.getAffinityGroupIdList(), cmd.getDetails(), cmd.getCustomId(), cmd.getDhcpOptionsMap(), - dataDiskTemplateToDiskOfferingMap, userVmOVFProperties); + dataDiskTemplateToDiskOfferingMap, userVmOVFProperties, dynamicScalingEnabled); } } else { if (zone.isSecurityGroupEnabled()) { vm = createAdvancedSecurityGroupVirtualMachine(zone, serviceOffering, template, networkIds, getSecurityGroupIdList(cmd), owner, name, displayName, diskOfferingId, size, group, cmd.getHypervisor(), cmd.getHttpMethod(), userData, sshKeyPairName, cmd.getIpToNetworkMap(), addrs, displayVm, keyboard, cmd.getAffinityGroupIdList(), cmd.getDetails(), cmd.getCustomId(), cmd.getDhcpOptionsMap(), - dataDiskTemplateToDiskOfferingMap, userVmOVFProperties); + dataDiskTemplateToDiskOfferingMap, userVmOVFProperties, dynamicScalingEnabled); } else { if (cmd.getSecurityGroupIdList() != null && !cmd.getSecurityGroupIdList().isEmpty()) { @@ -5404,7 +5432,7 @@ public UserVm createVirtualMachine(DeployVMCmd cmd) throws InsufficientCapacityE } vm = createAdvancedVirtualMachine(zone, serviceOffering, template, networkIds, owner, name, displayName, diskOfferingId, size, group, cmd.getHypervisor(), cmd.getHttpMethod(), userData, sshKeyPairName, cmd.getIpToNetworkMap(), addrs, displayVm, keyboard, cmd.getAffinityGroupIdList(), cmd.getDetails(), - cmd.getCustomId(), cmd.getDhcpOptionsMap(), dataDiskTemplateToDiskOfferingMap, userVmOVFProperties); + cmd.getCustomId(), cmd.getDhcpOptionsMap(), dataDiskTemplateToDiskOfferingMap, userVmOVFProperties, dynamicScalingEnabled); } } // check if this templateId has a child ISO @@ -7069,13 +7097,13 @@ public UserVm restoreVirtualMachine(final Account caller, final long vmId, final vm.setIsoId(newTemplateId); vm.setGuestOSId(template.getGuestOSId()); vm.setTemplateId(newTemplateId); - _vmDao.update(vmId, vm); } else { newVol = volumeMgr.allocateDuplicateVolume(root, newTemplateId); vm.setGuestOSId(template.getGuestOSId()); vm.setTemplateId(newTemplateId); - _vmDao.update(vmId, vm); } + // check and update VM if it can be dynamically scalable with the new template + updateVMDynamicallyScalabilityUsingTemplate(vm, newTemplateId); } else { newVol = volumeMgr.allocateDuplicateVolume(root, null); } @@ -7163,6 +7191,14 @@ public UserVm restoreVirtualMachine(final Account caller, final long vmId, final } + private void updateVMDynamicallyScalabilityUsingTemplate(UserVmVO vm, Long newTemplateId) { + ServiceOfferingVO serviceOffering = _offeringDao.findById(vm.getServiceOfferingId()); + VMTemplateVO newTemplate = _templateDao.findById(newTemplateId); + boolean dynamicScalingEnabled = checkIfDynamicScalingCanBeEnabled(vm, serviceOffering, newTemplate, vm.getDataCenterId()); + vm.setDynamicallyScalable(dynamicScalingEnabled); + _vmDao.update(vm.getId(), vm); + } + /** * Perform basic checkings to make sure restore is possible. If not, #InvalidParameterValueException is thrown. * @@ -7480,11 +7516,12 @@ public UserVm importVM(final DataCenter zone, final Host host, final VirtualMach final String uuidName = _uuidMgr.generateUuid(UserVm.class, null); final Host lastHost = powerState != VirtualMachine.PowerState.PowerOn ? host : null; + final Boolean dynamicScalingEnabled = checkIfDynamicScalingCanBeEnabled(null, serviceOffering, template, zone.getId()); return commitUserVm(true, zone, host, lastHost, template, hostName, displayName, owner, null, null, userData, caller, isDisplayVm, keyboard, accountId, userId, serviceOffering, template.getFormat().equals(ImageFormat.ISO), sshPublicKey, null, id, instanceName, uuidName, hypervisorType, customParameters, - null, null, null, powerState); + null, null, null, powerState, dynamicScalingEnabled); } @Override diff --git a/server/src/test/java/com/cloud/deploy/DeploymentPlanningManagerImplTest.java b/server/src/test/java/com/cloud/deploy/DeploymentPlanningManagerImplTest.java index ab73e63f287a..97e15de6f846 100644 --- a/server/src/test/java/com/cloud/deploy/DeploymentPlanningManagerImplTest.java +++ b/server/src/test/java/com/cloud/deploy/DeploymentPlanningManagerImplTest.java @@ -214,7 +214,7 @@ public void dataCenterAvoidTest() throws InsufficientServerCapacityException, Af ServiceOfferingVO svcOffering = new ServiceOfferingVO("testOffering", 1, 512, 500, 1, 1, false, false, false, "test dpm", ProvisioningType.THIN, false, false, null, false, VirtualMachine.Type.User, - null, "FirstFitPlanner"); + null, "FirstFitPlanner", true); Mockito.when(vmProfile.getServiceOffering()).thenReturn(svcOffering); DataCenterDeployment plan = new DataCenterDeployment(dataCenterId); @@ -229,7 +229,7 @@ public void plannerCannotHandleTest() throws InsufficientServerCapacityException ServiceOfferingVO svcOffering = new ServiceOfferingVO("testOffering", 1, 512, 500, 1, 1, false, false, false, "test dpm", ProvisioningType.THIN, false, false, null, false, VirtualMachine.Type.User, - null, "UserDispersingPlanner"); + null, "UserDispersingPlanner", true); Mockito.when(vmProfile.getServiceOffering()).thenReturn(svcOffering); DataCenterDeployment plan = new DataCenterDeployment(dataCenterId); @@ -245,7 +245,7 @@ public void emptyClusterListTest() throws InsufficientServerCapacityException, A ServiceOfferingVO svcOffering = new ServiceOfferingVO("testOffering", 1, 512, 500, 1, 1, false, false, false, "test dpm", ProvisioningType.THIN, false, false, null, false, VirtualMachine.Type.User, - null, "FirstFitPlanner"); + null, "FirstFitPlanner", true); Mockito.when(vmProfile.getServiceOffering()).thenReturn(svcOffering); DataCenterDeployment plan = new DataCenterDeployment(dataCenterId); diff --git a/test/integration/smoke/test_scale_vm.py b/test/integration/smoke/test_scale_vm.py index 64ab380b5414..c9b1bf837ba3 100644 --- a/test/integration/smoke/test_scale_vm.py +++ b/test/integration/smoke/test_scale_vm.py @@ -67,6 +67,11 @@ def setUpClass(cls): if cls.template == FAILED: assert False, "get_template() failed to return template\ with description %s" % cls.services["ostype"] + cls.template = Template.update( + cls.template, + cls.apiclient, + isdynamicallyscalable='true' + ) else: cls.template = Template.register( cls.apiclient, @@ -107,6 +112,18 @@ def setUpClass(cls): value="true" ) + cls.small_offering_dynamic_scaling_disabled = ServiceOffering.create( + cls.apiclient, + cls.services["service_offerings"]["small"], + dynamicscalingenabled=False + ) + + cls.big_offering_dynamic_scaling_disabled = ServiceOffering.create( + cls.apiclient, + cls.services["service_offerings"]["small"], + dynamicscalingenabled=False + ) + # create a virtual machine cls.virtual_machine = VirtualMachine.create( cls.apiclient, @@ -117,8 +134,42 @@ def setUpClass(cls): mode=cls.services["mode"] ) + # create a virtual machine which cannot be dynamically scalable + cls.virtual_machine_with_service_offering_dynamic_scaling_disabled = VirtualMachine.create( + cls.apiclient, + cls.services["small"], + accountid=cls.account.name, + domainid=cls.account.domainid, + serviceofferingid=cls.small_offering_dynamic_scaling_disabled.id, + mode=cls.services["mode"] + ) + + # create a virtual machine which cannot be dynamically scalable + cls.virtual_machine_not_dynamically_scalable = VirtualMachine.create( + cls.apiclient, + cls.services["small"], + accountid=cls.account.name, + domainid=cls.account.domainid, + serviceofferingid=cls.small_offering.id, + mode=cls.services["mode"], + dynamicscalingenabled=False + ) + + cls._cleanup = [ + cls.small_offering, + cls.big_offering, + cls.small_offering_dynamic_scaling_disabled, + cls.big_offering_dynamic_scaling_disabled, + cls.account + ] + @classmethod def tearDownClass(cls): + Configurations.update( + cls.apiclient, + name="enable.dynamic.scale.vm", + value="false" + ) super(TestScaleVm,cls).tearDownClass() return @@ -132,11 +183,6 @@ def setUp(self): %s" % self.hypervisor) def tearDown(self): - Configurations.update( - self.apiclient, - name="enable.dynamic.scale.vm", - value="false" - ) # Clean up, terminate the created ISOs super(TestScaleVm,self).tearDown() return @@ -265,3 +311,173 @@ def test_01_scale_vm(self): "Check the state of VM" ) return + + @attr(tags=["advanced", "basic"], required_hardware="false") + def test_02_scale_vm(self): + """Test scale virtual machine which is created from a service offering for which dynamicscalingenabled is false. Scaling operation should fail. + """ + + # VirtualMachine should be updated to tell cloudstack + # it has PV tools + # available and successfully scaled. We will only mock + # that behaviour + # here but it is not expected in production since the VM + # scaling is not + # guaranteed until tools are installed, vm rebooted + + # If hypervisor is Vmware, then check if + # the vmware tools are installed and the process is running + # Vmware tools are necessary for scale VM operation + if self.hypervisor.lower() == "vmware": + sshClient = self.virtual_machine_with_service_offering_dynamic_scaling_disabled.get_ssh_client() + result = str( + sshClient.execute("service vmware-tools status")).lower() + self.debug("and result is: %s" % result) + if not "running" in result: + self.skipTest("Skipping scale VM operation because\ + VMware tools are not installed on the VM") + + list_vm_response = VirtualMachine.list( + self.apiclient, + id=self.virtual_machine_with_service_offering_dynamic_scaling_disabled.id + ) + self.assertEqual( + isinstance(list_vm_response, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + list_vm_response, + None, + "Check virtual machine is in listVirtualMachines" + ) + + vm_response = list_vm_response[0] + self.assertEqual( + vm_response.id, + self.virtual_machine_with_service_offering_dynamic_scaling_disabled.id, + "Check virtual machine ID of scaled VM" + ) + + self.assertEqual( + vm_response.isdynamicallyscalable, + False, + "Check if VM is not dynamically scalable" + ) + + self.debug("Scaling VM-ID: %s to service offering: %s for which dynamic scaling is disabled and VM state %s" % ( + self.virtual_machine_with_service_offering_dynamic_scaling_disabled.id, + self.big_offering_dynamic_scaling_disabled.id, + self.virtual_machine.state + )) + + cmd = scaleVirtualMachine.scaleVirtualMachineCmd() + cmd.serviceofferingid = self.big_offering_dynamic_scaling_disabled.id + cmd.id = self.virtual_machine_with_service_offering_dynamic_scaling_disabled.id + + try: + self.apiclient.scaleVirtualMachine(cmd) + except Exception as e: + if "LicenceRestriction" in str(e): + self.skipTest("Your XenServer License does not allow scaling") + else: + pass + else: + self.fail("Expected an exception to be thrown, failing") + + self.debug("Scaling VM-ID: %s to service offering: %s for which dynamic scaling is enabled and VM state %s" % ( + self.virtual_machine_with_service_offering_dynamic_scaling_disabled.id, + self.big_offering.id, + self.virtual_machine.state + )) + + cmd = scaleVirtualMachine.scaleVirtualMachineCmd() + cmd.serviceofferingid = self.big_offering.id + cmd.id = self.virtual_machine_with_service_offering_dynamic_scaling_disabled.id + + try: + self.apiclient.scaleVirtualMachine(cmd) + except Exception as e: + if "LicenceRestriction" in str(e): + self.skipTest("Your XenServer License does not allow scaling") + else: + pass + else: + self.fail("Expected an exception to be thrown, failing") + + return + + @attr(tags=["advanced", "basic"], required_hardware="false") + def test_03_scale_vm(self): + """Test scale virtual machine which is not dynamically scalable to a service offering. Scaling operation should fail. + """ + # Validate the following + # Scale up the vm which is not dynamically scalable and see if scaling operation fails + + # VirtualMachine should be updated to tell cloudstack + # it has PV tools + # available and successfully scaled. We will only mock + # that behaviour + # here but it is not expected in production since the VM + # scaling is not + # guaranteed until tools are installed, vm rebooted + + # If hypervisor is Vmware, then check if + # the vmware tools are installed and the process is running + # Vmware tools are necessary for scale VM operation + if self.hypervisor.lower() == "vmware": + sshClient = self.virtual_machine_not_dynamically_scalable.get_ssh_client() + result = str( + sshClient.execute("service vmware-tools status")).lower() + self.debug("and result is: %s" % result) + if not "running" in result: + self.skipTest("Skipping scale VM operation because\ + VMware tools are not installed on the VM") + + list_vm_response = VirtualMachine.list( + self.apiclient, + id=self.virtual_machine_not_dynamically_scalable.id + ) + self.assertEqual( + isinstance(list_vm_response, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + list_vm_response, + None, + "Check virtual machine is in listVirtualMachines" + ) + vm_response = list_vm_response[0] + self.assertEqual( + vm_response.id, + self.virtual_machine_not_dynamically_scalable.id, + "Check virtual machine ID of scaled VM" + ) + self.assertEqual( + vm_response.isdynamicallyscalable, + False, + "Check if VM is not dynamically scalable" + ) + + self.debug("Scaling VM-ID: %s to service offering: %s for which dynamic scaling is enabled and VM state %s" % ( + self.virtual_machine_not_dynamically_scalable.id, + self.big_offering.id, + self.virtual_machine.state + )) + + cmd = scaleVirtualMachine.scaleVirtualMachineCmd() + cmd.serviceofferingid = self.big_offering.id + cmd.id = self.virtual_machine_not_dynamically_scalable.id + + try: + self.apiclient.scaleVirtualMachine(cmd) + except Exception as e: + if "LicenceRestriction" in str(e): + self.skipTest("Your XenServer License does not allow scaling") + else: + pass + else: + self.fail("Expected an exception to be thrown, failing") + + return \ No newline at end of file diff --git a/tools/marvin/marvin/lib/base.py b/tools/marvin/marvin/lib/base.py index 5a8db630a500..916af64d96cc 100755 --- a/tools/marvin/marvin/lib/base.py +++ b/tools/marvin/marvin/lib/base.py @@ -522,7 +522,7 @@ def create(cls, apiclient, services, templateid=None, accountid=None, method='GET', hypervisor=None, customcpunumber=None, customcpuspeed=None, custommemory=None, rootdisksize=None, rootdiskcontroller=None, vpcid=None, macaddress=None, datadisktemplate_diskoffering_list={}, - properties=None, nicnetworklist=None, bootmode=None, boottype=None): + properties=None, nicnetworklist=None, bootmode=None, boottype=None, dynamicscalingenabled=None): """Create the instance""" cmd = deployVirtualMachine.deployVirtualMachineCmd() @@ -615,6 +615,9 @@ def create(cls, apiclient, services, templateid=None, accountid=None, if "dhcpoptionsnetworklist" in services: cmd.dhcpoptionsnetworklist = services["dhcpoptionsnetworklist"] + if dynamicscalingenabled is not None: + cmd.dynamicscalingenabled = dynamicscalingenabled + cmd.details = [{}] if customcpunumber: @@ -2293,6 +2296,9 @@ def create(cls, apiclient, services, tags=None, domainid=None, cacheMode=None, * if "offerha" in services: cmd.offerha = services["offerha"] + if "dynamicscalingenabled" in services: + cmd.dynamicscalingenabled = services["dynamicscalingenabled"] + # Service Offering private to that domain if domainid: cmd.domainid = domainid diff --git a/ui/public/locales/de_DE.json b/ui/public/locales/de_DE.json index 348b1075c56b..50711fc003b0 100644 --- a/ui/public/locales/de_DE.json +++ b/ui/public/locales/de_DE.json @@ -783,6 +783,7 @@ "label.drag.new.position": "Ziehe zu neuer Position", "label.driver": "Treiber", "label.duration.in.sec": "Dauer (in Sekunden)", +"label.dynamicscalingenabled": "Dynamische Skalierung aktiviert", "label.edit": "Bearbeiten", "label.edit.acl.list": "Edit ACL List", "label.edit.acl.rule": "ACL-Regel bearbeiten", diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json index 190398287e62..77334fb98767 100644 --- a/ui/public/locales/en.json +++ b/ui/public/locales/en.json @@ -810,6 +810,8 @@ "label.drag.new.position": "Drag to new position", "label.driver": "Driver", "label.duration.in.sec": "Duration (in sec)", +"label.dynamicscalingenabled": "Dynamic Scaling Enabled", +"label.dynamicscalingenabled.tooltip": "VM can dynamically scale only when dynamic scaling is enabled on template, service offering and global setting", "label.edit": "Edit", "label.edit.acl.list": "Edit ACL List", "label.edit.acl.rule": "Edit ACL rule", diff --git a/ui/public/locales/es.json b/ui/public/locales/es.json index c05967bf56c7..df225b90212f 100644 --- a/ui/public/locales/es.json +++ b/ui/public/locales/es.json @@ -670,6 +670,7 @@ "label.drag.new.position": "Arrastrar a una nueva ubicaci\u00f3n", "label.driver": "Controlador", "label.duration.in.sec": "Duraci\u00f3n (en seg)", +"label.dynamicscalingenabled": "Escalado din\u00e1mico habilitado", "label.edit": "Editar", "label.edit.acl.list": "Edit ACL List", "label.edit.acl.rule": "Editar regla ACL", diff --git a/ui/public/locales/hu.json b/ui/public/locales/hu.json index 5a010ff1ee2c..46efd9049a16 100644 --- a/ui/public/locales/hu.json +++ b/ui/public/locales/hu.json @@ -658,6 +658,7 @@ "label.drag.new.position": "\u00daj helyre h\u00faz\u00e1s", "label.driver": "Driver", "label.duration.in.sec": "Id\u0151tartam (mp)", +"label.dynamicscalingenabled": "dinamikus méretezés engedélyezve", "label.edit": "Szerkeszt\u00e9s", "label.edit.acl.list": "Edit ACL List", "label.edit.acl.rule": "ACL szab\u00e1ly szerkeszt\u00e9se", diff --git a/ui/public/locales/nl_NL.json b/ui/public/locales/nl_NL.json index 07aae3063b00..89fcb701c44e 100644 --- a/ui/public/locales/nl_NL.json +++ b/ui/public/locales/nl_NL.json @@ -658,6 +658,7 @@ "label.dpd": "Dead Peer detectie", "label.drag.new.position": "Sleep naar nieuwe positie", "label.driver": "Driver", +"label.dynamicscalingenabled": "Dynamisch schalen ingeschakeld\n", "label.duration.in.sec": "duur (in sec)", "label.edit": "Wijzig", "label.edit.acl.list": "Verander een ACL lijst", diff --git a/ui/src/components/view/ActionButton.vue b/ui/src/components/view/ActionButton.vue index bf9f63b0fdd6..5202cc6001d2 100644 --- a/ui/src/components/view/ActionButton.vue +++ b/ui/src/components/view/ActionButton.vue @@ -39,7 +39,8 @@ action.showBadge && ( (!dataView && ((action.listView && ('show' in action ? action.show(resource, $store.getters) : true)) || (action.groupAction && selectedRowKeys.length > 0 && ('groupShow' in action ? action.show(resource, $store.getters) : true)))) || (dataView && action.dataView && ('show' in action ? action.show(resource, $store.getters) : true)) - )" > + )" + :disabled="'disabled' in action ? action.disabled(resource, $store.getters) : false" > 0 && ('groupShow' in action ? action.show(resource, $store.getters) : true)))) || (dataView && action.dataView && ('show' in action ? action.show(resource, $store.getters) : true)) )" + :disabled="'disabled' in action ? action.disabled(resource, $store.getters) : false" :type="action.icon === 'delete' ? 'danger' : (action.icon === 'plus' ? 'primary' : 'default')" :shape="!dataView && ['plus', 'user-add'].includes(action.icon) ? 'round' : 'circle'" style="margin-left: 5px" diff --git a/ui/src/config/section/compute.js b/ui/src/config/section/compute.js index 3f97995cd160..db6ab88d7367 100644 --- a/ui/src/config/section/compute.js +++ b/ui/src/config/section/compute.js @@ -287,6 +287,7 @@ export default { docHelp: 'adminguide/virtual_machines.html#how-to-dynamically-scale-cpu-and-ram', dataView: true, show: (record) => { return ['Stopped'].includes(record.state) || (['Running'].includes(record.state) && record.hypervisor !== 'KVM' && record.hypervisor !== 'LXC') }, + disabled: (record) => { return !record.isdynamicallyscalable }, popup: true, component: () => import('@/views/compute/ScaleVM.vue') }, diff --git a/ui/src/config/section/infra/systemVms.js b/ui/src/config/section/infra/systemVms.js index 181b8e8dacea..cdde06805562 100644 --- a/ui/src/config/section/infra/systemVms.js +++ b/ui/src/config/section/infra/systemVms.js @@ -22,7 +22,7 @@ export default { docHelp: 'adminguide/systemvm.html', permission: ['listSystemVms'], columns: ['name', 'state', 'agentstate', 'systemvmtype', 'publicip', 'privateip', 'linklocalip', 'hostname', 'zonename'], - details: ['name', 'id', 'agentstate', 'systemvmtype', 'publicip', 'privateip', 'linklocalip', 'gateway', 'hostname', 'zonename', 'created', 'activeviewersessions'], + details: ['name', 'id', 'agentstate', 'systemvmtype', 'publicip', 'privateip', 'linklocalip', 'gateway', 'hostname', 'zonename', 'created', 'activeviewersessions', 'isdynamicallyscalable'], actions: [ { api: 'startSystemVm', diff --git a/ui/src/config/section/offering.js b/ui/src/config/section/offering.js index b8b2beff3d96..f495841e49b3 100644 --- a/ui/src/config/section/offering.js +++ b/ui/src/config/section/offering.js @@ -31,7 +31,7 @@ export default { params: { isrecursive: 'true' }, columns: ['name', 'displaytext', 'cpunumber', 'cpuspeed', 'memory', 'domain', 'zone', 'order'], details: () => { - var fields = ['name', 'id', 'displaytext', 'offerha', 'provisioningtype', 'storagetype', 'iscustomized', 'limitcpuuse', 'cpunumber', 'cpuspeed', 'memory', 'hosttags', 'tags', 'domain', 'zone', 'created'] + var fields = ['name', 'id', 'displaytext', 'offerha', 'provisioningtype', 'storagetype', 'iscustomized', 'limitcpuuse', 'cpunumber', 'cpuspeed', 'memory', 'hosttags', 'tags', 'domain', 'zone', 'created', 'dynamicscalingenabled'] if (store.getters.apis.createServiceOffering && store.getters.apis.createServiceOffering.params.filter(x => x.name === 'storagepolicy').length > 0) { fields.splice(6, 0, 'vspherestoragepolicy') @@ -87,7 +87,7 @@ export default { permission: ['listServiceOfferings', 'listInfrastructure'], params: { issystem: 'true', isrecursive: 'true' }, columns: ['name', 'systemvmtype', 'cpunumber', 'cpuspeed', 'memory', 'storagetype', 'order'], - details: ['name', 'id', 'displaytext', 'systemvmtype', 'provisioningtype', 'storagetype', 'iscustomized', 'limitcpuuse', 'cpunumber', 'cpuspeed', 'memory', 'hosttags', 'tags', 'domain', 'zone', 'created'], + details: ['name', 'id', 'displaytext', 'systemvmtype', 'provisioningtype', 'storagetype', 'iscustomized', 'limitcpuuse', 'cpunumber', 'cpuspeed', 'memory', 'hosttags', 'tags', 'domain', 'zone', 'created', 'dynamicscalingenabled'], actions: [{ api: 'createServiceOffering', icon: 'plus', diff --git a/ui/src/views/compute/DeployVM.vue b/ui/src/views/compute/DeployVM.vue index c0965bfd593e..bccd9287e675 100644 --- a/ui/src/views/compute/DeployVM.vue +++ b/ui/src/views/compute/DeployVM.vue @@ -509,6 +509,21 @@ v-decorator="['bootintosetup']"> + + + {{ $t('label.dynamicscalingenabled') }} + + + + + + + + @@ -675,6 +690,7 @@ export default { clusterId: null, zoneSelected: false, startvm: true, + dynamicscalingenabled: true, vm: { name: null, zoneid: null, @@ -710,7 +726,8 @@ export default { groups: [], keyboards: [], bootTypes: [], - bootModes: [] + bootModes: [], + dynamicScalingVmConfig: false }, rowCount: {}, loading: { @@ -892,6 +909,13 @@ export default { type: 'Routing' }, field: 'hostid' + }, + dynamicScalingVmConfig: { + list: 'listConfigurations', + options: { + zoneid: _.get(this.zone, 'id'), + name: 'enable.dynamic.scale.vm' + } } } }, @@ -970,6 +994,9 @@ export default { }, showSecurityGroupSection () { return (this.networks.length > 0 && this.zone.securitygroupsenabled) || (this.zone && this.zone.networktype === 'Basic') + }, + dynamicScalingVmConfigValue () { + return this.options.dynamicScalingVmConfig?.[0]?.value === 'true' } }, watch: { @@ -1086,6 +1113,16 @@ export default { } } }, + serviceOffering (oldValue, newValue) { + if (oldValue && newValue && oldValue.id !== newValue.id) { + this.dynamicscalingenabled = this.isDynamicallyScalable() + } + }, + template (oldValue, newValue) { + if (oldValue && newValue && oldValue.id !== newValue.id) { + this.dynamicscalingenabled = this.isDynamicallyScalable() + } + }, created () { this.form = this.$form.createForm(this, { onValuesChange: (props, fields) => { @@ -1179,6 +1216,9 @@ export default { this.instanceConfig = this.form.getFieldsValue() // ToDo: maybe initialize with some other defaults }) }, + isDynamicallyScalable () { + return this.serviceOffering && this.serviceOffering.dynamicscalingenabled && this.template && this.template.isdynamicallyscalable && this.dynamicScalingVmConfigValue + }, async fetchDataByZone (zoneId) { this.fillValue('zoneid') this.options.zones = await this.fetchZones() @@ -1416,6 +1456,7 @@ export default { deployVmData.keyboard = values.keyboard deployVmData.boottype = values.boottype deployVmData.bootmode = values.bootmode + deployVmData.dynamicscalingenabled = values.dynamicscalingenabled if (values.userdata && values.userdata.length > 0) { deployVmData.userdata = encodeURIComponent(btoa(this.sanitizeReverse(values.userdata))) } diff --git a/ui/src/views/compute/EditVM.vue b/ui/src/views/compute/EditVM.vue index c9f342ba3928..1aa654bfff53 100644 --- a/ui/src/views/compute/EditVM.vue +++ b/ui/src/views/compute/EditVM.vue @@ -72,7 +72,8 @@ + v-decorator="['isdynamicallyscalable']" + :disabled="!canDynamicScalingEnabled()" /> @@ -125,6 +126,9 @@ export default { }, data () { return { + serviceOffering: {}, + template: {}, + dynamicScalingVmConfig: false, loading: false, osTypes: { loading: false, @@ -151,6 +155,44 @@ export default { fetchData () { this.fetchOsTypes() this.fetchInstaceGroups() + this.fetchServiceOfferingData() + this.fetchTemplateData() + this.fetchDynamicScalingVmConfig() + }, + fetchServiceOfferingData () { + const params = {} + params.id = this.resource.serviceofferingid + params.isrecursive = true + var apiName = 'listServiceOfferings' + api(apiName, params).then(json => { + const offerings = json.listserviceofferingsresponse.serviceoffering + this.serviceOffering = offerings[0] + }) + }, + fetchTemplateData () { + const params = {} + console.log('templateid ' + this.resource.templateid) + params.id = this.resource.templateid + params.isrecursive = true + params.templatefilter = 'all' + var apiName = 'listTemplates' + api(apiName, params).then(json => { + const templateResponses = json.listtemplatesresponse.template + this.template = templateResponses[0] + }) + }, + fetchDynamicScalingVmConfig () { + const params = {} + params.name = 'enable.dynamic.scale.vm' + params.zoneid = this.resource.zoneid + var apiName = 'listConfigurations' + api(apiName, params).then(json => { + const configResponse = json.listconfigurationsresponse.configuration + this.dynamicScalingVmConfig = configResponse[0]?.value === 'true' + }) + }, + canDynamicScalingEnabled () { + return this.template.isdynamicallyscalable && this.serviceOffering.dynamicscalingenabled && this.dynamicScalingVmConfig }, fetchOsTypes () { this.osTypes.loading = true diff --git a/ui/src/views/offering/AddComputeOffering.vue b/ui/src/views/offering/AddComputeOffering.vue index 1c13dc3dca16..c61d2d10a07f 100644 --- a/ui/src/views/offering/AddComputeOffering.vue +++ b/ui/src/views/offering/AddComputeOffering.vue @@ -532,6 +532,15 @@ + + + {{ $t('label.dynamicscalingenabled') }} + + + + + + {{ $t('label.hosttags') }} @@ -797,7 +806,8 @@ export default { ], vGpuVisible: false, vGpuTypes: [], - loading: false + loading: false, + dynamicscalingenabled: true } }, beforeCreate () { @@ -959,7 +969,8 @@ export default { cachemode: values.cachemode, customized: values.offeringtype !== 'fixed', offerha: values.offerha === true, - limitcpuuse: values.limitcpuuse === true + limitcpuuse: values.limitcpuuse === true, + dynamicscalingenabled: values.dynamicscalingenabled } // custom fields (begin)