mirror of
https://github.com/harvester/harvester-ui-extension.git
synced 2026-10-01 03:36:04 +08:00
Latest changes from Harvester master - d7d9d4af8a88d677695d7aff47a81d52041dfcca
Signed-off-by: Francesco Torchia <francesco.torchia@suse.com>
This commit is contained in:
@@ -489,7 +489,7 @@ export default class HciNode extends HarvesterResource {
|
||||
get unProvisionedDisks() {
|
||||
const blockDevices = this.blockDevices || [];
|
||||
|
||||
return blockDevices.filter(d => d?.spec?.fileSystem?.provisioned && d?.status?.provisionPhase !== 'Provisioned');
|
||||
return blockDevices.filter(d => d?.isProvisioned && d?.status?.provisionPhase !== 'Provisioned');
|
||||
}
|
||||
|
||||
get diskStatusCount() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { _CLONE } from '@shell/config/query-params';
|
||||
import pick from 'lodash/pick';
|
||||
import { PV, LONGHORN } from '@shell/config/types';
|
||||
import { PV, LONGHORN, STORAGE_CLASS } from '@shell/config/types';
|
||||
import { DESCRIPTION } from '@shell/config/labels-annotations';
|
||||
import { HCI as HCI_ANNOTATIONS } from '@pkg/harvester/config/labels-annotations';
|
||||
import { findBy } from '@shell/utils/array';
|
||||
@@ -10,7 +10,10 @@ import { HCI, VOLUME_SNAPSHOT } from '../../types';
|
||||
import HarvesterResource from '../harvester';
|
||||
import { PRODUCT_NAME as HARVESTER_PRODUCT } from '../../config/harvester';
|
||||
|
||||
const DEGRADED_ERROR = 'replica scheduling failed';
|
||||
import { LONGHORN_DRIVER } from '@shell/config/types';
|
||||
import { DATA_ENGINE_V2 } from '../../edit/harvesterhci.io.storage/index.vue';
|
||||
|
||||
const DEGRADED_ERRORS = ['replica scheduling failed', 'precheck new replica failed'];
|
||||
|
||||
export default class HciPv extends HarvesterResource {
|
||||
applyDefaults(_, realMode) {
|
||||
@@ -27,32 +30,44 @@ export default class HciPv extends HarvesterResource {
|
||||
}
|
||||
|
||||
get availableActions() {
|
||||
const out = super._availableActions;
|
||||
const clone = out.find(action => action.action === 'goToClone');
|
||||
let out = super._availableActions;
|
||||
|
||||
if (clone) {
|
||||
clone.action = 'goToCloneVolume';
|
||||
// Longhorn V2 provisioner do not support volume clone feature yet
|
||||
if (this.storageClass.longhornVersion === DATA_ENGINE_V2) {
|
||||
out = out.filter(action => action.action !== 'goToClone');
|
||||
} else {
|
||||
const clone = out.find(action => action.action === 'goToClone');
|
||||
|
||||
if (clone) {
|
||||
clone.action = 'goToCloneVolume';
|
||||
}
|
||||
}
|
||||
|
||||
if (this.storageClass.provisioner !== LONGHORN_DRIVER || this.storageClass.longhornVersion !== DATA_ENGINE_V2) {
|
||||
out = [
|
||||
{
|
||||
action: 'exportImage',
|
||||
enabled: this.hasAction('export') && !this.isEncrypted,
|
||||
icon: 'icon icon-copy',
|
||||
label: this.t('harvester.action.exportImage')
|
||||
},
|
||||
{
|
||||
action: 'snapshot',
|
||||
enabled: this.hasAction('snapshot'),
|
||||
icon: 'icon icon-backup',
|
||||
label: this.t('harvester.action.snapshot'),
|
||||
},
|
||||
...out
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
action: 'exportImage',
|
||||
enabled: this.hasAction('export') && !this.isEncrypted,
|
||||
icon: 'icon icon-copy',
|
||||
label: this.t('harvester.action.exportImage')
|
||||
},
|
||||
{
|
||||
action: 'cancelExpand',
|
||||
enabled: this.hasAction('cancelExpand'),
|
||||
icon: 'icon icon-backup',
|
||||
label: this.t('harvester.action.cancelExpand')
|
||||
},
|
||||
{
|
||||
action: 'snapshot',
|
||||
enabled: this.hasAction('snapshot'),
|
||||
icon: 'icon icon-backup',
|
||||
label: this.t('harvester.action.snapshot'),
|
||||
},
|
||||
...out
|
||||
];
|
||||
}
|
||||
@@ -91,13 +106,19 @@ export default class HciPv extends HarvesterResource {
|
||||
this.metadata.annotations = pick(this.metadata.annotations, keys);
|
||||
}
|
||||
|
||||
get storageClass() {
|
||||
const inStore = this.$rootGetters['currentProduct'].inStore;
|
||||
|
||||
return this.$rootGetters[`${ inStore }/all`](STORAGE_CLASS).find(sc => sc.name === this.spec.storageClassName);
|
||||
}
|
||||
|
||||
get canUpdate() {
|
||||
return this.hasLink('update');
|
||||
}
|
||||
|
||||
get stateDisplay() {
|
||||
const volumeError = this.relatedPV?.metadata?.annotations?.[HCI_ANNOTATIONS.VOLUME_ERROR];
|
||||
const degradedVolume = volumeError === DEGRADED_ERROR;
|
||||
const degradedVolume = DEGRADED_ERRORS.includes(volumeError);
|
||||
const status = this?.status?.phase === 'Bound' && !volumeError && this.isLonghornVolumeReady ? 'Ready' : 'Not Ready';
|
||||
|
||||
const conditions = this?.status?.conditions || [];
|
||||
@@ -116,7 +137,7 @@ export default class HciPv extends HarvesterResource {
|
||||
// state is similar with stateDisplay, the reason we keep this property is the status of In-use should not be displayed on vm detail page
|
||||
get state() {
|
||||
const volumeError = this.relatedPV?.metadata?.annotations?.[HCI_ANNOTATIONS.VOLUME_ERROR];
|
||||
const degradedVolume = volumeError === DEGRADED_ERROR;
|
||||
const degradedVolume = DEGRADED_ERRORS.includes(volumeError);
|
||||
let status = this?.status?.phase === 'Bound' && !volumeError ? 'Ready' : 'Not Ready';
|
||||
|
||||
const conditions = this?.status?.conditions || [];
|
||||
|
||||
@@ -2,6 +2,10 @@ import { clone } from '@shell/utils/object';
|
||||
import StorageClass from '@shell/models/storage.k8s.io.storageclass';
|
||||
import { HCI } from '../../types';
|
||||
import { PRODUCT_NAME as HARVESTER_PRODUCT } from '../../config/harvester';
|
||||
import { LONGHORN_DRIVER } from '@shell/config/types';
|
||||
import { DATA_ENGINE_V1, DATA_ENGINE_V2 } from '../../edit/harvesterhci.io.storage/index.vue';
|
||||
|
||||
export const LVM_DRIVER = 'lvm.driver.harvesterhci.io';
|
||||
|
||||
export default class HciStorageClass extends StorageClass {
|
||||
get detailLocation() {
|
||||
@@ -31,4 +35,30 @@ export default class HciStorageClass extends StorageClass {
|
||||
get parentNameOverride() {
|
||||
return this.$rootGetters['i18n/t'](`typeLabel."${ HCI.STORAGE }"`, { count: 1 })?.trim();
|
||||
}
|
||||
|
||||
get longhornVersion() {
|
||||
if (this.provisioner === LONGHORN_DRIVER) {
|
||||
return (this.parameters || {}).dataEngine || DATA_ENGINE_V1;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
get provisionerDisplay() {
|
||||
let key = '';
|
||||
|
||||
if (this.provisioner === LONGHORN_DRIVER) {
|
||||
key = `harvester.storage.storageClass.longhorn.${ this.longhornVersion }.label`;
|
||||
}
|
||||
|
||||
if (this.provisioner === LVM_DRIVER) {
|
||||
key = `harvester.storage.storageClass.lvm.label`;
|
||||
}
|
||||
|
||||
return this.$rootGetters['i18n/t'](key);
|
||||
}
|
||||
|
||||
get isLonghornV2() {
|
||||
return this.provisioner === LONGHORN_DRIVER && this.longhornVersion === DATA_ENGINE_V2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import jsyaml from 'js-yaml';
|
||||
import startCase from 'lodash/startCase';
|
||||
import { HCI as HCI_ANNOTATIONS } from '../config/labels-annotations';
|
||||
import HarvesterResource from './harvester';
|
||||
import { HCI } from '../types';
|
||||
|
||||
export default class HciAddonConfig extends HarvesterResource {
|
||||
get availableActions() {
|
||||
@@ -106,6 +107,10 @@ export default class HciAddonConfig extends HarvesterResource {
|
||||
return failedCondition?.message || super.stateDescription;
|
||||
}
|
||||
|
||||
get parentNameOverride() {
|
||||
return this.$rootGetters['i18n/t'](`typeLabel."${ HCI.ADD_ONS }"`, { count: 1 })?.trim();
|
||||
}
|
||||
|
||||
get displayName() {
|
||||
const isExperimental = this.metadata?.labels?.[HCI_ANNOTATIONS.ADDON_EXPERIMENTAL] === 'true';
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ export default class HciBlockDevice extends HarvesterResource {
|
||||
}
|
||||
|
||||
get isChildPartProvisioned() {
|
||||
const parts = this.childParts.filter(p => p.spec?.fileSystem?.provisioned) || [];
|
||||
const parts = this.childParts.filter(p => p.isProvisioned) || [];
|
||||
|
||||
return parts.length > 0;
|
||||
}
|
||||
@@ -59,4 +59,9 @@ export default class HciBlockDevice extends HarvesterResource {
|
||||
|
||||
return formatting.status === 'True';
|
||||
}
|
||||
|
||||
get isProvisioned() {
|
||||
// spec.fileSystem.provisioned is deprecated
|
||||
return this.spec?.fileSystem?.provisioned || this.spec?.provision;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { HARVESTER_NAME, HARVESTER_NAME as VIRTUAL } from '@shell/config/feature
|
||||
import { SETTING } from '@shell/config/settings';
|
||||
|
||||
export default class HciCluster extends ProvCluster {
|
||||
|
||||
get stateObj() {
|
||||
return this._stateObj;
|
||||
}
|
||||
@@ -29,10 +30,6 @@ export default class HciCluster extends ProvCluster {
|
||||
return false;
|
||||
}
|
||||
|
||||
cachedHarvesterClusterVersion = '';
|
||||
|
||||
_uiInfo = undefined;
|
||||
|
||||
/**
|
||||
* Fetch and cache the response for /ui-info
|
||||
*
|
||||
|
||||
@@ -13,6 +13,7 @@ import { parseVolumeClaimTemplates } from '@pkg/utils/vm';
|
||||
import { BACKUP_TYPE } from '../config/types';
|
||||
import { HCI } from '../types';
|
||||
import HarvesterResource from './harvester';
|
||||
import { LVM_DRIVER } from './harvester/storage.k8s.io.storageclass';
|
||||
|
||||
export const OFF = 'Off';
|
||||
|
||||
@@ -87,12 +88,17 @@ const IgnoreMessages = ['pod has unbound immediate PersistentVolumeClaims'];
|
||||
|
||||
export default class VirtVm extends HarvesterResource {
|
||||
get availableActions() {
|
||||
const out = super._availableActions;
|
||||
let out = super._availableActions;
|
||||
|
||||
const clone = out.find(action => action.action === 'goToClone');
|
||||
// VM attached with Longhorn V2 volume doesn't support clone feature
|
||||
if (this.longhornV2Volumes.length > 0) {
|
||||
out = out.filter(action => action.action !== 'goToClone');
|
||||
} else {
|
||||
const clone = out.find(action => action.action === 'goToClone');
|
||||
|
||||
if (clone) {
|
||||
clone.action = 'goToCloneVM';
|
||||
if (clone) {
|
||||
clone.action = 'goToCloneVM';
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
@@ -150,7 +156,7 @@ export default class VirtVm extends HarvesterResource {
|
||||
},
|
||||
{
|
||||
action: 'takeVMSnapshot',
|
||||
enabled: !!this.actions?.backup,
|
||||
enabled: !!this.actions?.backup && !this.longhornV2Volumes.length,
|
||||
icon: 'icon icon-snapshot',
|
||||
label: this.t('harvester.action.vmSnapshot')
|
||||
},
|
||||
@@ -593,16 +599,26 @@ export default class VirtVm extends HarvesterResource {
|
||||
return vmis.find(VMI => VMI.id === this.id);
|
||||
}
|
||||
|
||||
get encryptedVolumeType() {
|
||||
const inStore = this.productInStore;
|
||||
const pvcs = this.$rootGetters[`${ inStore }/all`](PVC);
|
||||
get volumes() {
|
||||
const pvcs = this.$rootGetters[`${ this.productInStore }/all`](PVC);
|
||||
|
||||
const volumeClaimNames = this.spec.template.spec.volumes?.map(v => v.persistentVolumeClaim?.claimName).filter(v => !!v) || [];
|
||||
const volumes = pvcs.filter(pvc => volumeClaimNames.includes(pvc.metadata.name));
|
||||
|
||||
if (volumes.every(vol => vol.isEncrypted)) {
|
||||
return pvcs.filter(pvc => volumeClaimNames.includes(pvc.metadata.name));
|
||||
}
|
||||
|
||||
get lvmVolumes() {
|
||||
return this.volumes.filter(volume => volume.storageClass.provisioner === LVM_DRIVER);
|
||||
}
|
||||
|
||||
get longhornV2Volumes() {
|
||||
return this.volumes.filter(volume => volume.storageClass.isLonghornV2);
|
||||
}
|
||||
|
||||
get encryptedVolumeType() {
|
||||
if (this.volumes.every(vol => vol.isEncrypted)) {
|
||||
return 'all';
|
||||
} else if (volumes.some(vol => vol.isEncrypted)) {
|
||||
} else if (this.volumes.some(vol => vol.isEncrypted)) {
|
||||
return 'partial';
|
||||
} else {
|
||||
return 'none';
|
||||
|
||||
Reference in New Issue
Block a user