Skip to content

Commit 9200dd0

Browse files
committed
Pass extra parameters to virt-v2v if administrator allows via global setting
1 parent f9e9466 commit 9200dd0

File tree

8 files changed

+97
-13
lines changed

8 files changed

+97
-13
lines changed

api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ImportVmCmd.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,12 @@ public class ImportVmCmd extends ImportUnmanagedInstanceCmd {
159159
description = "(only for importing VMs from VMware to KVM) optional - if true, forces MS to export OVF from VMware to temporary storage, else uses KVM Host if ovftool is available, falls back to MS if not.")
160160
private Boolean forceMsToImportVmFiles;
161161

162+
@Parameter(name = "extraparams",
163+
type = CommandType.STRING,
164+
since = "4.22",
165+
description = "(VMware to KVM only) extra parameters to be passed on the virt-v2v command, if allowed by the administrator")
166+
private String extraParams;
167+
162168
/////////////////////////////////////////////////////
163169
/////////////////// Accessors ///////////////////////
164170
/////////////////////////////////////////////////////
@@ -248,6 +254,10 @@ public String getEventType() {
248254
return EventTypes.EVENT_VM_IMPORT;
249255
}
250256

257+
public String getExtraParams() {
258+
return extraParams;
259+
}
260+
251261
@Override
252262
public String getEventDescription() {
253263
String vmName = getName();

api/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManager.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,15 @@ public interface UnmanagedVMsManager extends VmImportService, UnmanageVMService,
7070
ConfigKey.Scope.Global,
7171
null);
7272

73+
ConfigKey<Boolean> ConvertVmwareInstanceToKvmExtraParamsAllowed = new ConfigKey<>(Boolean.class,
74+
"convert.vmware.instance.to.kvm.extra.params.allowed",
75+
"Advanced",
76+
"false",
77+
"Disabled by default. If enabled, allows extra parameters to be passed to the virt-v2v binary on KVM conversion hosts",
78+
true,
79+
ConfigKey.Scope.Global,
80+
null);
81+
7382
static boolean isSupported(Hypervisor.HypervisorType hypervisorType) {
7483
return hypervisorType == VMware || hypervisorType == KVM;
7584
}

