Skip to content

Commit 63c0eac

Browse files
committed
Improve UI and add checks for force convert to pool parameter
1 parent b1012d2 commit 63c0eac

File tree

4 files changed

+91
-14
lines changed

4 files changed

+91
-14
lines changed

server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,10 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
206206
private static final List<Hypervisor.HypervisorType> importUnmanagedInstancesSupportedHypervisors =
207207
Arrays.asList(Hypervisor.HypervisorType.VMware, Hypervisor.HypervisorType.KVM);
208208

209+
private static final List<Storage.StoragePoolType> forceConvertToPoolAllowedTypes =
210+
Arrays.asList(Storage.StoragePoolType.NetworkFilesystem, Storage.StoragePoolType.Filesystem,
211+
Storage.StoragePoolType.SharedMountPoint);
212+
209213
@Inject
210214
private AgentManager agentManager;
211215
@Inject
@@ -1655,6 +1659,8 @@ protected UserVm importUnmanagedInstanceFromVmwareToKvm(DataCenter zone, Cluster
16551659
"Please set all the information for a vCenter IP/Name, datacenter, username and password");
16561660
}
16571661

1662+
checkConversionStoragePool(convertStoragePoolId, forceConvertToPool);
1663+
16581664
checkExtraParamsAllowed(extraParams);
16591665

16601666
if (existingVcenterId != null) {
@@ -1752,6 +1758,30 @@ protected UserVm importUnmanagedInstanceFromVmwareToKvm(DataCenter zone, Cluster
17521758
}
17531759
}
17541760

1761+
/**
1762+
* Check whether the conversion storage pool exists and is suitable for the conversion or not.
1763+
* Secondary storage is only allowed when forceConvertToPool is false.
1764+
* @param convertStoragePoolId the ID of the storage pool (primary or secondary)
1765+
* @param forceConvertToPool when true, only primary storage pool must be allowed
1766+
* @throws CloudRuntimeException in case these requirements are not met
1767+
*/
1768+
protected void checkConversionStoragePool(Long convertStoragePoolId, boolean forceConvertToPool) {
1769+
if (forceConvertToPool && convertStoragePoolId == null) {
1770+
String msg = "The parameter forceconverttopool is set to true, but a primary storage pool has not been provided for conversion";
1771+
logFailureAndThrowException(msg);
1772+
}
1773+
if (convertStoragePoolId != null) {
1774+
StoragePoolVO selectedStoragePool = primaryDataStoreDao.findById(convertStoragePoolId);
1775+
if (selectedStoragePool == null) {
1776+
logFailureAndThrowException(String.format("Cannot find a storage pool with ID %s", convertStoragePoolId));
1777+
}
1778+
if (forceConvertToPool && !forceConvertToPoolAllowedTypes.contains(selectedStoragePool.getPoolType())) {
1779+
logFailureAndThrowException(String.format("The selected storage pool %s does not support direct conversion " +
1780+
"as its type %s", selectedStoragePool.getName(), selectedStoragePool.getPoolType().name()));
1781+
}
1782+
}
1783+
}
1784+
17551785
private void checkNetworkingBeforeConvertingVmwareInstance(DataCenter zone, Account owner, String displayName,
17561786
String hostName, UnmanagedInstanceTO sourceVMwareInstance,
17571787
Map<String, Long> nicNetworkMap,

server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1151,4 +1151,41 @@ public void testCheckUnmanagedDiskLimits() {
11511151
Assert.fail("Exception encountered: " + e.getMessage());
11521152
}
11531153
}
1154+
1155+
@Test
1156+
public void testCheckConversionStoragePoolSecondaryStorageStaging() {
1157+
unmanagedVMsManager.checkConversionStoragePool(null, false);
1158+
Mockito.verifyNoInteractions(primaryDataStoreDao);
1159+
}
1160+
1161+
@Test(expected = CloudRuntimeException.class)
1162+
public void testCheckConversionStoragePoolTemporarySecondaryStorageForceConvertToPool() {
1163+
unmanagedVMsManager.checkConversionStoragePool(null, true);
1164+
}
1165+
1166+
@Test
1167+
public void testCheckConversionStoragePoolPrimaryStagingPool() {
1168+
StoragePoolVO destPool = mock(StoragePoolVO.class);
1169+
long destPoolId = 1L;
1170+
Mockito.when(primaryDataStoreDao.findById(destPoolId)).thenReturn(destPool);
1171+
unmanagedVMsManager.checkConversionStoragePool(destPoolId, false);
1172+
}
1173+
1174+
@Test
1175+
public void testCheckConversionStoragePoolPrimaryStagingPoolTypeAllowedForce() {
1176+
StoragePoolVO destPool = mock(StoragePoolVO.class);
1177+
Mockito.when(destPool.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
1178+
long destPoolId = 1L;
1179+
Mockito.when(primaryDataStoreDao.findById(destPoolId)).thenReturn(destPool);
1180+
unmanagedVMsManager.checkConversionStoragePool(destPoolId, true);
1181+
}
1182+
1183+
@Test(expected = CloudRuntimeException.class)
1184+
public void testCheckConversionStoragePoolPrimaryStagingPoolTypeNotAllowedForce() {
1185+
StoragePoolVO destPool = mock(StoragePoolVO.class);
1186+
Mockito.when(destPool.getPoolType()).thenReturn(Storage.StoragePoolType.RBD);
1187+
long destPoolId = 1L;
1188+
Mockito.when(primaryDataStoreDao.findById(destPoolId)).thenReturn(destPool);
1189+
unmanagedVMsManager.checkConversionStoragePool(destPoolId, true);
1190+
}
11541191
}

