You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by ro...@apache.org on 2021/03/29 07:25:09 UTC

[cloudstack] branch 4.15 updated: server: Fix issue with volume resize on VMWare (deploy as-is templates) (#4829)

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

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


The following commit(s) were added to refs/heads/4.15 by this push:
     new 9717669  server: Fix issue with volume resize on VMWare (deploy as-is templates) (#4829)
9717669 is described below

commit 97176690b85ff4c63ced2f37888252cd1322eef8
Author: Pearl Dsilva <pe...@gmail.com>
AuthorDate: Mon Mar 29 12:54:47 2021 +0530

    server: Fix issue with volume resize on VMWare (deploy as-is templates) (#4829)
    
    This PR fixes the issue pertaining to volume resize on VMWare for deploy as-is templates. VMware deploy as-is templates are those that are deployed as per the specification in the imported OVF. Hence override root disk size will not be adhered to for such templates. Moreover, when we deploy VMs in stopped state and resize the volume, the root disk doesn't get resized but the volume size is merely updated in the DB.
    This PR also includes the following (for deploy as-is templates):
    - Disables overriding root disk size during VM deployment on the UI
    - Disables selection of compute offerings with root disk size specified, at the time of deployment
    - Provided users with the option to deploy VM is stopped state via UI (so as to give an option to users to resize the volumes before starting the VM)
    
    Co-authored-by: Pearl Dsilva <pe...@shapeblue.com>
---
 .../hypervisor/vmware/resource/VmwareResource.java |  8 ++++-
 ...ernetesClusterResourceModifierActionWorker.java | 34 ++++++++++++++++++++++
 .../KubernetesClusterStartWorker.java              |  2 ++
 .../com/cloud/storage/VolumeApiServiceImpl.java    |  9 +++++-
 .../main/java/com/cloud/vm/UserVmManagerImpl.java  | 22 ++++++++++++--
 ui/public/locales/en.json                          |  1 +
 ui/src/views/compute/DeployVM.vue                  | 11 ++++++-
 .../compute/wizard/ComputeOfferingSelection.vue    | 10 +++++++
 8 files changed, 91 insertions(+), 6 deletions(-)

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 56d08a4..e59c757 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
@@ -2071,7 +2071,13 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
             // Setup ROOT/DATA disk devices
             //
             for (DiskTO vol : sortedDisks) {
-                if (vol.getType() == Volume.Type.ISO || deployAsIs && vol.getType() == Volume.Type.ROOT) {
+                if (vol.getType() == Volume.Type.ISO) {
+                    continue;
+                }
+
+                if (deployAsIs && vol.getType() == Volume.Type.ROOT) {
+                    rootDiskTO = vol;
+                    resizeRootDiskOnVMStart(vmMo, rootDiskTO, hyperHost, context);
                     continue;
                 }
 
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 b715f09..c771cf3 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
@@ -31,6 +31,7 @@ import org.apache.cloudstack.api.ApiConstants;
 import org.apache.cloudstack.api.BaseCmd;
 import org.apache.cloudstack.api.command.user.firewall.CreateFirewallRuleCmd;
 import org.apache.cloudstack.api.command.user.vm.StartVMCmd;
+import org.apache.cloudstack.api.command.user.volume.ResizeVolumeCmd;
 import org.apache.commons.codec.binary.Base64;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.log4j.Level;
@@ -70,6 +71,10 @@ import com.cloud.network.rules.RulesService;
 import com.cloud.network.rules.dao.PortForwardingRulesDao;
 import com.cloud.offering.ServiceOffering;
 import com.cloud.resource.ResourceManager;
+import com.cloud.storage.Volume;
+import com.cloud.storage.VolumeApiService;
+import com.cloud.storage.VolumeVO;
+import com.cloud.storage.dao.VolumeDao;
 import com.cloud.user.Account;
 import com.cloud.user.SSHKeyPairVO;
 import com.cloud.uservm.UserVm;
@@ -118,6 +123,10 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu
     protected VMInstanceDao vmInstanceDao;
     @Inject
     protected UserVmManager userVmManager;
+    @Inject
+    protected VolumeApiService volumeService;
+    @Inject
+    protected VolumeDao volumeDao;
 
     protected String kubernetesClusterNodeNamePrefix;
 
@@ -268,6 +277,29 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu
         return plan(kubernetesCluster.getTotalNodeCount(), zone, offering);
     }
 
+    protected void resizeNodeVolume(final UserVm vm) throws ManagementServerException {
+        try {
+            if (vm.getHypervisorType() == Hypervisor.HypervisorType.VMware && templateDao.findById(vm.getTemplateId()).isDeployAsIs()) {
+                List<VolumeVO> vmVols = volumeDao.findByInstance(vm.getId());
+                for (VolumeVO volumeVO : vmVols) {
+                    if (volumeVO.getVolumeType() == Volume.Type.ROOT) {
+                        ResizeVolumeCmd resizeVolumeCmd = new ResizeVolumeCmd();
+                        resizeVolumeCmd = ComponentContext.inject(resizeVolumeCmd);
+                        Field f = resizeVolumeCmd.getClass().getDeclaredField("size");
+                        Field f1 = resizeVolumeCmd.getClass().getDeclaredField("id");
+                        f.setAccessible(true);
+                        f1.setAccessible(true);
+                        f1.set(resizeVolumeCmd, volumeVO.getId());
+                        f.set(resizeVolumeCmd, kubernetesCluster.getNodeRootDiskSize());
+                        volumeService.resizeVolume(resizeVolumeCmd);
+                    }
+                }
+            }
+        } catch (IllegalAccessException | NoSuchFieldException e) {
+            throw new ManagementServerException(String.format("Failed to resize volume of  VM in the Kubernetes cluster : %s", kubernetesCluster.getName()), e);
+        }
+    }
+
     protected void startKubernetesVM(final UserVm vm) throws ManagementServerException {
         try {
             StartVMCmd startVm = new StartVMCmd();
@@ -275,6 +307,7 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu
             Field f = startVm.getClass().getDeclaredField("id");
             f.setAccessible(true);
             f.set(startVm, vm.getId());
+            resizeNodeVolume(vm);
             userVmService.startVirtualMachine(startVm);
             if (LOGGER.isInfoEnabled()) {
                 LOGGER.info(String.format("Started VM : %s in the Kubernetes cluster : %s", vm.getDisplayName(), kubernetesCluster.getName()));
@@ -296,6 +329,7 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu
         for (int i = offset + 1; i <= nodeCount; i++) {
             UserVm vm = createKubernetesNode(publicIpAddress, i);
             addKubernetesClusterVm(kubernetesCluster.getId(), vm.getId());
+            resizeNodeVolume(vm);
             startKubernetesVM(vm);
             vm = userVmDao.findById(vm.getId());
             if (vm == null) {
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 855c264..5855319 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
@@ -277,6 +277,7 @@ public class KubernetesClusterStartWorker extends KubernetesClusterResourceModif
         UserVm k8sMasterVM = null;
         k8sMasterVM = createKubernetesMaster(network, publicIpAddress);
         addKubernetesClusterVm(kubernetesCluster.getId(), k8sMasterVM.getId());
+        resizeNodeVolume(k8sMasterVM);
         startKubernetesVM(k8sMasterVM);
         k8sMasterVM = userVmDao.findById(k8sMasterVM.getId());
         if (k8sMasterVM == null) {
@@ -296,6 +297,7 @@ public class KubernetesClusterStartWorker extends KubernetesClusterResourceModif
                 UserVm vm = null;
                 vm = createKubernetesAdditionalMaster(publicIpAddress, i);
                 addKubernetesClusterVm(kubernetesCluster.getId(), vm.getId());
+                resizeNodeVolume(vm);
                 startKubernetesVM(vm);
                 vm = userVmDao.findById(vm.getId());
                 if (vm == null) {
diff --git a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
index 0adeb83..dc3bf04 100644
--- a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
+++ b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
@@ -916,8 +916,15 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
         }
 
         // if we are to use the existing disk offering
+        ImageFormat format = null;
         if (newDiskOffering == null) {
-            if (volume.getVolumeType().equals(Volume.Type.ROOT) && diskOffering.getDiskSize() > 0) {
+            Long templateId = volume.getTemplateId();
+            if (templateId != null) {
+                VMTemplateVO template = _templateDao.findById(templateId);
+                format = template.getFormat();
+            }
+
+            if (volume.getVolumeType().equals(Volume.Type.ROOT) && diskOffering.getDiskSize() > 0 && format != null && format != ImageFormat.ISO) {
                 throw new InvalidParameterValueException(
                         "Failed to resize Root volume. The service offering of this Volume has been configured with a root disk size; "
                                 + "on such case a Root Volume can only be resized when changing to another Service Offering with a Root disk size. "
diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
index d94c00c..737beb5 100644
--- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
+++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
@@ -49,6 +49,8 @@ import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
 
 import com.cloud.agent.api.to.deployasis.OVFPropertyTO;
+import com.cloud.api.query.dao.ServiceOfferingJoinDao;
+import com.cloud.api.query.vo.ServiceOfferingJoinVO;
 import com.cloud.deployasis.UserVmDeployAsIsDetailVO;
 import com.cloud.deployasis.dao.UserVmDeployAsIsDetailsDao;
 import com.cloud.exception.UnsupportedServiceException;
@@ -514,6 +516,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
     private UserVmDeployAsIsDetailsDao userVmDeployAsIsDetailsDao;
     @Inject
     private StorageManager storageMgr;
+    @Inject
+    private ServiceOfferingJoinDao serviceOfferingJoinDao;
 
     private ScheduledExecutorService _executor = null;
     private ScheduledExecutorService _vmIpFetchExecutor = null;
@@ -5266,9 +5270,21 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
             throw new InvalidParameterValueException("Unable to use template " + templateId);
         }
 
-        // Bootmode and boottype are not supported on VMWare dpeloy-as-is templates (since 4.15)
-        if (template.isDeployAsIs() && (cmd.getBootMode() != null || cmd.getBootType() != null)) {
-            throw new InvalidParameterValueException("Boot type and boot mode are not supported on VMware, as we honour what is defined in the template.");
+        ServiceOfferingJoinVO svcOffering = serviceOfferingJoinDao.findById(serviceOfferingId);
+
+        if (template.isDeployAsIs()) {
+            if (svcOffering != null && svcOffering.getRootDiskSize() != null && svcOffering.getRootDiskSize() > 0) {
+                throw new InvalidParameterValueException("Failed to deploy Virtual Machine as a service offering with root disk size specified cannot be used with a deploy as-is template");
+            }
+
+            if (cmd.getDetails().get("rootdisksize") != null) {
+                throw new InvalidParameterValueException("Overriding root disk size isn't supported for VMs deployed from defploy as-is templates");
+            }
+
+            // Bootmode and boottype are not supported on VMWare dpeloy-as-is templates (since 4.15)
+            if ((cmd.getBootMode() != null || cmd.getBootType() != null)) {
+                throw new InvalidParameterValueException("Boot type and boot mode are not supported on VMware, as we honour what is defined in the template.");
+            }
         }
 
         Long diskOfferingId = cmd.getDiskOfferingId();
diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json
index a003577..476bd89e 100644
--- a/ui/public/locales/en.json
+++ b/ui/public/locales/en.json
@@ -2682,6 +2682,7 @@
 "message.delete.vpn.customer.gateway": "Please confirm that you want to delete this VPN Customer Gateway",
 "message.delete.vpn.gateway": "Please confirm that you want to delete this VPN Gateway",
 "message.deleting.vm": "Deleting VM",
+"message.deployasis": "Selected template is Deploy As-Is i.e., the VM is deployed by importing an OVA with vApps directly into vCenter. Root disk(s) resize is allowed only on stopped VMs for such templates.", 
 "message.desc.add.new.lb.sticky.rule": "Add new LB sticky rule",
 "message.desc.advanced.zone": "For more sophisticated network topologies. This network model provides the most flexibility in defining guest networks and providing custom network offerings such as firewall, VPN, or load balancer support.",
 "message.desc.basic.zone": "Provide a single network where each VM instance is assigned an IP directly from the network. Guest isolation can be provided through layer-3 means such as security groups (IP address source filtering).",
diff --git a/ui/src/views/compute/DeployVM.vue b/ui/src/views/compute/DeployVM.vue
index b2a5fee..c4c20de 100644
--- a/ui/src/views/compute/DeployVM.vue
+++ b/ui/src/views/compute/DeployVM.vue
@@ -105,7 +105,8 @@
                           @update-template-iso="updateFieldValue" />
                         <span>
                           {{ $t('label.override.rootdisk.size') }}
-                          <a-switch @change="val => { this.showRootDiskSizeChanger = val }" style="margin-left: 10px;"/>
+                          <a-switch :disabled="template.deployasis" @change="val => { this.showRootDiskSizeChanger = val }" style="margin-left: 10px;"/>
+                          <div v-if="template.deployasis">  {{ this.$t('message.deployasis') }} </div>
                         </span>
                         <disk-size-selection
                           v-show="showRootDiskSizeChanger"
@@ -183,6 +184,7 @@
                     </a-form-item>
                     <compute-offering-selection
                       :compute-items="options.serviceOfferings"
+                      :selected-template="template"
                       :row-count="rowCount.serviceOfferings"
                       :zoneId="zoneId"
                       :value="serviceOffering ? serviceOffering.id : ''"
@@ -543,6 +545,9 @@
                         :options="keyboardSelectOptions"
                       ></a-select>
                     </a-form-item>
+                    <a-form-item :label="$t('label.action.start.instance')">
+                      <a-switch v-decorator="['startvm', { initialValue: true }]" :checked="this.startvm" @change="checked => { this.startvm = checked }" />
+                    </a-form-item>
                   </div>
                 </template>
               </a-step>
@@ -663,6 +668,7 @@ export default {
       podId: null,
       clusterId: null,
       zoneSelected: false,
+      startvm: true,
       vm: {
         name: null,
         zoneid: null,
@@ -1419,6 +1425,9 @@ export default {
         if (values.hypervisor && values.hypervisor.length > 0) {
           deployVmData.hypervisor = values.hypervisor
         }
+
+        deployVmData.startvm = values.startvm
+
         // step 3: select service offering
         deployVmData.serviceofferingid = values.computeofferingid
         if (this.serviceOffering && this.serviceOffering.iscustomized) {
diff --git a/ui/src/views/compute/wizard/ComputeOfferingSelection.vue b/ui/src/views/compute/wizard/ComputeOfferingSelection.vue
index c372b82..f1a6128 100644
--- a/ui/src/views/compute/wizard/ComputeOfferingSelection.vue
+++ b/ui/src/views/compute/wizard/ComputeOfferingSelection.vue
@@ -63,6 +63,10 @@ export default {
       type: Array,
       default: () => []
     },
+    selectedTemplate: {
+      type: Object,
+      default: () => {}
+    },
     rowCount: {
       type: Number,
       default: () => 0
@@ -161,6 +165,9 @@ export default {
             (item.iscustomized === true && maxMemory < this.minimumMemory))) {
           disabled = true
         }
+        if (this.selectedTemplate && this.selectedTemplate.hypervisor === 'VMware' && this.selectedTemplate.deployasis && item.rootdisksize) {
+          disabled = true
+        }
         return {
           key: item.id,
           name: item.name,
@@ -238,6 +245,9 @@ export default {
       return {
         on: {
           click: () => {
+            if (record.disabled) {
+              return
+            }
             this.selectedRowKeys = [record.key]
             this.$emit('select-compute-item', record.key)
           }