diff --git a/pkg/harvester/config/feature-flags.js b/pkg/harvester/config/feature-flags.js new file mode 100644 index 00000000..06d1e3c3 --- /dev/null +++ b/pkg/harvester/config/feature-flags.js @@ -0,0 +1,54 @@ + +// https://github.com/harvester/harvester/wiki/Roadmap +const FEATURES = { + cpuPinning: false, + usbPassthrough: false, + volumeEncryption: false, + schedulingVMBackup: false, + vmSnapshotQuota: false, + longhornV2LVMSupport: false, + improveMaintainMode: false, + autoRotateRke2CertsSetting: false, + kubeconfigDefaultTokenTTLMinutesSetting: false, + supportBundleNodeCollectionTimeoutSetting: false +}; + +// https://github.com/harvester/dashboard/releases/tag/v1.3.0 +const releaseV130 = { ...FEATURES }; + +// https://github.com/harvester/dashboard/releases/tag/v1.3.1 +const releaseV131 = { + ...releaseV130, + autoRotateRke2CertsSetting: true, + supportBundleNodeCollectionTimeoutSetting: true +}; + +// https://github.com/harvester/dashboard/releases/tag/v1.3.2 +const releaseV132 = { + ...releaseV131, + kubeconfigDefaultTokenTTLMinutesSetting: true, +}; + +// TODO: change to https://github.com/harvester/dashboard/releases/tag/v1.4.0 after v1.4.0 release +// https://github.com/harvester/dashboard/releases/tag/v1.4.0-rc5 +// https://github.com/harvester/dashboard/releases/tag/v1.4.0-rc4 +// https://github.com/harvester/dashboard/releases/tag/v1.4.0-rc3 +// https://github.com/harvester/dashboard/releases/tag/v1.4.0-rc2 +// https://github.com/harvester/dashboard/releases/tag/v1.4.0-rc1 +const releaseV140 = { + ...releaseV132, + cpuPinning: true, + usbPassthrough: true, + volumeEncryption: true, + schedulingVMBackup: true, + vmSnapshotQuota: true, + longhornV2LVMSupport: true, + improveMaintainMode: true, +}; + +export const RELEASE_FEATURES = { + 'v1.4.0': releaseV140, + 'v1.3.2': releaseV132, + 'v1.3.1': releaseV132, + 'v1.3.0': releaseV130, +}; diff --git a/pkg/harvester/detail/harvesterhci.io.host/HarvesterHostBasic.vue b/pkg/harvester/detail/harvesterhci.io.host/HarvesterHostBasic.vue index d5a0da9b..d35c49cb 100644 --- a/pkg/harvester/detail/harvesterhci.io.host/HarvesterHostBasic.vue +++ b/pkg/harvester/detail/harvesterhci.io.host/HarvesterHostBasic.vue @@ -247,7 +247,7 @@ export default {
node.cpuPinningFeatureEnabled)) { + out.push({ + name: 'cpuManager', + labelKey: 'harvester.tableHeaders.cpuManager', + value: 'id', + formatter: 'HarvesterCPUPinning', + formatterOpts: { rows: this.rows }, + width: 150, + align: 'center', + }); + } if (this.hasLonghornSchema) { out.push({ name: 'diskState', diff --git a/pkg/harvester/models/harvester/node.js b/pkg/harvester/models/harvester/node.js index b07e7a34..ddcfa958 100644 --- a/pkg/harvester/models/harvester/node.js +++ b/pkg/harvester/models/harvester/node.js @@ -14,8 +14,8 @@ import { findBy, isArray } from '@shell/utils/array'; import { ucFirst } from '@shell/utils/string'; import HarvesterResource from '../harvester'; import { PRODUCT_NAME as HARVESTER_PRODUCT } from '../../config/harvester'; - import { HCI } from '../../types'; +import { featureEnabled } from '../../utils/server'; const ALLOW_SYSTEM_LABEL_KEYS = [ 'topology.kubernetes.io/zone', @@ -61,7 +61,7 @@ export default class HciNode extends HarvesterResource { const enableCPUManager = { action: 'enableCPUManager', - enabled: this.hasAction('enableCPUManager') && !this.isCPUManagerEnableInProgress && !this.isCPUManagerEnabled && !this.isEtcd, // witness node doesn't have CPU manager + enabled: this.cpuPinningFeatureEnabled && this.hasAction('enableCPUManager') && !this.isCPUManagerEnableInProgress && !this.isCPUManagerEnabled && !this.isEtcd, // witness node doesn't have CPU manager icon: 'icon icon-fw icon-os-management', label: this.t('harvester.action.enableCPUManager'), total: 1 @@ -69,7 +69,7 @@ export default class HciNode extends HarvesterResource { const disableCPUManager = { action: 'disableCPUManager', - enabled: this.hasAction('disableCPUManager') && !this.isCPUManagerEnableInProgress && this.isCPUManagerEnabled && !this.isEtcd, + enabled: this.cpuPinningFeatureEnabled && this.hasAction('disableCPUManager') && !this.isCPUManagerEnableInProgress && this.isCPUManagerEnabled && !this.isEtcd, icon: 'icon icon-fw icon-os-management', label: this.t('harvester.action.disableCPUManager'), total: 1 @@ -370,6 +370,10 @@ export default class HciNode extends HarvesterResource { ); } + get cpuPinningFeatureEnabled() { + return featureEnabled(this.$rootGetters, 'cpuPinning'); + } + get isCPUManagerEnabled() { return this.metadata?.labels?.[HCI_ANNOTATIONS.CPU_MANAGER] === 'true'; } diff --git a/pkg/harvester/models/kubevirt.io.virtualmachine.js b/pkg/harvester/models/kubevirt.io.virtualmachine.js index b37f2f52..40801a5f 100644 --- a/pkg/harvester/models/kubevirt.io.virtualmachine.js +++ b/pkg/harvester/models/kubevirt.io.virtualmachine.js @@ -14,6 +14,7 @@ import { BACKUP_TYPE } from '../config/types'; import { HCI } from '../types'; import HarvesterResource from './harvester'; import { LVM_DRIVER } from './harvester/storage.k8s.io.storageclass'; +import { featureEnabled } from '../utils/server'; export const OFF = 'Off'; @@ -480,6 +481,10 @@ export default class VirtVm extends HarvesterResource { return null; } + get cpuPinningFeatureEnabled() { + return featureEnabled(this.$rootGetters, 'cpuPinning'); + } + get isCpuPinning() { return this.spec?.template?.spec?.domain?.cpu?.dedicatedCpuPlacement === true; } diff --git a/pkg/harvester/utils/server.js b/pkg/harvester/utils/server.js new file mode 100644 index 00000000..3284cf36 --- /dev/null +++ b/pkg/harvester/utils/server.js @@ -0,0 +1,24 @@ +import semver from 'semver'; +import { HCI } from '../types'; +import { RELEASE_FEATURES } from '../config/feature-flags'; + +export function serverVersion(getters) { + // e.g v1.4.0 + if (process.env.VUE_APP_SERVER_VERSION) { + return process.env.VUE_APP_SERVER_VERSION; + } + + try { + const v = getters['harvester/byId'](HCI.SETTING, 'server-version')?.value; + + return `v${ semver.major(v) }.${ semver.minor(v) }.${ semver.patch(v) }`; + } catch (error) {} + + return ''; +} + +export const featureEnabled = (getters, featureKey) => { + const version = serverVersion(getters); + + return !!RELEASE_FEATURES[version]?.[featureKey] || false; +};