feat: add VMware VDDK image input field to VM migration provider (#1057) (#1060)

* feat: add VMware VDDK image option to VM migration provider

Add an optional VMware VDDK Image field to the Configure Provider step of the VM migration wizard, bound to spec.settings.vddkInitImage when set and left unset when empty. Supports create and edit flows and populates from the existing provider when editing.



* refactor: copilot review



---------


(cherry picked from commit a6bd7ec00e7c8d85f4f097c0dbc61eb9bf5fac56)

Signed-off-by: Andy Lee <andy.lee@suse.com>
Co-authored-by: Andy Lee <andy.lee@suse.com>
This commit is contained in:
mergify[bot] 2026-07-28 14:33:31 +08:00 committed by GitHub
parent f0929100f0
commit a0f21d5902
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 59 additions and 19 deletions

View File

@ -42,6 +42,7 @@ const {
username, username,
password, password,
skipTlsVerify, skipTlsVerify,
vddkInitImage,
testPassed, testPassed,
testResult, testResult,
testError, testError,
@ -97,6 +98,7 @@ watch(selectedProvider, (val) => {
username.value = ''; username.value = '';
password.value = ''; password.value = '';
skipTlsVerify.value = false; skipTlsVerify.value = false;
vddkInitImage.value = '';
createdProvider.value = null; createdProvider.value = null;
createdSecret.value = null; createdSecret.value = null;
} else { } else {
@ -107,6 +109,7 @@ watch(selectedProvider, (val) => {
if (provider) { if (provider) {
providerName.value = provider.metadata.name; providerName.value = provider.metadata.name;
url.value = provider.spec?.url || ''; url.value = provider.spec?.url || '';
vddkInitImage.value = provider.spec?.settings?.vddkInitImage || '';
const secretRef = provider.spec?.secret; const secretRef = provider.spec?.secret;
@ -239,6 +242,18 @@ const testConnection = async(buttonCb) => {
if (props.editMode && createdProvider.value) { if (props.editMode && createdProvider.value) {
try { try {
createdProvider.value.spec.url = url.value; createdProvider.value.spec.url = url.value;
const vddkImage = vddkInitImage.value?.trim();
if (vddkImage) {
createdProvider.value.spec.settings = {
...(createdProvider.value.spec.settings || {}),
vddkInitImage: vddkImage,
};
} else if (createdProvider.value.spec.settings) {
delete createdProvider.value.spec.settings.vddkInitImage;
}
await createdProvider.value.save(); await createdProvider.value.save();
const secretRef = createdProvider.value.spec?.secret; const secretRef = createdProvider.value.spec?.secret;
@ -283,20 +298,28 @@ const testConnection = async(buttonCb) => {
const namespace = FORKLIFT_NAMESPACE; const namespace = FORKLIFT_NAMESPACE;
const secretName = `${ providerName.value }-creds-${ randomStr(4).toLowerCase() }`; const secretName = `${ providerName.value }-creds-${ randomStr(4).toLowerCase() }`;
const spec = {
type: 'vsphere',
url: url.value,
secret: {
name: secretName,
namespace,
},
};
const vddkImage = vddkInitImage.value?.trim();
if (vddkImage) {
spec.settings = { vddkInitImage: vddkImage };
}
const provider = await store.dispatch(`${ inStore }/create`, { const provider = await store.dispatch(`${ inStore }/create`, {
type: HCI.FORKLIFT_PROVIDER, type: HCI.FORKLIFT_PROVIDER,
metadata: { metadata: {
name: providerName.value, name: providerName.value,
namespace, namespace,
}, },
spec: { spec,
type: 'vsphere',
url: url.value,
secret: {
name: secretName,
namespace,
},
}
}); });
await provider.save(); await provider.save();
@ -420,17 +443,28 @@ defineExpose({ testConnection, clickTestButton });
/> />
</div> </div>
<div> <div class="row">
<LabeledInput <div class="col span-6">
v-model:value="url" <LabeledInput
:label="t('harvester.addons.vmMigration.configureProvider.urlLabel')" v-model:value="url"
:placeholder="t('harvester.addons.vmMigration.configureProvider.urlPlaceholder')" :label="t('harvester.addons.vmMigration.configureProvider.urlLabel')"
:disabled="isExistingProvider && !editMode" :placeholder="t('harvester.addons.vmMigration.configureProvider.urlPlaceholder')"
required :disabled="isExistingProvider && !editMode"
/> required
<p class="text-deemphasized mt-5"> />
{{ t('harvester.addons.vmMigration.configureProvider.urlHint') }} <p class="text-deemphasized mt-5">
</p> {{ t('harvester.addons.vmMigration.configureProvider.urlHint') }}
</p>
</div>
<div class="col span-6">
<LabeledInput
v-model:value="vddkInitImage"
:label="t('harvester.addons.vmMigration.configureProvider.vddkImageLabel')"
:placeholder="t('harvester.addons.vmMigration.configureProvider.vddkImagePlaceholder')"
:tooltip="t('harvester.addons.vmMigration.configureProvider.vddkImageTooltip')"
:disabled="isExistingProvider && !editMode"
/>
</div>
</div> </div>
<div class="row"> <div class="row">

View File

@ -1898,6 +1898,9 @@ harvester:
urlLabel: vCenter/ESXi URL urlLabel: vCenter/ESXi URL
urlPlaceholder: "https://vcenter.example.com/sdk" urlPlaceholder: "https://vcenter.example.com/sdk"
urlHint: Enter the full URL including https:// urlHint: Enter the full URL including https://
vddkImageLabel: VMware VDDK Image
vddkImagePlaceholder: "[your-private-registry]/vddk-image:latest"
vddkImageTooltip: Specify the VMware VDDK (Virtual Disk Development Kit) Image to accelerate and optimize VMware virtual machine migration.
skipSsl: Skip SSL certificate verification skipSsl: Skip SSL certificate verification
skipSslHint: Not recommended for production environments skipSslHint: Not recommended for production environments
testConnection: testConnection:

View File

@ -46,6 +46,7 @@ const stepData = reactive({
username: '', username: '',
password: '', password: '',
skipTlsVerify: false, skipTlsVerify: false,
vddkInitImage: '',
testPassed: false, testPassed: false,
testResult: null, testResult: null,
testError: null, testError: null,
@ -193,6 +194,7 @@ const init = async() => {
stepData.provider.selectedProvider = fetchedProvider.metadata.name; stepData.provider.selectedProvider = fetchedProvider.metadata.name;
stepData.provider.providerName = fetchedProvider.metadata.name; stepData.provider.providerName = fetchedProvider.metadata.name;
stepData.provider.url = fetchedProvider.spec?.url || ''; stepData.provider.url = fetchedProvider.spec?.url || '';
stepData.provider.vddkInitImage = fetchedProvider.spec?.settings?.vddkInitImage || '';
stepData.provider.createdProvider = fetchedProvider; stepData.provider.createdProvider = fetchedProvider;
const secretRef = fetchedProvider.spec?.secret; const secretRef = fetchedProvider.spec?.secret;

View File

@ -41,6 +41,7 @@ const stepData = reactive({
username: '', username: '',
password: '', password: '',
skipTlsVerify: false, skipTlsVerify: false,
vddkInitImage: '',
testPassed: false, testPassed: false,
testResult: null, testResult: null,
testError: null, testError: null,