Latest changes from harvester/master

Signed-off-by: Francesco Torchia <francesco.torchia@suse.com>
This commit is contained in:
Francesco Torchia
2024-10-23 17:01:03 +02:00
parent deeccf3db6
commit ec3d88aeb7
56 changed files with 1498 additions and 2197 deletions
@@ -5,6 +5,7 @@ import SYSTEM_NAMESPACES from '@shell/config/system-namespaces';
import { get } from '@shell/utils/object';
import { NAMESPACE } from '@shell/config/types';
import { PRODUCT_NAME as HARVESTER_PRODUCT } from '@pkg/harvester/config/harvester';
import { HCI } from '../../types';
const OBSCURE_NAMESPACE_PREFIX = [
'c-', // cluster namespace
@@ -37,15 +38,32 @@ export default class HciNamespace extends namespace {
weight: -10,
};
const editQuotaAction = {
action: 'editNSQuota',
label: this.t('harvester.modal.quota.editQuota'),
icon: 'icon icon-storage',
enabled: !!this?.actions?.updateResourceQuota && !!this?.actions?.deleteResourceQuota,
weight: -11,
};
if (remove > -1) {
out.splice(remove, 1);
}
insertAt(out, out.length - 1, promptRemove);
insertAt(out, out.length - 5, editQuotaAction);
return out;
}
editNSQuota(resources = this) {
this.$dispatch('promptModal', {
resources,
snapshotSizeQuota: this.snapshotSizeQuota,
component: 'HarvesterQuotaDialog'
});
}
promptRemove(resources = this) {
this.$dispatch('promptModal', {
resources,
@@ -54,6 +72,17 @@ export default class HciNamespace extends namespace {
});
}
get nsResourceQuota() {
const inStore = this.$rootGetters['currentProduct'].inStore;
const allResQuotas = this.$rootGetters[`${ inStore }/all`](HCI.RESOURCE_QUOTA);
return allResQuotas.find( RQ => RQ.metadata.namespace === this.id);
}
get snapshotSizeQuota() {
return this.nsResourceQuota?.spec?.snapshotLimit?.namespaceTotalSnapshotSizeQuota;
}
get isSystem() {
const systemNamespaces = ['fleet-default'];
+50 -5
View File
@@ -59,6 +59,22 @@ export default class HciNode extends HarvesterResource {
total: 1
};
const enableCPUManager = {
action: 'enableCPUManager',
enabled: 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
};
const disableCPUManager = {
action: 'disableCPUManager',
enabled: this.hasAction('disableCPUManager') && !this.isCPUManagerEnableInProgress && this.isCPUManagerEnabled && !this.isEtcd,
icon: 'icon icon-fw icon-os-management',
label: this.t('harvester.action.disableCPUManager'),
total: 1
};
const shutDown = {
action: 'shutDown',
enabled: this.hasAction('powerActionPossible') && this.hasAction('powerAction') && !this.isStopped && !!this.inventory,
@@ -88,6 +104,8 @@ export default class HciNode extends HarvesterResource {
uncordon,
enableMaintenance,
disableMaintenance,
enableCPUManager,
disableCPUManager,
shutDown,
powerOn,
reboot,
@@ -134,15 +152,12 @@ export default class HciNode extends HarvesterResource {
get consoleUrl() {
const url = this.metadata?.annotations?.[HCI_ANNOTATIONS.HOST_CONSOLE_URL];
const validator = /^[a-z]+:\/\//;
if (!url) {
if (!url?.match(validator)) {
return false;
}
if (!url.startsWith('http://') && !url.startsWith('https://')) {
return `http://${ url }`;
}
return url;
}
@@ -308,6 +323,14 @@ export default class HciNode extends HarvesterResource {
this.doAction('disableMaintenanceMode', {});
}
enableCPUManager() {
this.doActionGrowl('enableCPUManager', {});
}
disableCPUManager() {
this.doActionGrowl('disableCPUManager', {});
}
get isUnSchedulable() {
return (
this.metadata?.labels?.[HCI_ANNOTATIONS.NODE_SCHEDULABLE] === 'false' ||
@@ -347,6 +370,28 @@ export default class HciNode extends HarvesterResource {
);
}
get isCPUManagerEnabled() {
return this.metadata?.labels?.[HCI_ANNOTATIONS.CPU_MANAGER] === 'true';
}
get isCPUManagerEnableInProgress() {
return this.cpuManagerUpdateStatus === 'requested' || this.cpuManagerUpdateStatus === 'running';
}
get isCPUManagerEnableFailed() {
return this.cpuManagerUpdateStatus === 'failed';
}
get cpuManagerUpdateStatus() {
try {
const cpuManagerUpdate = JSON.parse(this.metadata.annotations[HCI_ANNOTATIONS.NODE_CPU_MANAGER_UPDATE_STATUS] || '{}');
return cpuManagerUpdate.status || '';
} catch {
return '';
}
}
get longhornDisks() {
const inStore = this.$rootGetters['currentProduct'].inStore;
const longhornNode = this.$rootGetters[`${ inStore }/byId`](
@@ -20,12 +20,10 @@ export default class HciPv extends HarvesterResource {
const storageClassName =
realMode === _CLONE ? this.spec.storageClassName : '';
this['spec'] = {
accessModes,
this['spec'] = {accessModes,
storageClassName,
volumeName: '',
resources: { requests: { storage } }
};
resources: { requests: { storage } }};
}
get availableActions() {
@@ -98,7 +96,6 @@ export default class HciPv extends HarvesterResource {
}
get stateDisplay() {
const ownedBy = this?.metadata?.annotations?.[HCI_ANNOTATIONS.OWNED_BY];
const volumeError = this.relatedPV?.metadata?.annotations?.[HCI_ANNOTATIONS.VOLUME_ERROR];
const degradedVolume = volumeError === DEGRADED_ERROR;
const status = this?.status?.phase === 'Bound' && !volumeError && this.isLonghornVolumeReady ? 'Ready' : 'Not Ready';
@@ -107,7 +104,7 @@ export default class HciPv extends HarvesterResource {
if (findBy(conditions, 'type', 'Resizing')?.status === 'True') {
return 'Resizing';
} else if (ownedBy && !volumeError) {
} else if (!!this.attachVM && !volumeError) {
return 'In-use';
} else if (degradedVolume) {
return 'Degraded';
@@ -179,17 +176,19 @@ export default class HciPv extends HarvesterResource {
}
get attachVM() {
const allVMs = this.$rootGetters['harvester/all'](HCI.VM);
const ownedBy =
get(this, `metadata.annotations."${ HCI_ANNOTATIONS.OWNED_BY }"`) || '';
const allVMs = this.$rootGetters['harvester/all'](HCI.VM) || [];
const findAttachVM = (vm) => {
const attachVolumes = vm.spec.template?.spec?.volumes || [];
if (vm.namespace === this.namespace && attachVolumes.length > 0) {
return attachVolumes.find(vol => vol.persistentVolumeClaim?.claimName === this.name);
}
if (!ownedBy) {
return null;
}
};
const ownedId = JSON.parse(ownedBy)[0]?.refs?.[0];
return allVMs.find(D => D.id === ownedId);
return allVMs.find(findAttachVM);
}
get isAvailable() {
@@ -9,6 +9,10 @@ export default class HciUpgrade extends HarvesterResource {
return this?.metadata?.labels?.[HCI.LATEST_UPGRADE] === 'true';
}
get isUpgradeFailed() {
return this?.metadata?.labels?.[HCI.UPGRADE_STATE] === 'Failed';
}
get isUpgradeSucceeded() {
return this?.metadata?.labels?.[HCI.UPGRADE_STATE] === 'Succeeded';
}
@@ -117,10 +121,10 @@ export default class HciUpgrade extends HarvesterResource {
get upgradeImageMessage() {
const conditions = this?.status?.conditions || [];
const imageReady = conditions.find( cond => cond.type === 'ImageReady');
const hasError = imageReady?.status === 'False';
const success = imageReady?.status === 'True';
const message = imageReady?.message || imageReady?.reason;
return hasError ? message : '';
return success ? '' : message;
}
get nodeUpgradeMessage() {
@@ -47,7 +47,7 @@ export default class HciVmImage extends HarvesterResource {
{
action: 'createFromImage',
enabled: canCreateVM,
icon: 'icon icon-fw icon-spinner',
icon: 'icon icon-circle-plus',
label: this.t('harvester.action.createVM'),
disabled: !this.isReady,
},
@@ -74,7 +74,7 @@ export default class HciVmImage extends HarvesterResource {
router.push({
name: `${ HARVESTER_PRODUCT }-c-cluster-resource-create`,
params: { resource: HCI.VM },
query: { image: this.id }
query: { image: this.id, fromPage: HCI.IMAGE }
});
}
@@ -101,6 +101,10 @@ export default class HciVmImage extends HarvesterResource {
const imported = this.getStatusConditionOfType('Imported');
if (imported?.status === 'Unknown') {
if (this.spec.sourceType === 'restore') {
return 'Restoring';
}
if (this.spec.sourceType === 'download') {
return 'Downloading';
}
@@ -131,7 +135,8 @@ export default class HciVmImage extends HarvesterResource {
const conditions = this?.status?.conditions || [];
const initialized = conditions.find( cond => cond.type === 'Initialized');
const imported = conditions.find( cond => cond.type === 'Imported');
const message = initialized?.message || imported?.message;
const retryLimitExceeded = conditions.find( cond => cond.type === 'RetryLimitExceeded');
const message = initialized?.message || imported?.message || retryLimitExceeded?.message;
return ucFirst(message);
}
@@ -167,6 +172,21 @@ export default class HciVmImage extends HarvesterResource {
});
}
get virtualSize() {
const virtualSize = this.status?.virtualSize;
if (!virtualSize) {
return '-';
}
return formatSi(virtualSize, {
increment: 1024,
maxPrecision: 2,
suffix: 'B',
firstSuffix: 'B',
});
}
getStatusConditionOfType(type, defaultValue = []) {
const conditions = Array.isArray(get(this, 'status.conditions')) ? this.status.conditions : defaultValue;
@@ -133,7 +133,7 @@ export default class VirtVm extends HarvesterResource {
{
action: 'softrebootVM',
enabled: !!this.actions?.softreboot,
icon: 'icon icon-refresh',
icon: 'icon icon-pipeline',
label: this.t('harvester.action.softreboot')
},
{
@@ -152,9 +152,15 @@ export default class VirtVm extends HarvesterResource {
{
action: 'takeVMSnapshot',
enabled: !!this.actions?.backup,
icon: 'icon icon-backup',
icon: 'icon icon-snapshot',
label: this.t('harvester.action.vmSnapshot')
},
{
action: 'editVMQuota',
enabled: !!this.actions?.updateResourceQuota && !!this.actions.deleteResourceQuota,
icon: 'icon icon-storage',
label: this.t('harvester.action.editVMQuota')
},
{
action: 'restoreVM',
enabled: !!this.actions?.restore,
@@ -331,6 +337,14 @@ export default class VirtVm extends HarvesterResource {
});
}
editVMQuota(resources = this) {
this.$dispatch('promptModal', {
resources,
snapshotSizeQuota: this.snapshotSizeQuota,
component: 'HarvesterQuotaDialog'
});
}
unplugVolume(diskName) {
const resources = this;
@@ -541,6 +555,17 @@ export default class VirtVm extends HarvesterResource {
return null;
}
get nsResourceQuota() {
const inStore = this.productInStore;
const allResQuotas = this.$rootGetters[`${ inStore }/all`](HCI.RESOURCE_QUOTA);
return allResQuotas.find( RQ => RQ.namespace === this.metadata.namespace);
}
get snapshotSizeQuota() {
return this.nsResourceQuota?.spec?.snapshotLimit?.vmTotalSnapshotSizeQuota?.[this.metadata.name];
}
get vmi() {
const inStore = this.productInStore;
@@ -646,6 +671,10 @@ export default class VirtVm extends HarvesterResource {
return null;
}
get isTerminating() {
return !!this?.metadata?.deletionTimestamp;
}
get otherState() {
const state = (this.vmi &&
[VMIPhase.Scheduling, VMIPhase.Scheduled].includes(
@@ -685,18 +714,21 @@ export default class VirtVm extends HarvesterResource {
const allRestore = this.$rootGetters[`${ inStore }/all`](HCI.RESTORE);
return allRestore.find(O => O.id === id);
const res = allRestore.find(O => O.id === id);
if (res) {
const allBackups = this.$rootGetters[`${ inStore }/all`](HCI.BACKUP);
res.fromSnapshot = !!allBackups
.filter(b => b.spec?.type !== BACKUP_TYPE.BACKUP)
.find(s => s.id === `${ res.spec?.virtualMachineBackupNamespace }/${ res.spec?.virtualMachineBackupName }`);
}
return res;
}
get restoreProgress() {
const inStore = this.productInStore;
const allBackups = this.$rootGetters[`${ inStore }/all`](HCI.BACKUP);
const isSnapshotRestore = !!allBackups
.filter(b => b.spec?.type !== BACKUP_TYPE.BACKUP)
.find(s => s.id === `${ this.restoreResource?.spec?.virtualMachineBackupNamespace }/${ this.restoreResource?.spec?.virtualMachineBackupName }`);
if (isSnapshotRestore) {
if (this.isVMError || this.isTerminating) {
return {};
}
@@ -726,7 +758,7 @@ export default class VirtVm extends HarvesterResource {
return 'Restoring';
}
if (this?.metadata?.deletionTimestamp) {
if (this.isTerminating) {
return 'Terminating';
}