mirror of
https://github.com/apache/cloudstack.git
synced 2025-11-02 11:52:28 +01:00
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 <abhishek.mrt22@gmail.com>
This commit is contained in:
parent
ddd311c695
commit
8adb8df2fe
@ -34,4 +34,6 @@ public interface DiskOfferingDao extends GenericDao<DiskOfferingVO, Long> {
|
||||
|
||||
List<DiskOfferingVO> listAllBySizeAndProvisioningType(long size, Storage.ProvisioningType provisioningType);
|
||||
|
||||
List<DiskOfferingVO> findCustomDiskOfferings();
|
||||
|
||||
}
|
||||
|
||||
@ -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;
|
||||
@ -173,6 +174,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();
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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"/>
|
||||
@ -79,6 +80,22 @@
|
||||
</a-select-option>
|
||||
</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
|
||||
@ -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
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user