core/src/main/java/com/cloud/agent/api/ConvertInstanceCommand.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ public class ConvertInstanceCommand extends Command {
2929
private boolean checkConversionSupport;
3030
private boolean exportOvfToConversionLocation;
3131
private int threadsCountToExportOvf = 0;
32+
private String extraParams;
3233

3334
public ConvertInstanceCommand() {
3435
}
@@ -75,6 +76,14 @@ public void setThreadsCountToExportOvf(int threadsCountToExportOvf) {
7576
this.threadsCountToExportOvf = threadsCountToExportOvf;
7677
}
7778

79+
public String getExtraParams() {
80+
return extraParams;
81+
}
82+
83+
public void setExtraParams(String extraParams) {
84+
this.extraParams = extraParams;
85+
}
86+
7887
@Override
7988
public boolean executeInSequence() {
8089
return false;

plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapper.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ public Answer execute(ConvertInstanceCommand cmd, LibvirtComputingResource serve
5757
Hypervisor.HypervisorType destinationHypervisorType = cmd.getDestinationHypervisorType();
5858
DataStoreTO conversionTemporaryLocation = cmd.getConversionTemporaryLocation();
5959
long timeout = (long) cmd.getWait() * 1000;
60+
String extraParams = cmd.getExtraParams();
6061

6162
if (cmd.getCheckConversionSupport() && !serverResource.hostSupportsInstanceConversion()) {
6263
String msg = String.format("Cannot convert the instance %s from VMware as the virt-v2v binary is not found. " +
@@ -117,7 +118,7 @@ public Answer execute(ConvertInstanceCommand cmd, LibvirtComputingResource serve
117118
boolean cleanupSecondaryStorage = false;
118119
try {
119120
boolean result = performInstanceConversion(sourceInstanceName, sourceOVFDirPath, temporaryConvertPath, temporaryConvertUuid,
120-
timeout, verboseModeEnabled);
121+
timeout, verboseModeEnabled, extraParams);
121122
if (!result) {
122123
String err = String.format(
123124
"(%s) The virt-v2v conversion for the OVF %s failed. Please check the agent logs " +
@@ -220,7 +221,7 @@ private boolean exportOVAFromVMOnVcenter(String vmExportUrl,
220221
protected boolean performInstanceConversion(String sourceInstanceName, String sourceOVFDirPath,
221222
String temporaryConvertFolder,
222223
String temporaryConvertUuid,
223-
long timeout, boolean verboseModeEnabled) {
224+
long timeout, boolean verboseModeEnabled, String extraParams) {
224225
Script script = new Script("virt-v2v", timeout, logger);
225226
script.add("--root", "first");
226227
script.add("-i", "ova");
@@ -232,6 +233,9 @@ protected boolean performInstanceConversion(String sourceInstanceName, String so
232233
if (verboseModeEnabled) {
233234
script.add("-v");
234235
}
236+
if (StringUtils.isNotBlank(extraParams)) {
237+
script.add(extraParams);
238+
}
235239

236240
String logPrefix = String.format("(%s) virt-v2v ovf source: %s progress", sourceInstanceName, sourceOVFDirPath);
237241
OutputInterpreter.LineByLineOutputLogger outputLogger = new OutputInterpreter.LineByLineOutputLogger(logger, logPrefix);

plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapperTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ public void testExecuteConvertFailure() {
167167
Answer answer = convertInstanceCommandWrapper.execute(cmd, libvirtComputingResourceMock);
168168
Assert.assertFalse(answer.getResult());
169169
Mockito.verify(convertInstanceCommandWrapper).performInstanceConversion(Mockito.anyString(), Mockito.anyString(),
170-
Mockito.anyString(), Mockito.anyString(), Mockito.anyLong(), Mockito.anyBoolean());
170+
Mockito.anyString(), Mockito.anyString(), Mockito.anyLong(), Mockito.anyBoolean(), null);
171171
}
172172
}
173173
}

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

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@
179179
import org.apache.cloudstack.utils.volume.VirtualMachineDiskInfo;
180180
import org.apache.commons.collections.CollectionUtils;
181181
import org.apache.commons.collections.MapUtils;
182+
import org.apache.commons.lang3.BooleanUtils;
182183
import org.apache.commons.lang3.StringUtils;
183184
import org.apache.logging.log4j.LogManager;
184185
import org.apache.logging.log4j.Logger;
@@ -1406,6 +1407,7 @@ private UserVmResponse baseImportInstance(ImportUnmanagedInstanceCmd cmd) {
14061407
throw new CloudRuntimeException("Please provide an import source for importing the VM");
14071408
}
14081409
String source = importVmCmd.getImportSource().toUpperCase();
1410+
String extraParams = ((ImportVmCmd) cmd).getExtraParams();
14091411
ImportSource importSource = Enum.valueOf(ImportSource.class, source);
14101412
if (ImportSource.VMWARE == importSource) {
14111413
userVm = importUnmanagedInstanceFromVmwareToKvm(zone, cluster,
@@ -1434,6 +1436,16 @@ private UserVmResponse baseImportInstance(ImportUnmanagedInstanceCmd cmd) {
14341436
return responseGenerator.createUserVmResponse(ResponseObject.ResponseView.Full, "virtualmachine", userVm).get(0);
14351437
}
14361438

1439+
private void checkExtraParamsAllowed(String extraParams) {
1440+
if (StringUtils.isBlank(extraParams)) {
1441+
return;
1442+
}
1443+
if (BooleanUtils.isFalse(ConvertVmwareInstanceToKvmExtraParamsAllowed.value())) {
1444+
throw new ServerApiException(ApiErrorCode.PARAM_ERROR,
1445+
"Extra parameters for Vmware to KVM conversion are disabled by the administrator");
1446+
}
1447+
}
1448+
14371449
private long getUserIdForImportInstance(Account owner) {
14381450
long userId = CallContext.current().getCallingUserId();
14391451
List<UserVO> userVOs = userDao.listByAccount(owner.getAccountId());
@@ -1529,6 +1541,7 @@ public UserVmResponse importVm(ImportVmCmd cmd) {
15291541
String source = cmd.getImportSource().toUpperCase();
15301542
ImportSource importSource = Enum.valueOf(ImportSource.class, source);
15311543
if (ImportSource.VMWARE == importSource || ImportSource.UNMANAGED == importSource) {
1544+
checkExtraParamsAllowed(cmd.getExtraParams());
15321545
return baseImportInstance(cmd);
15331546
} else {
15341547
return importKvmInstance(cmd);
@@ -1689,6 +1702,7 @@ protected UserVm importUnmanagedInstanceFromVmwareToKvm(DataCenter zone, Cluster
16891702
Long convertInstanceHostId = cmd.getConvertInstanceHostId();
16901703
Long importInstanceHostId = cmd.getImportInstanceHostId();
16911704
Long convertStoragePoolId = cmd.getConvertStoragePoolId();
1705+
String extraParams = cmd.getExtraParams();
16921706

16931707
if ((existingVcenterId == null && vcenter == null) || (existingVcenterId != null && vcenter != null)) {
16941708
throw new ServerApiException(ApiErrorCode.PARAM_ERROR,
@@ -1699,6 +1713,8 @@ protected UserVm importUnmanagedInstanceFromVmwareToKvm(DataCenter zone, Cluster
16991713
"Please set all the information for a vCenter IP/Name, datacenter, username and password");
17001714
}
17011715

1716+
checkExtraParamsAllowed(extraParams);
1717+
17021718
if (existingVcenterId != null) {
17031719
VmwareDatacenterVO existingDC = vmwareDatacenterDao.findById(existingVcenterId);
17041720
if (existingDC == null) {
@@ -1752,14 +1768,14 @@ protected UserVm importUnmanagedInstanceFromVmwareToKvm(DataCenter zone, Cluster
17521768
convertedInstance = convertVmwareInstanceToKVMWithOVFOnConvertLocation(sourceVMName,
17531769
sourceVMwareInstance, convertHost, importHost, convertStoragePools,
17541770
serviceOffering, dataDiskOfferingMap, temporaryConvertLocation,
1755-
ovfTemplateOnConvertLocation);
1771+
ovfTemplateOnConvertLocation, extraParams);
17561772
} else {
17571773
// Uses KVM Host for OVF export to temporary conversion location, through ovftool
17581774
updateImportVMTaskStep(importVMTaskVO, zone, owner, convertHost, importHost, ConvertingInstance);
17591775
convertedInstance = convertVmwareInstanceToKVMAfterExportingOVFToConvertLocation(
17601776
sourceVMName, sourceVMwareInstance, convertHost, importHost,
17611777
convertStoragePools, serviceOffering, dataDiskOfferingMap,
1762-
temporaryConvertLocation, vcenter, username, password, datacenterName);
1778+
temporaryConvertLocation, vcenter, username, password, datacenterName, extraParams);
17631779
}
17641780

17651781
sanitizeConvertedInstance(convertedInstance, sourceVMwareInstance);
@@ -2041,15 +2057,17 @@ private UnmanagedInstanceTO convertVmwareInstanceToKVMWithOVFOnConvertLocation(
20412057
String sourceVM, UnmanagedInstanceTO sourceVMwareInstance, HostVO convertHost,
20422058
HostVO importHost, List<StoragePoolVO> convertStoragePools,
20432059
ServiceOfferingVO serviceOffering, Map<String, Long> dataDiskOfferingMap,
2044-
DataStoreTO temporaryConvertLocation, String ovfTemplateDirConvertLocation
2045-
) {
2060+
DataStoreTO temporaryConvertLocation, String ovfTemplateDirConvertLocation, String extraParams) {
20462061
logger.debug("Delegating the conversion of instance {} from VMware to KVM to the host {} using OVF {} on conversion datastore",
20472062
sourceVM, convertHost, ovfTemplateDirConvertLocation);
20482063

20492064
RemoteInstanceTO remoteInstanceTO = new RemoteInstanceTO(sourceVM);
20502065
List<String> destinationStoragePools = selectInstanceConversionStoragePools(convertStoragePools, sourceVMwareInstance.getDisks(), serviceOffering, dataDiskOfferingMap);
20512066
ConvertInstanceCommand cmd = new ConvertInstanceCommand(remoteInstanceTO,
20522067
Hypervisor.HypervisorType.KVM, temporaryConvertLocation, ovfTemplateDirConvertLocation, false, false);
2068+
if (StringUtils.isNotBlank(extraParams)) {
2069+
cmd.setExtraParams(extraParams);
2070+
}
20532071
int timeoutSeconds = UnmanagedVMsManager.ConvertVmwareInstanceToKvmTimeout.value() * 60 * 60;
20542072
cmd.setWait(timeoutSeconds);
20552073

@@ -2062,8 +2080,7 @@ private UnmanagedInstanceTO convertVmwareInstanceToKVMAfterExportingOVFToConvert
20622080
HostVO importHost, List<StoragePoolVO> convertStoragePools,
20632081
ServiceOfferingVO serviceOffering, Map<String, Long> dataDiskOfferingMap,
20642082
DataStoreTO temporaryConvertLocation, String vcenterHost, String vcenterUsername,
2065-
String vcenterPassword, String datacenterName
2066-
) {
2083+
String vcenterPassword, String datacenterName, String extraParams) {
20672084
logger.debug("Delegating the conversion of instance {} from VMware to KVM to the host {} after OVF export through ovftool", sourceVM, convertHost);
20682085

20692086
RemoteInstanceTO remoteInstanceTO = new RemoteInstanceTO(sourceVMwareInstance.getName(), sourceVMwareInstance.getPath(), vcenterHost, vcenterUsername, vcenterPassword, datacenterName);
@@ -2078,7 +2095,9 @@ private UnmanagedInstanceTO convertVmwareInstanceToKVMAfterExportingOVFToConvert
20782095
noOfThreads = sourceVMwareInstance.getDisks().size();
20792096
}
20802097
cmd.setThreadsCountToExportOvf(noOfThreads);
2081-
2098+
if (StringUtils.isNotBlank(extraParams)) {
2099+
cmd.setExtraParams(extraParams);
2100+
}
20822101
return convertAndImportToKVM(cmd, convertHost, importHost, sourceVM,
20832102
remoteInstanceTO, destinationStoragePools, temporaryConvertLocation);
20842103
}
@@ -3064,7 +3083,8 @@ public ConfigKey<?>[] getConfigKeys() {
30643083
RemoteKvmInstanceDisksCopyTimeout,
30653084
ConvertVmwareInstanceToKvmTimeout,
30663085
ThreadsOnMSToImportVMwareVMFiles,
3067-
ThreadsOnKVMHostToImportVMwareVMFiles
3086+
ThreadsOnKVMHostToImportVMwareVMFiles,
3087+
ConvertVmwareInstanceToKvmExtraParamsAllowed
30683088
};
30693089
}
30703090
}

ui/public/locales/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3696,6 +3696,7 @@
36963696
"message.select.destination.image.stores": "Please select Image Store(s) to which data is to be migrated to",
36973697
"message.select.disk.offering": "Please select a disk offering for disk",
36983698
"message.select.end.date.and.time": "Select an end date & time.",
3699+
"message.select.extra.parameters.for.instance.conversion": "(Optional) Pass extra parameters to the virt-v2v command on the conversion host",
36993700
"message.select.kvm.host.instance.conversion": "(Optional) Select a KVM host in the Zone to perform the instance conversion through virt-v2v",
37003701
"message.select.kvm.host.instance.import": "(Optional) Select a KVM host in the Cluster to perform the importing of the converted instance",
37013702
"message.select.load.balancer.rule": "Please select a load balancer rule for your AutoScale Instance group.",

ui/src/views/tools/ImportUnmanagedInstance.vue

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,6 @@
179179
<a-form-item name="convertstorageoption" ref="convertstorageoption">
180180
<check-box-select-pair
181181
layout="vertical"
182-
style="margin-bottom: 5px"
183182
v-if="cluster.hypervisortype === 'KVM' && selectedVmwareVcenter"
184183
:resourceKey="cluster.id"
185184
:selectOptions="storageOptionsForConversion"
@@ -189,6 +188,18 @@
189188
@handle-checkselectpair-change="updateSelectedStorageOptionForConversion"
190189
/>
191190
</a-form-item>
191+
<a-form-item name="extraparams" ref="extraparams">
192+
<a-checkbox
193+
v-if="cluster.hypervisortype === 'KVM' && selectedVmwareVcenter && vmwareToKvmExtraParamsAllowed"
194+
v-model:checked="vmwareToKvmExtraParamsSelected">
195+
{{ $t('message.select.extra.parameters.for.instance.conversion') }}
196+
</a-checkbox>
197+
<a-input
198+
v-if="vmwareToKvmExtraParamsSelected"
199+
v-model:value="vmwareToKvmExtraParams"
200+
:placeholder="$t('label.extra')"
201+
/>
202+
</a-form-item>
192203
<a-form-item v-if="showStoragePoolsForConversion" name="convertstoragepool" ref="convertstoragepool" :label="$t('label.storagepool')">
193204
<a-select
194205
v-model:value="form.convertstoragepoolid"
@@ -529,7 +540,10 @@ export default {
529540
title: this.$t('label.rootdisk')
530541
}
531542
],
532-
selectedRootDiskSources: []
543+
selectedRootDiskSources: [],
544+
vmwareToKvmExtraParamsAllowed: false,
545+
vmwareToKvmExtraParamsSelected: false,
546+
vmwareToKvmExtraParams: ''
533547
}
534548
},
535549
beforeCreate () {
@@ -749,6 +763,20 @@ export default {
749763
if (this.resource?.disk?.length > 1) {
750764
this.updateSelectedRootDisk()
751765
}
766+
this.fetchVmwareToKVMExtraConfigsSetting()
767+
},
768+
fetchVmwareToKVMExtraConfigsSetting () {
769+
const params = {
770+
name: 'convert.vmware.instance.to.kvm.extra.params.allowed'
771+
}
772+
getAPI('listConfigurations', params).then(json => {
773+
if (json.listconfigurationsresponse.configuration !== null) {
774+
const config = json.listconfigurationsresponse.configuration[0]
775+
if (config && config.name === params.name) {
776+
this.vmwareToKvmExtraParamsAllowed = config.value
777+
}
778+
}
779+
})
752780
},
753781
getMeta (obj, metaKeys) {
754782
var meta = []
@@ -1156,6 +1184,9 @@ export default {
11561184
if (this.selectedStoragePoolForConversion) {
11571185
params.convertinstancepoolid = this.selectedStoragePoolForConversion
11581186
}
1187+
if (this.vmwareToKvmExtraParams) {
1188+
params.extraparams = this.vmwareToKvmExtraParams
1189+
}
11591190
params.forcemstoimportvmfiles = values.forcemstoimportvmfiles
11601191
}
11611192
var keys = ['hostname', 'domainid', 'projectid', 'account', 'migrateallowed', 'forced', 'forcemstoimportvmfiles']

0 commit comments

Comments
 (0)