mirror of
https://github.com/harvester/harvester-ui-extension.git
synced 2026-08-16 12:49:14 +00:00
* fix: hide Virtul machine migration tabs if forklift addon is not enabled * fix: rename advanced to configurations * fix: fixed column width * refactor: review comment * revert: power on background color --------- (cherry picked from commit 41cf44d23131ce30762fd29fe38d833d22238398) Signed-off-by: Andy Lee <andy.lee@suse.com> Co-authored-by: Andy Lee <andy.lee@suse.com>
This commit is contained in:
parent
e71d12f67f
commit
7493fe2dfc
@ -402,6 +402,7 @@ defineExpose({ testConnection, clickTestButton });
|
|||||||
<div v-if="!createOnly && !editMode">
|
<div v-if="!createOnly && !editMode">
|
||||||
<LabeledSelect
|
<LabeledSelect
|
||||||
v-model:value="selectedProvider"
|
v-model:value="selectedProvider"
|
||||||
|
required
|
||||||
:label="t('harvester.addons.vmMigration.configureProvider.providerSelect')"
|
:label="t('harvester.addons.vmMigration.configureProvider.providerSelect')"
|
||||||
:options="providerOptions"
|
:options="providerOptions"
|
||||||
:reduce="(opt) => opt.value"
|
:reduce="(opt) => opt.value"
|
||||||
|
|||||||
@ -107,18 +107,21 @@ const headers = [
|
|||||||
labelKey: 'harvester.addons.vmMigration.selectVms.columns.os',
|
labelKey: 'harvester.addons.vmMigration.selectVms.columns.os',
|
||||||
value: 'os',
|
value: 'os',
|
||||||
sort: ['os'],
|
sort: ['os'],
|
||||||
|
width: 180,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'resources',
|
name: 'resources',
|
||||||
labelKey: 'harvester.addons.vmMigration.selectVms.columns.resources',
|
labelKey: 'harvester.addons.vmMigration.selectVms.columns.resources',
|
||||||
value: 'resources',
|
value: 'resources',
|
||||||
sort: false,
|
sort: false,
|
||||||
|
width: 140,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'powerState',
|
name: 'powerState',
|
||||||
labelKey: 'harvester.addons.vmMigration.selectVms.columns.powerState',
|
labelKey: 'harvester.addons.vmMigration.selectVms.columns.powerState',
|
||||||
value: 'powerState',
|
value: 'powerState',
|
||||||
sort: ['powerState'],
|
sort: ['powerState'],
|
||||||
|
width: 130,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'network',
|
name: 'network',
|
||||||
@ -126,6 +129,7 @@ const headers = [
|
|||||||
value: 'network',
|
value: 'network',
|
||||||
sort: ['network'],
|
sort: ['network'],
|
||||||
subLabel: t('harvester.addons.vmMigration.generic.identifier'),
|
subLabel: t('harvester.addons.vmMigration.generic.identifier'),
|
||||||
|
width: 200,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'datastore',
|
name: 'datastore',
|
||||||
@ -133,6 +137,7 @@ const headers = [
|
|||||||
value: 'datastore',
|
value: 'datastore',
|
||||||
sort: ['datastore'],
|
sort: ['datastore'],
|
||||||
subLabel: t('harvester.addons.vmMigration.generic.identifier'),
|
subLabel: t('harvester.addons.vmMigration.generic.identifier'),
|
||||||
|
width: 200,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@ -1378,7 +1378,7 @@ export function init($plugin, store) {
|
|||||||
virtualType({
|
virtualType({
|
||||||
name: HCI.FORKLIFT_NETWORK_MAP,
|
name: HCI.FORKLIFT_NETWORK_MAP,
|
||||||
labelKey: 'harvester.addons.vmMigration.labels.networkMap',
|
labelKey: 'harvester.addons.vmMigration.labels.networkMap',
|
||||||
group: 'vmMigration::Advanced',
|
group: 'vmMigration::Configurations',
|
||||||
namespaced: true,
|
namespaced: true,
|
||||||
route: {
|
route: {
|
||||||
name: `${ PRODUCT_NAME }-c-cluster-resource`,
|
name: `${ PRODUCT_NAME }-c-cluster-resource`,
|
||||||
@ -1405,7 +1405,7 @@ export function init($plugin, store) {
|
|||||||
virtualType({
|
virtualType({
|
||||||
name: HCI.FORKLIFT_STORAGE_MAP,
|
name: HCI.FORKLIFT_STORAGE_MAP,
|
||||||
labelKey: 'harvester.addons.vmMigration.labels.storageMap',
|
labelKey: 'harvester.addons.vmMigration.labels.storageMap',
|
||||||
group: 'vmMigration::Advanced',
|
group: 'vmMigration::Configurations',
|
||||||
namespaced: true,
|
namespaced: true,
|
||||||
route: {
|
route: {
|
||||||
name: `${ PRODUCT_NAME }-c-cluster-resource`,
|
name: `${ PRODUCT_NAME }-c-cluster-resource`,
|
||||||
@ -1433,7 +1433,7 @@ export function init($plugin, store) {
|
|||||||
virtualType({
|
virtualType({
|
||||||
name: HCI.FORKLIFT_PLAN,
|
name: HCI.FORKLIFT_PLAN,
|
||||||
labelKey: 'harvester.addons.vmMigration.labels.plan',
|
labelKey: 'harvester.addons.vmMigration.labels.plan',
|
||||||
group: 'vmMigration::Advanced',
|
group: 'vmMigration::Configurations',
|
||||||
namespaced: true,
|
namespaced: true,
|
||||||
route: {
|
route: {
|
||||||
name: `${ PRODUCT_NAME }-c-cluster-resource`,
|
name: `${ PRODUCT_NAME }-c-cluster-resource`,
|
||||||
@ -1459,7 +1459,7 @@ export function init($plugin, store) {
|
|||||||
virtualType({
|
virtualType({
|
||||||
name: HCI.FORKLIFT_MIGRATION,
|
name: HCI.FORKLIFT_MIGRATION,
|
||||||
labelKey: 'harvester.addons.vmMigration.labels.migration',
|
labelKey: 'harvester.addons.vmMigration.labels.migration',
|
||||||
group: 'vmMigration::Advanced',
|
group: 'vmMigration::Configurations',
|
||||||
namespaced: true,
|
namespaced: true,
|
||||||
route: {
|
route: {
|
||||||
name: `${ PRODUCT_NAME }-c-cluster-resource`,
|
name: `${ PRODUCT_NAME }-c-cluster-resource`,
|
||||||
@ -1479,11 +1479,16 @@ export function init($plugin, store) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Register the dashboard entry directly — it's a virtual type with no schema,
|
|
||||||
// so it cannot go through registerAddonSideNav (which filters by schema).
|
|
||||||
basicType(['forklift-create'], 'vmMigration');
|
|
||||||
|
|
||||||
// Enable SideNav based on Forklift Addon Status
|
// Enable SideNav based on Forklift Addon Status
|
||||||
|
// The dashboard entry ('forklift-create') is a schema-less virtual type, so it
|
||||||
|
// is registered with requireSchema:false to still be gated by the addon status.
|
||||||
|
registerAddonSideNav(store, PRODUCT_NAME, {
|
||||||
|
addonName: ADD_ONS.FORKLIFT_OPERATOR,
|
||||||
|
resourceType: HCI.ADD_ONS,
|
||||||
|
navGroup: 'vmMigration',
|
||||||
|
requireSchema: false,
|
||||||
|
types: ['forklift-create']
|
||||||
|
});
|
||||||
registerAddonSideNav(store, PRODUCT_NAME, {
|
registerAddonSideNav(store, PRODUCT_NAME, {
|
||||||
addonName: ADD_ONS.FORKLIFT_OPERATOR,
|
addonName: ADD_ONS.FORKLIFT_OPERATOR,
|
||||||
resourceType: HCI.ADD_ONS,
|
resourceType: HCI.ADD_ONS,
|
||||||
@ -1495,7 +1500,7 @@ export function init($plugin, store) {
|
|||||||
registerAddonSideNav(store, PRODUCT_NAME, {
|
registerAddonSideNav(store, PRODUCT_NAME, {
|
||||||
addonName: ADD_ONS.FORKLIFT_OPERATOR,
|
addonName: ADD_ONS.FORKLIFT_OPERATOR,
|
||||||
resourceType: HCI.ADD_ONS,
|
resourceType: HCI.ADD_ONS,
|
||||||
navGroup: 'vmMigration::Advanced',
|
navGroup: 'vmMigration::Configurations',
|
||||||
types: [
|
types: [
|
||||||
HCI.FORKLIFT_NETWORK_MAP,
|
HCI.FORKLIFT_NETWORK_MAP,
|
||||||
HCI.FORKLIFT_STORAGE_MAP,
|
HCI.FORKLIFT_STORAGE_MAP,
|
||||||
|
|||||||
@ -36,7 +36,8 @@ nav:
|
|||||||
Logging: Logging
|
Logging: Logging
|
||||||
'Monitoring and Logging': Monitoring and Logging
|
'Monitoring and Logging': Monitoring and Logging
|
||||||
vmimport: Virtual Machine Imports
|
vmimport: Virtual Machine Imports
|
||||||
vmMigration: VM Migration
|
vmMigration: Virtual Machine Migration
|
||||||
|
Configurations: Configurations
|
||||||
|
|
||||||
resourceTable:
|
resourceTable:
|
||||||
groupBy:
|
groupBy:
|
||||||
@ -378,7 +379,7 @@ harvester:
|
|||||||
vmMigrationMapSourceProvider: Source Provider
|
vmMigrationMapSourceProvider: Source Provider
|
||||||
vmMigrationMapDestProvider: Destination Provider
|
vmMigrationMapDestProvider: Destination Provider
|
||||||
vmMigrationPlanTargetNs: Target Namespace
|
vmMigrationPlanTargetNs: Target Namespace
|
||||||
vmMigrationPlanVmCount: VMs
|
vmMigrationPlanVmCount: VM Count
|
||||||
vmMigrationMigrationPlan: Plan
|
vmMigrationMigrationPlan: Plan
|
||||||
tab:
|
tab:
|
||||||
volume: Volumes
|
volume: Volumes
|
||||||
@ -1855,7 +1856,7 @@ harvester:
|
|||||||
connectionFailed: Connection failed
|
connectionFailed: Connection failed
|
||||||
providerNotReady: Provider not ready
|
providerNotReady: Provider not ready
|
||||||
labels:
|
labels:
|
||||||
dashboard: Migrations
|
dashboard: Migration Overview
|
||||||
provider: Providers
|
provider: Providers
|
||||||
networkMap: Network Maps
|
networkMap: Network Maps
|
||||||
storageMap: Storage Maps
|
storageMap: Storage Maps
|
||||||
@ -1937,8 +1938,8 @@ harvester:
|
|||||||
network: Network
|
network: Network
|
||||||
datastore: Datastore
|
datastore: Datastore
|
||||||
dashboard:
|
dashboard:
|
||||||
title: VMware to Harvester Migration
|
title: Virtual Machine Migration Overview
|
||||||
description: Migrate virtual machines from VMware vSphere to Harvester using cold migration for data consistency. Create a migration plan to select VMs, map networks and storage, and prepare a migration to Harvester.
|
description: "Migrate virtual machines from VMware vSphere to Harvester using cold migration for data consistency. Create a migration plan to select VMs, map networks and storage, and prepare a migration to Harvester."
|
||||||
createPlan: Create Migration
|
createPlan: Create Migration
|
||||||
tableTitle: Migrations
|
tableTitle: Migrations
|
||||||
columns:
|
columns:
|
||||||
|
|||||||
@ -14,6 +14,17 @@ export default class ForkliftPlan extends HarvesterResource {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base HarvesterResource sets `doneOverride` to `listLocation`, which would
|
||||||
|
* force every single-item delete to redirect to the VM Migration dashboard.
|
||||||
|
* Return undefined so the delete flow stays on the current page (list/dashboard)
|
||||||
|
* and only falls back to the shell's default navigation when a now-invalid
|
||||||
|
* detail page is being viewed.
|
||||||
|
*/
|
||||||
|
get doneOverride() {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
get planFailed() {
|
get planFailed() {
|
||||||
const conditions = this.status?.conditions || [];
|
const conditions = this.status?.conditions || [];
|
||||||
|
|
||||||
@ -247,7 +258,7 @@ export default class ForkliftPlan extends HarvesterResource {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Deleting a Plan cascades via ownerReferences set at creation time.
|
* Deleting a Plan cascades via ownerReferences set at creation time.
|
||||||
* Kubernetes GC will automatically delete: Migration, NetworkMap, StorageMap, Provider (→ Secret).
|
* Kubernetes GC will automatically delete: Migration, NetworkMap, StorageMap.
|
||||||
* Use foreground propagation to ensure children are deleted before the parent.
|
* Use foreground propagation to ensure children are deleted before the parent.
|
||||||
*/
|
*/
|
||||||
remove(opt = {}) {
|
remove(opt = {}) {
|
||||||
|
|||||||
@ -11,6 +11,8 @@ import { SCHEMA } from '@shell/config/types';
|
|||||||
import { useI18n } from '@shell/composables/useI18n';
|
import { useI18n } from '@shell/composables/useI18n';
|
||||||
import { HCI } from '../../../../types';
|
import { HCI } from '../../../../types';
|
||||||
import { PRODUCT_NAME } from '../../../../config/harvester';
|
import { PRODUCT_NAME } from '../../../../config/harvester';
|
||||||
|
import { ADD_ONS } from '../../../../config/harvester-map';
|
||||||
|
import { currentRouter } from '../../../../utils/router';
|
||||||
import { FORKLIFT_PLAN_VM_COUNT } from '../../../../config/table-headers';
|
import { FORKLIFT_PLAN_VM_COUNT } from '../../../../config/table-headers';
|
||||||
import { STATE, NAME as NAME_COL, AGE } from '@shell/config/table-headers';
|
import { STATE, NAME as NAME_COL, AGE } from '@shell/config/table-headers';
|
||||||
|
|
||||||
@ -158,6 +160,29 @@ const headers = [
|
|||||||
|
|
||||||
const init = async() => {
|
const init = async() => {
|
||||||
try {
|
try {
|
||||||
|
// Guard: the Forklift dashboard is only available when the Forklift addon is
|
||||||
|
// enabled. Redirect back to the Harvester dashboard otherwise (e.g. direct URL).
|
||||||
|
let forkliftEnabled = false;
|
||||||
|
|
||||||
|
if (store.getters[`${ inStore.value }/schemaFor`](HCI.ADD_ONS)) {
|
||||||
|
const addons = await store.dispatch(`${ inStore.value }/findAll`, { type: HCI.ADD_ONS });
|
||||||
|
|
||||||
|
forkliftEnabled = addons.find((a) => a.metadata?.name === ADD_ONS.FORKLIFT_OPERATOR)?.spec?.enabled === true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!forkliftEnabled) {
|
||||||
|
await currentRouter().replace({
|
||||||
|
name: `${ PRODUCT_NAME }-c-cluster-resource`,
|
||||||
|
params: {
|
||||||
|
product: store.getters['productId'],
|
||||||
|
cluster: store.getters['clusterId'],
|
||||||
|
resource: HCI.DASHBOARD,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
store.dispatch(`${ inStore.value }/findAll`, { type: HCI.FORKLIFT_PLAN }),
|
store.dispatch(`${ inStore.value }/findAll`, { type: HCI.FORKLIFT_PLAN }),
|
||||||
store.dispatch(`${ inStore.value }/findAll`, { type: HCI.FORKLIFT_NETWORK_MAP }),
|
store.dispatch(`${ inStore.value }/findAll`, { type: HCI.FORKLIFT_NETWORK_MAP }),
|
||||||
@ -189,9 +214,10 @@ init();
|
|||||||
>
|
>
|
||||||
<template #subHeader>
|
<template #subHeader>
|
||||||
<div class="mmt-5">
|
<div class="mmt-5">
|
||||||
<p class="text-muted">
|
<p
|
||||||
{{ t('harvester.addons.vmMigration.dashboard.description') }}
|
v-clean-html="t('harvester.addons.vmMigration.dashboard.description')"
|
||||||
</p>
|
class="text-muted"
|
||||||
|
></p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #createButton>
|
<template #createButton>
|
||||||
@ -213,11 +239,6 @@ init();
|
|||||||
:search="false"
|
:search="false"
|
||||||
key-field="_key"
|
key-field="_key"
|
||||||
>
|
>
|
||||||
<template #header-left>
|
|
||||||
<h3 class="table-title m-0">
|
|
||||||
{{ t('harvester.addons.vmMigration.dashboard.tableTitle') }}
|
|
||||||
</h3>
|
|
||||||
</template>
|
|
||||||
<template #cell:name="{ row }">
|
<template #cell:name="{ row }">
|
||||||
<div class="plan-name-cell">
|
<div class="plan-name-cell">
|
||||||
<div class="plan-name">
|
<div class="plan-name">
|
||||||
|
|||||||
@ -8,9 +8,11 @@
|
|||||||
* @param {String} config.resourceType - The schema ID for addons.
|
* @param {String} config.resourceType - The schema ID for addons.
|
||||||
* @param {String} config.navGroup - The group name in the side nav.
|
* @param {String} config.navGroup - The group name in the side nav.
|
||||||
* @param {Array<String>} config.types - Array of Resource IDs to show/hide.
|
* @param {Array<String>} config.types - Array of Resource IDs to show/hide.
|
||||||
|
* @param {Boolean} [config.requireSchema=true] - When true, only types with an
|
||||||
|
* accessible schema are shown. Set false for schema-less virtual types.
|
||||||
*/
|
*/
|
||||||
export function registerAddonSideNav(store, productName, {
|
export function registerAddonSideNav(store, productName, {
|
||||||
addonName, resourceType, navGroup, types
|
addonName, resourceType, navGroup, types, requireSchema = true
|
||||||
}) {
|
}) {
|
||||||
if (typeof window === 'undefined') {
|
if (typeof window === 'undefined') {
|
||||||
return;
|
return;
|
||||||
@ -57,7 +59,7 @@ export function registerAddonSideNav(store, productName, {
|
|||||||
|
|
||||||
// Adds or removes the resource IDs from the product visibility whitelist.
|
// Adds or removes the resource IDs from the product visibility whitelist.
|
||||||
const setMenuVisibility = (visible) => {
|
const setMenuVisibility = (visible) => {
|
||||||
const accessibleTypes = visible ? types.filter(hasAccessibleSchema) : [];
|
const accessibleTypes = visible ? (requireSchema ? types.filter(hasAccessibleSchema) : types) : [];
|
||||||
|
|
||||||
// Always clear first to remove any previously-registered types that are
|
// Always clear first to remove any previously-registered types that are
|
||||||
// no longer accessible (e.g. partial permission changes like types=[A,B] where B is dropped).
|
// no longer accessible (e.g. partial permission changes like types=[A,B] where B is dropped).
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user