mirror of
https://github.com/harvester/harvester-ui-extension.git
synced 2026-08-16 04:39:15 +00:00
* feat: improve VM migration dashboard mapping labels and VM detail link - Display Management Network / Ignored labels via i18n in mappings column and review card - Add clickable external-link icon to migrated VM detail page after successful migration - Add tooltip to the VM detail external-link icon * refactor: modify duration column width * refactor: copilot review comment --------- (cherry picked from commit a10d6518271e999697340165c839df9e24fd75a8) Signed-off-by: Andy Lee <andy.lee@suse.com> Co-authored-by: Andy Lee <andy.lee@suse.com>
This commit is contained in:
parent
6e709bc00f
commit
e991c9bcc8
@ -55,6 +55,18 @@ watch([planName, targetNamespace], ([name, namespace]) => {
|
||||
emit('ready', !!name && !!namespace);
|
||||
}, { immediate: true });
|
||||
|
||||
const formatNetworkTarget = (target = '') => {
|
||||
if (target === 'pod') {
|
||||
return t('harvester.addons.vmMigration.configureMappings.networkMapping.options.podNetworking');
|
||||
}
|
||||
|
||||
if (target === 'ignored') {
|
||||
return t('harvester.addons.vmMigration.generic.ignored');
|
||||
}
|
||||
|
||||
return target;
|
||||
};
|
||||
|
||||
const totalVCpu = computed(() => vms.value.reduce((sum, vm) => sum + (vm.cpuCount || vm.numCPU || 0), 0));
|
||||
|
||||
const totalMemoryGB = computed(() => {
|
||||
@ -331,7 +343,7 @@ defineExpose({ startMigration: startMigrationAction });
|
||||
</span>
|
||||
</div>
|
||||
<MappingsCell
|
||||
:network-entries="vm.networkMappings.map(m => `${m.source} → ${m.target}`)"
|
||||
:network-entries="vm.networkMappings.map(m => `${m.source} → ${formatNetworkTarget(m.target)}`)"
|
||||
:storage-entries="vm.storageMappings.map(m => `${m.source} → ${m.target}`)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -286,7 +286,7 @@ export const FORKLIFT_PLAN_VM_COUNT = {
|
||||
labelKey: 'harvester.tableHeaders.vmMigrationPlanVmCount',
|
||||
value: 'spec.vms.length',
|
||||
sort: 'spec.vms.length',
|
||||
align: 'left',
|
||||
align: 'center',
|
||||
};
|
||||
|
||||
// Plan reference column in forklift.konveyor.io.migration list page
|
||||
|
||||
@ -1837,6 +1837,7 @@ harvester:
|
||||
unknown: Unknown
|
||||
identifier: Identifier
|
||||
podNetwork: Pod Network
|
||||
ignored: Ignored
|
||||
usedBy: "Used by:"
|
||||
vlan: "VLAN {id}"
|
||||
vCpu: "{count} vCPU"
|
||||
@ -1957,6 +1958,7 @@ harvester:
|
||||
step: "Step {index}"
|
||||
vmCount: "{count, plural, one {# VM} other {# VMs}}"
|
||||
vmId: "id: {id}"
|
||||
checkMigratedVm: Click to check migrated VM details
|
||||
configureMappings:
|
||||
title: Set Mappings
|
||||
description: Map VMware networks and datastores to Harvester and Longhorn target resources
|
||||
|
||||
@ -37,6 +37,7 @@ const inStore = computed(() => store.getters['currentProduct'].inStore);
|
||||
const allPlans = computed(() => store.getters[`${ inStore.value }/all`](HCI.FORKLIFT_PLAN));
|
||||
const allNetworkMaps = computed(() => store.getters[`${ inStore.value }/all`](HCI.FORKLIFT_NETWORK_MAP));
|
||||
const allStorageMaps = computed(() => store.getters[`${ inStore.value }/all`](HCI.FORKLIFT_STORAGE_MAP));
|
||||
const allVMs = computed(() => store.getters[`${ inStore.value }/all`](HCI.VM));
|
||||
|
||||
const escapeHtml = (value = '') => String(value)
|
||||
.replaceAll('&', '&')
|
||||
@ -129,8 +130,8 @@ const rows = computed(() => {
|
||||
const netMap = allNetworkMaps.value.find((m) => m.metadata.name === netMapName && m.metadata.namespace === netMapNs);
|
||||
const storMap = allStorageMaps.value.find((m) => m.metadata.name === storMapName && m.metadata.namespace === storMapNs);
|
||||
|
||||
plan.networkEntries = (netMap?.spec?.map || []).map((e) => `${ e.source?.id || '-' } → ${ e.destination?.type === 'pod' ? t('harvester.addons.vmMigration.generic.podNetwork') : (e.destination?.name || '-') }`);
|
||||
plan.storageEntries = (storMap?.spec?.map || []).map((e) => `${ e.source?.id || '-' } → ${ e.destination?.storageClass || '-' }`);
|
||||
plan.networkEntries = (netMap?.spec?.map || []).map((e) => `${ e.source?.id || 'Ignored' } → ${ e.destination?.type === 'pod' ? t('harvester.addons.vmMigration.configureMappings.networkMapping.options.podNetworking') : (e.destination?.name || 'Ignored') }`);
|
||||
plan.storageEntries = (storMap?.spec?.map || []).map((e) => `${ e.source?.id || 'Ignored' } → ${ e.destination?.storageClass || 'Ignored' }`);
|
||||
|
||||
plan.vmIdsDisplay = (plan.spec?.vms || []).map((vm) => vm.id || vm.name || '').filter(Boolean).join(', ') || '-';
|
||||
|
||||
@ -183,6 +184,24 @@ const rows = computed(() => {
|
||||
errorMsg = `${ currentStep || t('harvester.addons.vmMigration.dashboard.progress.migration') }: ${ t('harvester.addons.vmMigration.dashboard.progress.failed') }`;
|
||||
}
|
||||
|
||||
const vmNamespace = vm.namespace || plan.spec?.targetNamespace || plan.metadata?.namespace;
|
||||
const routeParams = currentRouter().currentRoute?.value?.params || {};
|
||||
const product = routeParams.product || store.getters['productId'];
|
||||
const cluster = routeParams.cluster || store.getters['clusterId'];
|
||||
const vmNameCandidates = [vm.targetName, vm.name, vm.id].filter(Boolean);
|
||||
const targetVm = allVMs.value.find((item) => vmNameCandidates.includes(item.metadata?.name) && (!vmNamespace || item.metadata?.namespace === vmNamespace));
|
||||
const canNavigateToVm = overallProgress >= 100 && !errorMsg && !plan.planFailed && !plan.planCanceled && !!targetVm && !!product && !!cluster;
|
||||
const vmDetailLocation = canNavigateToVm ? {
|
||||
name: `${ PRODUCT_NAME }-c-cluster-resource-namespace-id`,
|
||||
params: {
|
||||
product,
|
||||
cluster,
|
||||
resource: HCI.VM,
|
||||
namespace: targetVm.metadata.namespace,
|
||||
id: targetVm.metadata.name,
|
||||
}
|
||||
} : null;
|
||||
|
||||
return {
|
||||
vmName: vm.name || vm.id || t('harvester.addons.vmMigration.generic.unknown'),
|
||||
vmId: vm.id || '',
|
||||
@ -191,6 +210,7 @@ const rows = computed(() => {
|
||||
pipelineTooltip: formatPipelineTooltip(pipeline),
|
||||
errorMsg,
|
||||
canceled: plan.planCanceled,
|
||||
vmDetailLocation,
|
||||
};
|
||||
});
|
||||
|
||||
@ -248,7 +268,7 @@ const headers = [
|
||||
labelKey: 'harvester.addons.vmMigration.dashboard.columns.duration',
|
||||
value: 'duration',
|
||||
sort: 'durationSeconds',
|
||||
width: 120,
|
||||
width: 90,
|
||||
},
|
||||
{ ...AGE },
|
||||
];
|
||||
@ -283,6 +303,17 @@ const init = async() => {
|
||||
store.dispatch(`${ inStore.value }/findAll`, { type: HCI.FORKLIFT_NETWORK_MAP }),
|
||||
store.dispatch(`${ inStore.value }/findAll`, { type: HCI.FORKLIFT_STORAGE_MAP }),
|
||||
]);
|
||||
|
||||
// VMs are only needed to conditionally show the post-migration VM-detail
|
||||
// link, so load them separately and non-fatally: a failure here (or the
|
||||
// cost of findAll on large clusters) should not block plans/maps from
|
||||
// rendering.
|
||||
try {
|
||||
await store.dispatch(`${ inStore.value }/findAll`, { type: HCI.VM });
|
||||
} catch (e) {
|
||||
// Intentionally ignored: the dashboard still works without the VM list,
|
||||
// the migrated VM-detail link simply won't be shown.
|
||||
}
|
||||
} catch (e) {
|
||||
errors.value = [e?.message || t('harvester.addons.vmMigration.errors.failedLoadPlans')];
|
||||
} finally {
|
||||
@ -366,7 +397,17 @@ init();
|
||||
>
|
||||
<div class="vm-progress-header">
|
||||
<div class="vm-name-block">
|
||||
<span class="vm-name">{{ vm.vmName }}</span>
|
||||
<span class="vm-name">
|
||||
{{ vm.vmName }}
|
||||
<router-link
|
||||
v-if="vm.vmDetailLocation"
|
||||
v-clean-tooltip="t('harvester.addons.vmMigration.dashboard.progress.checkMigratedVm')"
|
||||
:to="vm.vmDetailLocation"
|
||||
class="vm-detail-link"
|
||||
>
|
||||
<i class="icon icon-external-link" />
|
||||
</router-link>
|
||||
</span>
|
||||
<span class="text-muted vm-id">{{ t('harvester.addons.vmMigration.dashboard.progress.vmId', { id: vm.vmId }) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -457,6 +498,14 @@ init();
|
||||
|
||||
.vm-name {
|
||||
font-size: 14px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.vm-detail-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.vm-name-block {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user