ui/public/locales/en.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1083,6 +1083,7 @@
10831083
"label.forcks": "For CKS",
10841084
"label.forbidden": "Forbidden",
10851085
"label.forced": "Force",
1086+
"label.force.convert.to.pool": "Force converting to storage pool directly (not using temporary storage for conversion)",
10861087
"label.force.ms.to.import.vm.files": "Enable to force OVF Download via Management Server. Disable to use KVM Host ovftool (if installed)",
10871088
"label.force.update.os.type": "Force update OS type",
10881089
"label.force.stop": "Force stop",
@@ -3718,6 +3719,7 @@
37183719
"message.select.deselect.desired.options": "Please select / deselect the desired options",
37193720
"message.select.deselect.to.sort": "Please select / deselect to sort the values",
37203721
"message.select.destination.image.stores": "Please select Image Store(s) to which data is to be migrated to",
3722+
"message.select.destination.storage.instance.conversion": "(Optional) Select a Primary Storage destination for the converted disks",
37213723
"message.select.disk.offering": "Please select a disk offering for disk",
37223724
"message.select.end.date.and.time": "Select an end date & time.",
37233725
"message.select.extra.parameters.for.instance.conversion": "(Optional) Pass extra parameters to the virt-v2v command on the conversion host",
@@ -3728,7 +3730,7 @@
37283730
"message.select.nic.network": "Please select a Network for NIC",
37293731
"message.select.security.groups": "Please select security group(s) for your new Instance.",
37303732
"message.select.start.date.and.time": "Select a start date & time.",
3731-
"message.select.temporary.storage.instance.conversion": "(Optional) Select a Storage temporary destination for the converted disks through virt-v2v",
3733+
"message.select.temporary.storage.instance.conversion": "(Optional) Select a staging storage for the converted disks",
37323734
"message.select.zone.description": "Select type of Zone basic/advanced.",
37333735
"message.select.zone.hint": "This is the type of Zone deployment that you want to use. Basic zone: provides a single Network where each 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). 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.",
37343736
"message.server": "Server : ",

ui/src/views/tools/ImportUnmanagedInstance.vue

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,9 @@
154154
</a-form-item>
155155
<a-form-item name="forceconverttopool" ref="forceconverttopool" v-if="selectedVmwareVcenter">
156156
<template #label>
157-
<tooltip-label :title="'Force converting to storage pool directly (not using temporary storage for conversion)'" :tooltip="apiParams.forcemstoimportvmfiles.description"/>
157+
<tooltip-label :title="$t('label.force.convert.to.pool')" :tooltip="apiParams.forceconverttopool.description"/>
158158
</template>
159-
<a-switch v-model:checked="form.forceconverttopool" @change="val => { switches.forceConvertToPool = val }" />
159+
<a-switch v-model:checked="form.forceconverttopool" @change="onForceConvertToPoolChange" />
160160
</a-form-item>
161161
<a-form-item name="converthostid" ref="converthostid">
162162
<check-box-select-pair
@@ -188,13 +188,18 @@
188188
v-if="cluster.hypervisortype === 'KVM' && selectedVmwareVcenter"
189189
:resourceKey="cluster.id"
190190
:selectOptions="storageOptionsForConversion"
191-
:checkBoxLabel="$t('message.select.temporary.storage.instance.conversion')"
191+
:checkBoxLabel="switches.forceConvertToPool ? $t('message.select.destination.storage.instance.conversion') : $t('message.select.temporary.storage.instance.conversion')"
192192
:defaultCheckBoxValue="false"
193193
:reversed="false"
194194
@handle-checkselectpair-change="updateSelectedStorageOptionForConversion"
195195
/>
196196
</a-form-item>
197-
<a-form-item v-if="showStoragePoolsForConversion" name="convertstoragepool" ref="convertstoragepool" :label="$t('label.storagepool')">
197+
<a-form-item
198+
v-if="showStoragePoolsForConversion"
199+
name="convertstoragepool"
200+
ref="convertstoragepool"
201+
:label="$t('label.storagepool')"
202+
>
198203
<a-select
199204
v-model:value="form.convertstoragepoolid"
200205
defaultActiveFirstOption
@@ -1062,20 +1067,23 @@ export default {
10621067
}
10631068
},
10641069
resetStorageOptionsForConversion () {
1065-
this.storageOptionsForConversion = [
1066-
{
1067-
id: 'secondary',
1068-
name: 'Secondary Storage'
1069-
}, {
1070-
id: 'primary',
1071-
name: 'Primary Storage'
1072-
}
1073-
]
1070+
this.storageOptionsForConversion = this.switches.forceConvertToPool ? [] : [{
1071+
id: 'secondary',
1072+
name: 'Secondary Storage'
1073+
}]
1074+
this.storageOptionsForConversion.push({
1075+
id: 'primary',
1076+
name: 'Primary Storage'
1077+
})
10741078
},
10751079
onSelectRootDisk (val) {
10761080
this.selectedRootDiskIndex = val
10771081
this.updateSelectedRootDisk()
10781082
},
1083+
onForceConvertToPoolChange (val) {
1084+
this.switches.forceConvertToPool = val
1085+
this.resetStorageOptionsForConversion()
1086+
},
10791087
updateSelectedRootDisk () {
10801088
var rootDisk = this.resource.disk[this.selectedRootDiskIndex]
10811089
rootDisk.size = rootDisk.capacity / (1024 * 1024 * 1024)

0 commit comments

Comments
 (0)