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

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


The following commit(s) were added to refs/heads/4.16 by this push:
     new 8adb8df  server: find suitable disk offering for volume upload (#5852)
8adb8df is described below

commit 8adb8df2fe8a3b29f19dc571b65ac33987f9be59
Author: Abhishek Kumar <[email protected]>
AuthorDate: Wed Feb 2 16:35:47 2022 +0530

    server: find suitable disk offering for volume upload (#5852)
    
    * server: find suitable disk offering for volume upload
    
    Fixes #5696
    
    * fix npe check
    
    * fixes, refactor, rename method and handle custom iops
    
    * ui: allow offering selection
    
    * list only disk offerings
    
    * show name
    
    * revert error check
    
    * use checkaccess
    
    Signed-off-by: Abhishek Kumar <[email protected]>
---
 .../com/cloud/storage/dao/DiskOfferingDao.java     |  2 +
 .../com/cloud/storage/dao/DiskOfferingDaoImpl.java | 13 +++++
 .../api/query/dao/DiskOfferingJoinDaoImpl.java     | 14 ++---
 .../com/cloud/storage/VolumeApiServiceImpl.java    | 60 ++++++++++++++++------
 ui/src/views/storage/UploadLocalVolume.vue         | 58 +++++++++++++++++++--
 5 files changed, 121 insertions(+), 26 deletions(-)

diff --git 
a/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDao.java 
b/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDao.java
index 3305752..2425e58 100644
--- a/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDao.java
+++ b/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDao.java
@@ -34,4 +34,6 @@ public interface DiskOfferingDao extends 
GenericDao<DiskOfferingVO, Long> {
 
     List<DiskOfferingVO> listAllBySizeAndProvisioningType(long size, 
Storage.ProvisioningType provisioningType);
 
+    List<DiskOfferingVO> findCustomDiskOfferings();
+
 }
diff --git 
a/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDaoImpl.java 
b/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDaoImpl.java
index b9fa10c..ce52b0e 100644
--- a/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDaoImpl.java
+++ b/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDaoImpl.java
@@ -29,6 +29,7 @@ import javax.persistence.EntityExistsException;
 import org.apache.cloudstack.resourcedetail.dao.DiskOfferingDetailsDao;
 import org.springframework.stereotype.Component;
 
+import com.cloud.offering.DiskOffering;
 import com.cloud.offering.DiskOffering.Type;
 import com.cloud.storage.DiskOfferingVO;
 import com.cloud.storage.Storage;
@@ -174,6 +175,18 @@ public class DiskOfferingDaoImpl extends 
GenericDaoBase<DiskOfferingVO, Long> im
     }
 
     @Override
+    public List<DiskOfferingVO> findCustomDiskOfferings() {
+        SearchBuilder<DiskOfferingVO> sb = createSearchBuilder();
+        sb.and("type", sb.entity().getType(), SearchCriteria.Op.EQ);
+        sb.and("customized", sb.entity().isCustomized(), SearchCriteria.Op.EQ);
+        sb.done();
+        SearchCriteria<DiskOfferingVO> sc = sb.create();
+        sc.setParameters("customized", true);
+        sc.setParameters("type", DiskOffering.Type.Disk.toString());
+        return listBy(sc);
+    }
+
+    @Override
     public boolean remove(Long id) {
         DiskOfferingVO diskOffering = createForUpdate();
         diskOffering.setRemoved(new Date());
diff --git 
a/server/src/main/java/com/cloud/api/query/dao/DiskOfferingJoinDaoImpl.java 
b/server/src/main/java/com/cloud/api/query/dao/DiskOfferingJoinDaoImpl.java
index 685f301..a738aed 100644
--- a/server/src/main/java/com/cloud/api/query/dao/DiskOfferingJoinDaoImpl.java
+++ b/server/src/main/java/com/cloud/api/query/dao/DiskOfferingJoinDaoImpl.java
@@ -19,11 +19,8 @@ package com.cloud.api.query.dao;
 import java.util.List;
 import java.util.Map;
 
-import com.cloud.api.ApiDBUtils;
-import com.cloud.dc.VsphereStoragePolicyVO;
-import com.cloud.dc.dao.VsphereStoragePolicyDao;
-import com.cloud.server.ResourceTag;
-import com.cloud.user.AccountManager;
+import javax.inject.Inject;
+
 import org.apache.cloudstack.annotation.AnnotationService;
 import org.apache.cloudstack.annotation.dao.AnnotationDao;
 import org.apache.cloudstack.api.ApiConstants;
@@ -32,16 +29,19 @@ import org.apache.cloudstack.context.CallContext;
 import org.apache.log4j.Logger;
 import org.springframework.stereotype.Component;
 
+import com.cloud.api.ApiDBUtils;
 import com.cloud.api.query.vo.DiskOfferingJoinVO;
+import com.cloud.dc.VsphereStoragePolicyVO;
+import com.cloud.dc.dao.VsphereStoragePolicyDao;
 import com.cloud.offering.DiskOffering;
 import com.cloud.offering.ServiceOffering;
+import com.cloud.server.ResourceTag;
+import com.cloud.user.AccountManager;
 import com.cloud.utils.db.Attribute;
 import com.cloud.utils.db.GenericDaoBase;
 import com.cloud.utils.db.SearchBuilder;
 import com.cloud.utils.db.SearchCriteria;
 
-import javax.inject.Inject;
-
 @Component
 public class DiskOfferingJoinDaoImpl extends 
GenericDaoBase<DiskOfferingJoinVO, Long> implements DiskOfferingJoinDao {
     public static final Logger s_logger = 
Logger.getLogger(DiskOfferingJoinDaoImpl.class);
diff --git a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java 
b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
index d092ad1..35de68b 100644
--- a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
+++ b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
@@ -125,6 +125,7 @@ import com.cloud.host.dao.HostDao;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
 import com.cloud.hypervisor.HypervisorCapabilitiesVO;
 import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao;
+import com.cloud.offering.DiskOffering;
 import com.cloud.org.Grouping;
 import com.cloud.resource.ResourceState;
 import com.cloud.serializer.GsonHelper;
@@ -315,6 +316,8 @@ public class VolumeApiServiceImpl extends ManagerBase 
implements VolumeApiServic
 
     private static final Set<Volume.State> STATES_VOLUME_CANNOT_BE_DESTROYED = 
new HashSet<>(Arrays.asList(Volume.State.Destroy, Volume.State.Expunging, 
Volume.State.Expunged, Volume.State.Allocated));
 
+    private static final String CUSTOM_DISK_OFFERING_UNIQUE_NAME = 
"Cloud.com-Custom";
+
     protected VolumeApiServiceImpl() {
         _volStateMachine = Volume.State.getStateMachine();
         _gson = GsonHelper.getGsonLogger();
@@ -478,7 +481,6 @@ public class VolumeApiServiceImpl extends ManagerBase 
implements VolumeApiServic
             if (!diskOffering.isCustomized()) {
                 throw new InvalidParameterValueException("Please specify a 
custom sized disk offering.");
             }
-
             _configMgr.checkDiskOfferingAccess(volumeOwner, diskOffering, 
zone);
         }
 
@@ -489,12 +491,41 @@ public class VolumeApiServiceImpl extends ManagerBase 
implements VolumeApiServic
         return UUID.randomUUID().toString();
     }
 
+    private Long getDefaultCustomOfferingId(Account owner, DataCenter zone) {
+        DiskOfferingVO diskOfferingVO = 
_diskOfferingDao.findByUniqueName(CUSTOM_DISK_OFFERING_UNIQUE_NAME);
+        if (diskOfferingVO == null || 
!DiskOffering.State.Active.equals(diskOfferingVO.getState())) {
+            return null;
+        }
+        try {
+            _configMgr.checkDiskOfferingAccess(owner, diskOfferingVO, zone);
+            return diskOfferingVO.getId();
+        } catch (PermissionDeniedException ignored) {
+        }
+        return null;
+    }
+
+    private Long getCustomDiskOfferingIdForVolumeUpload(Account owner, 
DataCenter zone) {
+        Long offeringId = getDefaultCustomOfferingId(owner, zone);
+        if (offeringId != null) {
+            return offeringId;
+        }
+        List<DiskOfferingVO> offerings = 
_diskOfferingDao.findCustomDiskOfferings();
+        for (DiskOfferingVO offering : offerings) {
+            try {
+                _configMgr.checkDiskOfferingAccess(owner, offering, zone);
+                return offering.getId();
+            } catch (PermissionDeniedException ignored) {}
+        }
+        return null;
+    }
+
     @DB
     protected VolumeVO persistVolume(final Account owner, final Long zoneId, 
final String volumeName, final String url, final String format, final Long 
diskOfferingId, final Volume.State state) {
-        return Transaction.execute(new TransactionCallback<VolumeVO>() {
+        return Transaction.execute(new 
TransactionCallbackWithException<VolumeVO, CloudRuntimeException>() {
             @Override
             public VolumeVO doInTransaction(TransactionStatus status) {
                 VolumeVO volume = new VolumeVO(volumeName, zoneId, -1, -1, -1, 
new Long(-1), null, null, Storage.ProvisioningType.THIN, 0, 
Volume.Type.DATADISK);
+                DataCenter zone = _dcDao.findById(zoneId);
                 volume.setPoolId(null);
                 volume.setDataCenterId(zoneId);
                 volume.setPodId(null);
@@ -504,23 +535,22 @@ public class VolumeApiServiceImpl extends ManagerBase 
implements VolumeApiServic
                 volume.setAccountId((owner == null) ? 
Account.ACCOUNT_ID_SYSTEM : owner.getAccountId());
                 volume.setDomainId((owner == null) ? Domain.ROOT_DOMAIN : 
owner.getDomainId());
 
-                if (diskOfferingId == null) {
-                    DiskOfferingVO diskOfferingVO = 
_diskOfferingDao.findByUniqueName("Cloud.com-Custom");
-                    if (diskOfferingVO != null) {
-                        long defaultDiskOfferingId = diskOfferingVO.getId();
-                        volume.setDiskOfferingId(defaultDiskOfferingId);
+                Long volumeDiskOfferingId = diskOfferingId;
+                if (volumeDiskOfferingId == null) {
+                    volumeDiskOfferingId = 
getCustomDiskOfferingIdForVolumeUpload(owner, zone);
+                    if (volumeDiskOfferingId == null) {
+                        throw new CloudRuntimeException(String.format("Unable 
to find custom disk offering in zone: %s for volume upload", zone.getUuid()));
                     }
-                } else {
-                    volume.setDiskOfferingId(diskOfferingId);
+                }
 
-                    DiskOfferingVO diskOfferingVO = 
_diskOfferingDao.findById(diskOfferingId);
+                volume.setDiskOfferingId(volumeDiskOfferingId);
+                DiskOfferingVO diskOfferingVO = 
_diskOfferingDao.findById(volumeDiskOfferingId);
 
-                    Boolean isCustomizedIops = diskOfferingVO != null && 
diskOfferingVO.isCustomizedIops() != null ? diskOfferingVO.isCustomizedIops() : 
false;
+                Boolean isCustomizedIops = diskOfferingVO != null && 
diskOfferingVO.isCustomizedIops() != null ? diskOfferingVO.isCustomizedIops() : 
false;
 
-                    if (isCustomizedIops == null || !isCustomizedIops) {
-                        volume.setMinIops(diskOfferingVO.getMinIops());
-                        volume.setMaxIops(diskOfferingVO.getMaxIops());
-                    }
+                if (isCustomizedIops == null || !isCustomizedIops) {
+                    volume.setMinIops(diskOfferingVO.getMinIops());
+                    volume.setMaxIops(diskOfferingVO.getMaxIops());
                 }
 
                 // volume.setSize(size);
diff --git a/ui/src/views/storage/UploadLocalVolume.vue 
b/ui/src/views/storage/UploadLocalVolume.vue
index c4f1d56..0e5df83 100644
--- a/ui/src/views/storage/UploadLocalVolume.vue
+++ b/ui/src/views/storage/UploadLocalVolume.vue
@@ -69,7 +69,8 @@
             optionFilterProp="children"
             :filterOption="(input, option) => {
               return 
option.componentOptions.propsData.label.toLowerCase().indexOf(input.toLowerCase())
 >= 0
-            }" >
+            }"
+            @change="onZoneChange" >
             <a-select-option :value="zone.id" v-for="zone in zones" 
:key="zone.id" :label="zone.name || zone.description">
               <span>
                 <resource-icon v-if="zone.icon" :image="zone.icon.base64image" 
size="1x" style="margin-right: 5px"/>
@@ -80,6 +81,22 @@
           </a-select>
         </a-form-item>
         <a-form-item>
+          <tooltip-label slot="label" :title="$t('label.diskofferingid')" 
:tooltip="apiParams.diskofferingid.description"/>
+          <a-select
+            v-decorator="['diskofferingid', {}]"
+            :loading="offeringLoading"
+            :placeholder="apiParams.diskofferingid.description"
+            showSearch
+            optionFilterProp="children"
+            :filterOption="(input, option) => {
+              return 
option.componentOptions.propsData.label.toLowerCase().indexOf(input.toLowerCase())
 >= 0
+            }" >
+            <a-select-option v-for="opt in offerings" :key="opt.id">
+              {{ opt.name || opt.displaytext }}
+            </a-select-option>
+          </a-select>
+        </a-form-item>
+        <a-form-item>
           <tooltip-label slot="label" :title="$t('label.format')" 
:tooltip="apiParams.format.description"/>
           <a-select
             v-decorator="['format', {
@@ -133,6 +150,8 @@ export default {
     return {
       fileList: [],
       zones: [],
+      offerings: [],
+      offeringLoading: false,
       formats: ['RAW', 'VHD', 'VHDX', 'OVA', 'QCOW2'],
       zoneSelected: '',
       uploadParams: null,
@@ -153,11 +172,34 @@ export default {
         if (json && json.listzonesresponse && json.listzonesresponse.zone) {
           this.zones = json.listzonesresponse.zone
           if (this.zones.length > 0) {
-            this.zoneSelected = this.zones[0].id
+            this.onZoneChange(this.zones[0].id)
           }
         }
       })
     },
+    onZoneChange (zoneId) {
+      this.zoneSelected = this.zones[0].id
+      this.fetchDiskOfferings(zoneId)
+    },
+    fetchDiskOfferings (zoneId) {
+      this.offeringLoading = true
+      this.offerings = [{ id: -1, name: '' }]
+      this.form.setFieldsValue({
+        diskofferingid: undefined
+      })
+      api('listDiskOfferings', {
+        zoneid: zoneId,
+        listall: true
+      }).then(json => {
+        for (var offering of json.listdiskofferingsresponse.diskoffering) {
+          if (offering.iscustomized) {
+            this.offerings.push(offering)
+          }
+        }
+      }).finally(() => {
+        this.offeringLoading = false
+      })
+    },
     handleRemove (file) {
       const index = this.fileList.indexOf(file)
       const newFileList = this.fileList.slice()
@@ -188,7 +230,7 @@ export default {
         }
         this.loading = true
         api('getUploadParamsForVolume', params).then(json => {
-          this.uploadParams = (json.postuploadvolumeresponse && 
json.postuploadvolumeresponse.getuploadparams) ? 
json.postuploadvolumeresponse.getuploadparams : ''
+          this.uploadParams = json.postuploadvolumeresponse?.getuploadparams 
|| ''
           const { fileList } = this
           if (this.fileList.length > 1) {
             this.$notification.error({
@@ -224,12 +266,20 @@ export default {
           }).catch(e => {
             this.$notification.error({
               message: this.$t('message.upload.failed'),
-              description: 
`${this.$t('message.upload.iso.failed.description')} -  ${e}`,
+              description: `${this.$t('message.upload.volume.failed')} -  
${e}`,
               duration: 0
             })
           }).finally(() => {
             this.loading = false
           })
+        }).catch(e => {
+          this.$notification.error({
+            message: this.$t('message.upload.failed'),
+            description: `${this.$t('message.upload.volume.failed')} -  
${e?.response?.data?.postuploadvolumeresponse?.errortext || e}`,
+            duration: 0
+          })
+        }).finally(() => {
+          this.loading = false
         })
       })
     },

Reply via email to