Skip to content

Commit b420f90

Browse files
committed
review comments
1 parent 3dbfbf7 commit b420f90

4 files changed

Lines changed: 91 additions & 15 deletions

File tree

api/src/main/java/org/apache/cloudstack/api/command/user/volume/CreateVolumeCmd.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ public class CreateVolumeCmd extends BaseAsyncCreateCustomIdCmd implements UserC
113113
@Parameter(name = ApiConstants.STORAGE_ID,
114114
type = CommandType.UUID,
115115
entityType = StoragePoolResponse.class,
116-
description = "Storage pool ID to create the volume in. Exclusive with SnapshotId parameter.",
116+
description = "Storage pool ID to create the volume in. Cannot be used with the snapshotid parameter.",
117117
authorized = {RoleType.Admin})
118118
private Long storageId;
119119

server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1045,32 +1045,32 @@ public boolean validateVolumeSizeInBytes(long size) {
10451045

10461046
private VolumeVO createVolumeOnStoragePool(Long volumeId, Long storageId) throws ExecutionException, InterruptedException {
10471047
VolumeVO volume = _volsDao.findById(volumeId);
1048-
StoragePool destPool = (StoragePool) dataStoreMgr.getDataStore(storageId, DataStoreRole.Primary);
1049-
if (destPool == null) {
1050-
throw new InvalidParameterValueException("Failed to find the destination storage pool: " + storageId);
1051-
} else if (destPool.isInMaintenance()) {
1052-
throw new InvalidParameterValueException(String.format("Cannot create volume %s on storage pool %s as the storage pool is in maintenance mode.",
1053-
volume.getUuid(), destPool.getName()));
1048+
StoragePool storagePool = (StoragePool) dataStoreMgr.getDataStore(storageId, DataStoreRole.Primary);
1049+
if (storagePool == null) {
1050+
throw new InvalidParameterValueException("Failed to find the storage pool: " + storageId);
1051+
} else if (!storagePool.getStatus().equals(StoragePoolStatus.Up)) {
1052+
throw new InvalidParameterValueException(String.format("Cannot create volume %s on storage pool %s as the storage pool is not in Up state.",
1053+
volume.getUuid(), storagePool.getName()));
10541054
}
10551055

1056-
if (destPool.getDataCenterId() != volume.getDataCenterId()) {
1056+
if (storagePool.getDataCenterId() != volume.getDataCenterId()) {
10571057
throw new InvalidParameterValueException(String.format("Cannot create volume %s in zone %s on storage pool %s in zone %s.",
1058-
volume.getUuid(), volume.getDataCenterId(), destPool.getUuid(), destPool.getDataCenterId()));
1058+
volume.getUuid(), volume.getDataCenterId(), storagePool.getUuid(), storagePool.getDataCenterId()));
10591059
}
10601060

10611061
DiskOfferingVO diskOffering = _diskOfferingDao.findById(volume.getDiskOfferingId());
1062-
if (!doesStoragePoolSupportDiskOffering(destPool, diskOffering)) {
1062+
if (!doesStoragePoolSupportDiskOffering(storagePool, diskOffering)) {
10631063
throw new InvalidParameterValueException(String.format("Disk offering: %s is not compatible with the storage pool", diskOffering.getUuid()));
10641064
}
10651065

1066-
DataStore destStore = dataStoreMgr.getDataStore(storageId, DataStoreRole.Primary);
1067-
VolumeInfo destVolume = volFactory.getVolume(volumeId, destStore);
1068-
AsyncCallFuture<VolumeApiResult> createVolumeFuture = volService.createVolumeAsync(destVolume, destStore);
1066+
DataStore dataStore = dataStoreMgr.getDataStore(storageId, DataStoreRole.Primary);
1067+
VolumeInfo volumeInfo = volFactory.getVolume(volumeId, dataStore);
1068+
AsyncCallFuture<VolumeApiResult> createVolumeFuture = volService.createVolumeAsync(volumeInfo, dataStore);
10691069
VolumeApiResult createVolumeResult = createVolumeFuture.get();
10701070
if (createVolumeResult.isFailed()) {
1071-
throw new CloudRuntimeException("Creation of a dest volume failed: " + createVolumeResult.getResult());
1071+
throw new CloudRuntimeException("Volume creation on storage failed: " + createVolumeResult.getResult());
10721072
}
1073-
return _volsDao.findById(destVolume.getId());
1073+
return _volsDao.findById(volumeInfo.getId());
10741074
}
10751075

10761076
@Override

ui/public/locales/en.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,7 @@
681681
"label.create.sharedfs": "Create Shared FileSystem",
682682
"label.create.network": "Create new Network",
683683
"label.create.nfs.secondary.staging.storage": "Create NFS secondary staging storage",
684+
"label.create.on.storage": "Create on Storage",
684685
"label.create.project": "Create Project",
685686
"label.create.project.role": "Create Project Role",
686687
"label.create.routing.policy": "Create Routing Policy",
@@ -697,6 +698,7 @@
697698
"label.create.tier.networkofferingid.description": "The Network offering for the Network Tier.",
698699
"label.create.tungsten.routing.policy": "Create Tungsten-Fabric routing policy",
699700
"label.create.user": "Create User",
701+
"label.create.volume.on.primary.storage": "Create Volume on the specified Primary Storage",
700702
"label.create.vm": "Create Instance",
701703
"label.create.vm.and.stay": "Create Instance & stay on this page",
702704
"label.create.vpn.connection": "Create VPN connection",

ui/src/views/storage/CreateVolume.vue

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,42 @@
116116
:placeholder="apiParams.maxiops.description"/>
117117
</a-form-item>
118118
</span>
119+
<a-form-item name="createOnStorage" ref="createOnStorage" v-if="showStoragePoolSelect">
120+
<template #label>
121+
<tooltip-label :title="$t('label.create.on.storage')" :tooltip="$t('label.create.volume.on.primary.storage')" />
122+
</template>
123+
<a-switch
124+
v-model:checked="form.createOnStorage"
125+
:checked="createOnStorage"
126+
@change="onChangeCreateOnStorage" />
127+
</a-form-item>
128+
<span v-if="showStoragePoolSelect && createOnStorage">
129+
<a-form-item ref="storageid" name="storageid">
130+
<template #label>
131+
<tooltip-label :title="$t('label.storageid')" />
132+
</template>
133+
<a-select
134+
v-model:value="form.storageid"
135+
:loading="loading"
136+
showSearch
137+
optionFilterProp="label"
138+
:filterOption="(input, option) => {
139+
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
140+
}" >
141+
<a-select-option
142+
v-for="(pool, index) in storagePools"
143+
:value="pool.id"
144+
:key="index"
145+
:label="pool.name">
146+
<span>
147+
<resource-icon v-if="pool.icon" :image="pool.icon.base64image" size="1x" style="margin-right: 5px"/>
148+
<hdd-outlined v-else style="margin-right: 5px"/>
149+
{{ pool.name }}
150+
</span>
151+
</a-select-option>
152+
</a-select>
153+
</a-form-item>
154+
</span>
119155
<a-form-item name="attachVolume" ref="attachVolume" v-if="!createVolumeFromVM">
120156
<template #label>
121157
<tooltip-label :title="$t('label.action.attach.to.instance')" :tooltip="$t('label.attach.vol.to.instance')" />
@@ -170,6 +206,7 @@
170206
import { ref, reactive, toRaw } from 'vue'
171207
import { getAPI, postAPI } from '@/api'
172208
import { mixinForm } from '@/utils/mixin'
209+
import { isAdmin } from '@/role'
173210
import ResourceIcon from '@/components/view/ResourceIcon'
174211
import TooltipLabel from '@/components/widgets/TooltipLabel'
175212
import OwnershipSelection from '@/views/compute/wizard/OwnershipSelection.vue'
@@ -203,11 +240,16 @@ export default {
203240
loading: false,
204241
isCustomizedDiskIOps: false,
205242
virtualmachines: [],
243+
createOnStorage: false,
244+
storagePools: [],
206245
attachVolume: false,
207246
vmidtoattach: null
208247
}
209248
},
210249
computed: {
250+
showStoragePoolSelect () {
251+
return isAdmin() && !this.createVolumeFromSnapshot
252+
},
211253
createVolumeFromVM () {
212254
return this.$route.path.startsWith('/vm/')
213255
},
@@ -299,6 +341,9 @@ export default {
299341
this.zones = json.listzonesresponse.zone || []
300342
this.form.zoneid = this.zones[0].id || ''
301343
this.fetchDiskOfferings(this.form.zoneid)
344+
if (this.createOnStorage) {
345+
this.fetchStoragePools(this.form.zoneid)
346+
}
302347
if (this.attachVolume) {
303348
this.fetchVirtualMachines(this.form.zoneid)
304349
}
@@ -355,6 +400,25 @@ export default {
355400
this.loading = false
356401
})
357402
},
403+
fetchStoragePools (zoneId) {
404+
if (!zoneId) {
405+
this.storagePools = []
406+
return
407+
}
408+
this.loading = true
409+
getAPI('listStoragePools', {
410+
zoneid: zoneId,
411+
showicon: true
412+
}).then(json => {
413+
const pools = json.liststoragepoolsresponse.storagepool || []
414+
this.storagePools = pools.filter(p => p.state === 'Up')
415+
}).catch(error => {
416+
this.$notifyError(error)
417+
this.storagePools = []
418+
}).finally(() => {
419+
this.loading = false
420+
})
421+
},
358422
fetchVirtualMachines (zoneId) {
359423
var params = {
360424
zoneid: zoneId,
@@ -394,6 +458,7 @@ export default {
394458
if (this.customDiskOffering) {
395459
values.size = values.size.trim()
396460
}
461+
delete values.createOnStorage
397462
if (this.createVolumeFromSnapshot) {
398463
values.snapshotid = this.resource.id
399464
}
@@ -467,6 +532,15 @@ export default {
467532
this.attachVolumeApiParams = this.$getApiParams('attachVolume')
468533
this.fetchVirtualMachines(this.form.zoneid)
469534
}
535+
},
536+
onChangeCreateOnStorage () {
537+
this.createOnStorage = !this.createOnStorage
538+
if (this.createOnStorage) {
539+
this.fetchStoragePools(this.form.zoneid)
540+
this.form.storageid = this.storagePools[0]?.id || undefined
541+
} else {
542+
this.form.storageid = undefined
543+
}
470544
}
471545
}
472546
}

0 commit comments

Comments
 (0)