mirror of
https://github.com/harvester/harvester-ui-extension.git
synced 2026-08-16 12:49:14 +00:00
* feat: allow user specify namespace for migrated VM * refactor: not pre-select target namespace * refactor: ignore option * refactor: copilot review --------- (cherry picked from commit 3ff45342944073ca10ae09c4a20de2cc18d656ac) Signed-off-by: Andy Lee <andy.lee@suse.com> Co-authored-by: Andy Lee <andy.lee@suse.com>
This commit is contained in:
parent
b937a2147b
commit
b4b49222c3
@ -5,8 +5,10 @@ import Loading from '@shell/components/Loading';
|
|||||||
import { Banner } from '@components/Banner';
|
import { Banner } from '@components/Banner';
|
||||||
import { RcItemCard } from '@components/RcItemCard';
|
import { RcItemCard } from '@components/RcItemCard';
|
||||||
import { LabeledInput } from '@components/Form/LabeledInput';
|
import { LabeledInput } from '@components/Form/LabeledInput';
|
||||||
|
import LabeledSelect from '@shell/components/form/LabeledSelect';
|
||||||
import MappingsCell from '../MappingsCell';
|
import MappingsCell from '../MappingsCell';
|
||||||
import { useI18n } from '@shell/composables/useI18n';
|
import { useI18n } from '@shell/composables/useI18n';
|
||||||
|
import { NAMESPACE } from '@shell/config/types';
|
||||||
import { HCI } from '../../types';
|
import { HCI } from '../../types';
|
||||||
import { FORKLIFT_NAMESPACE } from '../../config/harvester-map';
|
import { FORKLIFT_NAMESPACE } from '../../config/harvester-map';
|
||||||
import {
|
import {
|
||||||
@ -30,18 +32,27 @@ const vms = ref([]);
|
|||||||
const networkMappings = ref([]);
|
const networkMappings = ref([]);
|
||||||
const storageMappings = ref([]);
|
const storageMappings = ref([]);
|
||||||
const planName = ref('');
|
const planName = ref('');
|
||||||
|
const targetNamespace = ref('');
|
||||||
|
const namespaceOptions = ref([]);
|
||||||
const errors = ref([]);
|
const errors = ref([]);
|
||||||
const loading = ref(true);
|
const loading = ref(true);
|
||||||
|
|
||||||
// Restore persisted state
|
// Restore persisted state
|
||||||
planName.value = props.stepData.planName;
|
planName.value = props.stepData.planName;
|
||||||
|
targetNamespace.value = props.stepData.targetNamespace || '';
|
||||||
|
|
||||||
const NAMESPACE = FORKLIFT_NAMESPACE;
|
const NS = FORKLIFT_NAMESPACE;
|
||||||
const TARGET_NAMESPACE = 'default';
|
|
||||||
|
|
||||||
watch(planName, (val) => {
|
watch(planName, (val) => {
|
||||||
props.stepData.planName = val;
|
props.stepData.planName = val;
|
||||||
emit('ready', !!val);
|
}, { immediate: true });
|
||||||
|
|
||||||
|
watch(targetNamespace, (val) => {
|
||||||
|
props.stepData.targetNamespace = val;
|
||||||
|
});
|
||||||
|
|
||||||
|
watch([planName, targetNamespace], ([name, namespace]) => {
|
||||||
|
emit('ready', !!name && !!namespace);
|
||||||
}, { immediate: true });
|
}, { immediate: true });
|
||||||
|
|
||||||
const totalVCpu = computed(() => vms.value.reduce((sum, vm) => sum + (vm.cpuCount || vm.numCPU || 0), 0));
|
const totalVCpu = computed(() => vms.value.reduce((sum, vm) => sum + (vm.cpuCount || vm.numCPU || 0), 0));
|
||||||
@ -103,13 +114,13 @@ const startMigrationAction = async() => {
|
|||||||
apiVersion: FORKLIFT_API_VERSION,
|
apiVersion: FORKLIFT_API_VERSION,
|
||||||
kind: 'Provider',
|
kind: 'Provider',
|
||||||
name: props.providerName,
|
name: props.providerName,
|
||||||
namespace: NAMESPACE,
|
namespace: NS,
|
||||||
},
|
},
|
||||||
destination: {
|
destination: {
|
||||||
apiVersion: FORKLIFT_API_VERSION,
|
apiVersion: FORKLIFT_API_VERSION,
|
||||||
kind: 'Provider',
|
kind: 'Provider',
|
||||||
name: 'host',
|
name: 'host',
|
||||||
namespace: NAMESPACE,
|
namespace: NS,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -118,15 +129,15 @@ const startMigrationAction = async() => {
|
|||||||
|
|
||||||
const networkMap = await store.dispatch(`${ inStore }/create`, {
|
const networkMap = await store.dispatch(`${ inStore }/create`, {
|
||||||
type: HCI.FORKLIFT_NETWORK_MAP,
|
type: HCI.FORKLIFT_NETWORK_MAP,
|
||||||
metadata: { name: networkMapName, namespace: NAMESPACE },
|
metadata: { name: networkMapName, namespace: NS },
|
||||||
spec: { map: buildNetworkMapEntries(props.mappingEntries?.networkEntries || [], NAMESPACE), provider: providerRef },
|
spec: { map: buildNetworkMapEntries(props.mappingEntries?.networkEntries || [], NS), provider: providerRef },
|
||||||
});
|
});
|
||||||
|
|
||||||
await networkMap.save();
|
await networkMap.save();
|
||||||
|
|
||||||
const storageMap = await store.dispatch(`${ inStore }/create`, {
|
const storageMap = await store.dispatch(`${ inStore }/create`, {
|
||||||
type: HCI.FORKLIFT_STORAGE_MAP,
|
type: HCI.FORKLIFT_STORAGE_MAP,
|
||||||
metadata: { name: storageMapName, namespace: NAMESPACE },
|
metadata: { name: storageMapName, namespace: NS },
|
||||||
spec: { map: buildStorageMapEntries(props.mappingEntries?.storageEntries || []), provider: providerRef },
|
spec: { map: buildStorageMapEntries(props.mappingEntries?.storageEntries || []), provider: providerRef },
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -134,7 +145,7 @@ const startMigrationAction = async() => {
|
|||||||
|
|
||||||
const plan = await store.dispatch(`${ inStore }/create`, {
|
const plan = await store.dispatch(`${ inStore }/create`, {
|
||||||
type: HCI.FORKLIFT_PLAN,
|
type: HCI.FORKLIFT_PLAN,
|
||||||
metadata: { name: planName.value, namespace: NAMESPACE },
|
metadata: { name: planName.value, namespace: NS },
|
||||||
spec: {
|
spec: {
|
||||||
provider: providerRef,
|
provider: providerRef,
|
||||||
map: {
|
map: {
|
||||||
@ -142,16 +153,16 @@ const startMigrationAction = async() => {
|
|||||||
apiVersion: FORKLIFT_API_VERSION,
|
apiVersion: FORKLIFT_API_VERSION,
|
||||||
kind: 'NetworkMap',
|
kind: 'NetworkMap',
|
||||||
name: networkMapName,
|
name: networkMapName,
|
||||||
namespace: NAMESPACE,
|
namespace: NS,
|
||||||
},
|
},
|
||||||
storage: {
|
storage: {
|
||||||
apiVersion: FORKLIFT_API_VERSION,
|
apiVersion: FORKLIFT_API_VERSION,
|
||||||
kind: 'StorageMap',
|
kind: 'StorageMap',
|
||||||
name: storageMapName,
|
name: storageMapName,
|
||||||
namespace: NAMESPACE,
|
namespace: NS,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
targetNamespace: TARGET_NAMESPACE,
|
targetNamespace: targetNamespace.value,
|
||||||
vms: vms.value.map((vm) => ({ id: vm.id, name: vm.name || vm.id })),
|
vms: vms.value.map((vm) => ({ id: vm.id, name: vm.name || vm.id })),
|
||||||
warm: false,
|
warm: false,
|
||||||
},
|
},
|
||||||
@ -177,9 +188,19 @@ const startMigrationAction = async() => {
|
|||||||
await plan.startMigration();
|
await plan.startMigration();
|
||||||
};
|
};
|
||||||
|
|
||||||
const init = () => {
|
const init = async() => {
|
||||||
vms.value = props.selectedVms;
|
vms.value = props.selectedVms;
|
||||||
|
|
||||||
|
const inStore = store.getters['currentProduct'].inStore;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const namespaces = await store.dispatch(`${ inStore }/findAll`, { type: NAMESPACE });
|
||||||
|
|
||||||
|
namespaceOptions.value = namespaces.filter((ns) => !ns.isSystem).map((ns) => ns.metadata.name).sort().map((name) => ({ label: name, value: name }));
|
||||||
|
} catch (e) {
|
||||||
|
namespaceOptions.value = [];
|
||||||
|
}
|
||||||
|
|
||||||
if (props.mappingEntries) {
|
if (props.mappingEntries) {
|
||||||
networkMappings.value = (props.mappingEntries.networkEntries || []).map((entry) => ({
|
networkMappings.value = (props.mappingEntries.networkEntries || []).map((entry) => ({
|
||||||
source: entry.name || entry.id,
|
source: entry.name || entry.id,
|
||||||
@ -216,9 +237,18 @@ defineExpose({ startMigration: startMigrationAction });
|
|||||||
<h3 class="section-title m-0">
|
<h3 class="section-title m-0">
|
||||||
{{ t('harvester.addons.vmMigration.reviewMigration.migrationDetails') }}
|
{{ t('harvester.addons.vmMigration.reviewMigration.migrationDetails') }}
|
||||||
</h3>
|
</h3>
|
||||||
<div class="span-9">
|
<div class="name-row">
|
||||||
|
<LabeledSelect
|
||||||
|
v-model:value="targetNamespace"
|
||||||
|
class="namespace-select"
|
||||||
|
:label="t('harvester.addons.vmMigration.reviewMigration.targetNamespace')"
|
||||||
|
:placeholder="t('harvester.addons.vmMigration.reviewMigration.targetNamespacePlaceholder')"
|
||||||
|
:options="namespaceOptions"
|
||||||
|
required
|
||||||
|
/>
|
||||||
<LabeledInput
|
<LabeledInput
|
||||||
v-model:value="planName"
|
v-model:value="planName"
|
||||||
|
class="name-input"
|
||||||
:label="t('harvester.addons.vmMigration.reviewMigration.planName')"
|
:label="t('harvester.addons.vmMigration.reviewMigration.planName')"
|
||||||
:placeholder="t('harvester.addons.vmMigration.reviewMigration.planNamePlaceholder')"
|
:placeholder="t('harvester.addons.vmMigration.reviewMigration.planNamePlaceholder')"
|
||||||
required
|
required
|
||||||
@ -244,7 +274,7 @@ defineExpose({ startMigration: startMigrationAction });
|
|||||||
<span class="detail-label">{{ t('harvester.addons.vmMigration.reviewMigration.targetNamespace') }}</span>
|
<span class="detail-label">{{ t('harvester.addons.vmMigration.reviewMigration.targetNamespace') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="detail-item">
|
<div class="detail-item">
|
||||||
<span class="detail-value">{{ TARGET_NAMESPACE }}</span>
|
<span class="detail-value">{{ targetNamespace }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="detail-column">
|
<div class="detail-column">
|
||||||
@ -347,6 +377,22 @@ defineExpose({ startMigration: startMigrationAction });
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.name-row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
.namespace-select {
|
||||||
|
max-width: 280px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name-input {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.details-grid {
|
.details-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: minmax(230px, 1fr) 1fr 1fr;
|
grid-template-columns: minmax(230px, 1fr) 1fr 1fr;
|
||||||
|
|||||||
@ -1956,7 +1956,7 @@ harvester:
|
|||||||
initializingMigration: Initializing migration
|
initializingMigration: Initializing migration
|
||||||
finishedSuccessfully: Finished Successfully
|
finishedSuccessfully: Finished Successfully
|
||||||
step: "Step {index}"
|
step: "Step {index}"
|
||||||
vmCount: "{count} VMs"
|
vmCount: "{count, plural, one {# VM} other {# VMs}}"
|
||||||
vmId: "id: {id}"
|
vmId: "id: {id}"
|
||||||
configureMappings:
|
configureMappings:
|
||||||
title: Set Mappings
|
title: Set Mappings
|
||||||
@ -1971,7 +1971,7 @@ harvester:
|
|||||||
template: Use existing network mapping as template
|
template: Use existing network mapping as template
|
||||||
options:
|
options:
|
||||||
podNetworking: Management Network
|
podNetworking: Management Network
|
||||||
ignored: Ignored
|
ignored: Ignored (No NIC will be created)
|
||||||
storageMapping:
|
storageMapping:
|
||||||
title: Storage Mapping
|
title: Storage Mapping
|
||||||
description: Map VMware datastores to Harvester storage classes
|
description: Map VMware datastores to Harvester storage classes
|
||||||
@ -1988,7 +1988,8 @@ harvester:
|
|||||||
memory: Memory
|
memory: Memory
|
||||||
storage: Storage
|
storage: Storage
|
||||||
source: Source
|
source: Source
|
||||||
targetNamespace: Target Namespace
|
targetNamespace: Namespace
|
||||||
|
targetNamespacePlaceholder: "Select target namespace"
|
||||||
migrationMode: Migration Mode
|
migrationMode: Migration Mode
|
||||||
coldMigration: Cold Migration
|
coldMigration: Cold Migration
|
||||||
virtualMachines: Virtual Machines
|
virtualMachines: Virtual Machines
|
||||||
|
|||||||
@ -56,7 +56,7 @@ const stepData = reactive({
|
|||||||
networkEntries: [],
|
networkEntries: [],
|
||||||
storageEntries: [],
|
storageEntries: [],
|
||||||
},
|
},
|
||||||
review: { planName: '' },
|
review: { planName: '', targetNamespace: '' },
|
||||||
});
|
});
|
||||||
|
|
||||||
const steps = reactive([
|
const steps = reactive([
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user