mirror of
https://github.com/harvester/harvester-ui-extension.git
synced 2026-05-14 15:01:44 +00:00
perf: improve vm list page performance (#835)
* fix: remove unneeded persistentvolumeclaim type label translation key Signed-off-by: Andy Lee <andy.lee@suse.com> * refactor: improve lockIconTooltipMessage call twice Signed-off-by: Andy Lee <andy.lee@suse.com> * refactor: avoid watch allVMs Signed-off-by: Andy Lee <andy.lee@suse.com> * perf: improve the some functions with pre-created map Signed-off-by: Andy Lee <andy.lee@suse.com> * perf: improve the vm list page Signed-off-by: Andy Lee <andy.lee@suse.com> * refactor: AI comment Signed-off-by: Andy Lee <andy.lee@suse.com> * refactor: based on feedback Signed-off-by: Andy Lee <andy.lee@suse.com> --------- Signed-off-by: Andy Lee <andy.lee@suse.com>
This commit is contained in:
parent
8cb793e7ad
commit
032700293c
@ -2165,11 +2165,13 @@ typeLabel:
|
|||||||
one { PCI Device }
|
one { PCI Device }
|
||||||
other { PCI Devices }
|
other { PCI Devices }
|
||||||
}
|
}
|
||||||
|
|
||||||
persistentvolumeclaim: |-
|
persistentvolumeclaim: |-
|
||||||
{count, plural,
|
{count, plural,
|
||||||
one { Volume }
|
one { Volume }
|
||||||
other { Volumes }
|
other { Volumes }
|
||||||
}
|
}
|
||||||
|
|
||||||
network.harvesterhci.io.clusternetwork: |-
|
network.harvesterhci.io.clusternetwork: |-
|
||||||
{count, plural,
|
{count, plural,
|
||||||
one { Cluster Network }
|
one { Cluster Network }
|
||||||
|
|||||||
@ -12,6 +12,11 @@ import { HCI } from '../types';
|
|||||||
import HarvesterVmState from '../formatters/HarvesterVmState';
|
import HarvesterVmState from '../formatters/HarvesterVmState';
|
||||||
import ConsoleBar from '../components/VMConsoleBar';
|
import ConsoleBar from '../components/VMConsoleBar';
|
||||||
|
|
||||||
|
const ENCRYPTED_VOLUME_TOOLTIP_KEYS = {
|
||||||
|
all: 'harvester.virtualMachine.volume.lockTooltip.all',
|
||||||
|
partial: 'harvester.virtualMachine.volume.lockTooltip.partial',
|
||||||
|
};
|
||||||
|
|
||||||
export const VM_HEADERS = [
|
export const VM_HEADERS = [
|
||||||
STATE,
|
STATE,
|
||||||
{
|
{
|
||||||
@ -163,6 +168,12 @@ export default {
|
|||||||
*/
|
*/
|
||||||
hasBackUpRestoreInProgress() {
|
hasBackUpRestoreInProgress() {
|
||||||
return !!this.rows.find((r) => r.restoreResource && !r.restoreResource.fromSnapshot && !r.restoreResource.isComplete);
|
return !!this.rows.find((r) => r.restoreResource && !r.restoreResource.fromSnapshot && !r.restoreResource.isComplete);
|
||||||
|
},
|
||||||
|
|
||||||
|
vmRestartRequiredNames() {
|
||||||
|
return this.allVMs
|
||||||
|
.filter((vm) => vm.isRestartRequired)
|
||||||
|
.map((vm) => vm.metadata.name);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -181,15 +192,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
allVMs: {
|
vmRestartRequiredNames(vmNames) {
|
||||||
handler(neu) {
|
|
||||||
const vmNames = [];
|
|
||||||
|
|
||||||
neu.forEach((vm) => {
|
|
||||||
if (vm.isRestartRequired) {
|
|
||||||
vmNames.push(vm.metadata.name);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const count = vmNames.length;
|
const count = vmNames.length;
|
||||||
|
|
||||||
if (count === 0 && this.restartNotificationDisplayed) {
|
if (count === 0 && this.restartNotificationDisplayed) {
|
||||||
@ -203,9 +206,7 @@ export default {
|
|||||||
if (this.restartNotificationDisplayed) {
|
if (this.restartNotificationDisplayed) {
|
||||||
this.$store.dispatch('growl/clear');
|
this.$store.dispatch('growl/clear');
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (count > 0 && vmNames.length > 0) {
|
|
||||||
this.$store.dispatch('growl/warning', {
|
this.$store.dispatch('growl/warning', {
|
||||||
title: this.t('harvester.notification.restartRequired.title', { count }),
|
title: this.t('harvester.notification.restartRequired.title', { count }),
|
||||||
message: this.t('harvester.notification.restartRequired.message', { vmNames: vmNames.join(', ') }),
|
message: this.t('harvester.notification.restartRequired.message', { vmNames: vmNames.join(', ') }),
|
||||||
@ -213,21 +214,13 @@ export default {
|
|||||||
}, { root: true });
|
}, { root: true });
|
||||||
this.restartNotificationDisplayed = true;
|
this.restartNotificationDisplayed = true;
|
||||||
}
|
}
|
||||||
},
|
|
||||||
deep: true,
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
lockIconTooltipMessage(row) {
|
lockIconTooltipMessage(row) {
|
||||||
const message = '';
|
const key = ENCRYPTED_VOLUME_TOOLTIP_KEYS[row.encryptedVolumeType];
|
||||||
|
|
||||||
if (row.encryptedVolumeType === 'all') {
|
return key ? this.t(key) : '';
|
||||||
return this.t('harvester.virtualMachine.volume.lockTooltip.all');
|
|
||||||
} else if (row.encryptedVolumeType === 'partial') {
|
|
||||||
return this.t('harvester.virtualMachine.volume.lockTooltip.partial');
|
|
||||||
}
|
|
||||||
|
|
||||||
return message;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -267,7 +260,7 @@ export default {
|
|||||||
>
|
>
|
||||||
{{ scope.row.metadata.name }}
|
{{ scope.row.metadata.name }}
|
||||||
<i
|
<i
|
||||||
v-if="lockIconTooltipMessage(scope.row)"
|
v-if="scope.row.encryptedVolumeType !== 'none'"
|
||||||
v-tooltip="lockIconTooltipMessage(scope.row)"
|
v-tooltip="lockIconTooltipMessage(scope.row)"
|
||||||
class="icon icon-lock"
|
class="icon icon-lock"
|
||||||
:class="{'green-icon': scope.row.encryptedVolumeType === 'all', 'yellow-icon': scope.row.encryptedVolumeType === 'partial'}"
|
:class="{'green-icon': scope.row.encryptedVolumeType === 'all', 'yellow-icon': scope.row.encryptedVolumeType === 'partial'}"
|
||||||
|
|||||||
@ -83,6 +83,42 @@ const VMIPhase = {
|
|||||||
|
|
||||||
let productInStore;
|
let productInStore;
|
||||||
|
|
||||||
|
let _podOwnerMap = null;
|
||||||
|
let _podOwnerMapSource = null;
|
||||||
|
|
||||||
|
function getPodByOwnerName(rootGetters, inStore, ownerName) {
|
||||||
|
const podList = rootGetters[`${ inStore }/all`](POD);
|
||||||
|
|
||||||
|
if (!Array.isArray(podList)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
// if not equals (usually means the pod list has been updated), we need to rebuild the map, otherwise we can reuse the map for better performance
|
||||||
|
if (_podOwnerMapSource !== podList) {
|
||||||
|
_podOwnerMap = new Map(); // use Map to store ownerReference name and pod mapping
|
||||||
|
for (const pod of podList) {
|
||||||
|
const refName = pod.metadata?.ownerReferences?.[0]?.name;
|
||||||
|
|
||||||
|
if (refName) {
|
||||||
|
_podOwnerMap.set(refName, pod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_podOwnerMapSource = podList;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _podOwnerMap.get(ownerName);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPvcsByNames(rootGetters, inStore, names) {
|
||||||
|
const pvcList = rootGetters[`${ inStore }/all`](PVC);
|
||||||
|
|
||||||
|
if (!Array.isArray(pvcList)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const uniqueNames = new Set(names);
|
||||||
|
|
||||||
|
return pvcList.filter((pvc) => uniqueNames.has(pvc.metadata?.name));
|
||||||
|
}
|
||||||
|
|
||||||
const IgnoreMessages = ['pod has unbound immediate PersistentVolumeClaims'];
|
const IgnoreMessages = ['pod has unbound immediate PersistentVolumeClaims'];
|
||||||
|
|
||||||
export default class VirtVm extends HarvesterResource {
|
export default class VirtVm extends HarvesterResource {
|
||||||
@ -660,16 +696,13 @@ export default class VirtVm extends HarvesterResource {
|
|||||||
|
|
||||||
get podResource() {
|
get podResource() {
|
||||||
const inStore = this.productInStore;
|
const inStore = this.productInStore;
|
||||||
|
|
||||||
const vmiResource = this.$rootGetters[`${ inStore }/byId`](HCI.VMI, this.id);
|
const vmiResource = this.$rootGetters[`${ inStore }/byId`](HCI.VMI, this.id);
|
||||||
const podList = this.$rootGetters[`${ inStore }/all`](POD);
|
|
||||||
|
|
||||||
return podList.find((P) => {
|
if (!vmiResource?.metadata?.name) {
|
||||||
return (
|
return undefined;
|
||||||
vmiResource?.metadata?.name &&
|
}
|
||||||
vmiResource?.metadata?.name === P.metadata?.ownerReferences?.[0].name
|
|
||||||
);
|
return getPodByOwnerName(this.$rootGetters, inStore, vmiResource.metadata.name);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get isPaused() {
|
get isPaused() {
|
||||||
@ -710,17 +743,13 @@ export default class VirtVm extends HarvesterResource {
|
|||||||
get vmi() {
|
get vmi() {
|
||||||
const inStore = this.productInStore;
|
const inStore = this.productInStore;
|
||||||
|
|
||||||
const vmis = this.$rootGetters[`${ inStore }/all`](HCI.VMI);
|
return this.$rootGetters[`${ inStore }/byId`](HCI.VMI, this.id);
|
||||||
|
|
||||||
return vmis.find((VMI) => VMI.id === this.id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get volumes() {
|
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 volumeClaimNames = this.spec.template.spec.volumes?.map((v) => v.persistentVolumeClaim?.claimName).filter((v) => !!v) || [];
|
||||||
|
|
||||||
return pvcs.filter((pvc) => volumeClaimNames.includes(pvc.metadata.name));
|
return getPvcsByNames(this.$rootGetters, this.productInStore, volumeClaimNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
get lvmVolumes() {
|
get lvmVolumes() {
|
||||||
@ -753,17 +782,6 @@ export default class VirtVm extends HarvesterResource {
|
|||||||
return { status: 'VMI error', detailedMessage: vmiFailureCond.message };
|
return { status: 'VMI error', detailedMessage: vmiFailureCond.message };
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((this.vmi || this.isVMCreated) && this.podResource) {
|
|
||||||
// const podStatus = this.podResource.getPodStatus;
|
|
||||||
// if (POD_STATUS_ALL_ERROR.includes(podStatus?.status)) {
|
|
||||||
// return {
|
|
||||||
// ...podStatus,
|
|
||||||
// status: 'LAUNCHER_POD_ERROR',
|
|
||||||
// pod: this.podResource,
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
return this?.vmi?.status?.phase;
|
return this?.vmi?.status?.phase;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -901,9 +919,7 @@ export default class VirtVm extends HarvesterResource {
|
|||||||
|
|
||||||
const inStore = this.productInStore;
|
const inStore = this.productInStore;
|
||||||
|
|
||||||
const allRestore = this.$rootGetters[`${ inStore }/all`](HCI.RESTORE);
|
const res = this.$rootGetters[`${ inStore }/byId`](HCI.RESTORE, id);
|
||||||
|
|
||||||
const res = allRestore.find((O) => O.id === id);
|
|
||||||
|
|
||||||
if (res) {
|
if (res) {
|
||||||
const allBackups = this.$rootGetters[`${ inStore }/all`](HCI.BACKUP);
|
const allBackups = this.$rootGetters[`${ inStore }/all`](HCI.BACKUP);
|
||||||
@ -1073,42 +1089,6 @@ export default class VirtVm extends HarvesterResource {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
get warningCount() {
|
|
||||||
return this.resourcesStatus.warningCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
get errorCount() {
|
|
||||||
return this.resourcesStatus.errorCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
get resourcesStatus() {
|
|
||||||
const inStore = this.productInStore;
|
|
||||||
const vmList = this.$rootGetters[`${ inStore }/all`](HCI.VM);
|
|
||||||
let warningCount = 0;
|
|
||||||
let errorCount = 0;
|
|
||||||
|
|
||||||
vmList.forEach((vm) => {
|
|
||||||
const status = vm.actualState;
|
|
||||||
|
|
||||||
if (status === VM_ERROR) {
|
|
||||||
errorCount += 1;
|
|
||||||
} else if (
|
|
||||||
status === 'Stopping' ||
|
|
||||||
status === 'Waiting' ||
|
|
||||||
status === 'Pending' ||
|
|
||||||
status === 'Starting' ||
|
|
||||||
status === 'Terminating'
|
|
||||||
) {
|
|
||||||
warningCount += 1;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
warningCount,
|
|
||||||
errorCount
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
get volumeClaimTemplates() {
|
get volumeClaimTemplates() {
|
||||||
return parseVolumeClaimTemplates(this);
|
return parseVolumeClaimTemplates(this);
|
||||||
}
|
}
|
||||||
@ -1126,7 +1106,6 @@ export default class VirtVm extends HarvesterResource {
|
|||||||
get rootImageId() {
|
get rootImageId() {
|
||||||
let imageId = '';
|
let imageId = '';
|
||||||
const inStore = this.productInStore;
|
const inStore = this.productInStore;
|
||||||
const pvcs = this.$rootGetters[`${ inStore }/all`](PVC) || [];
|
|
||||||
|
|
||||||
const volumes = this.spec.template.spec.volumes || [];
|
const volumes = this.spec.template.spec.volumes || [];
|
||||||
|
|
||||||
@ -1136,9 +1115,7 @@ export default class VirtVm extends HarvesterResource {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!isNoExistingVolume) {
|
if (!isNoExistingVolume) {
|
||||||
const existingVolume = pvcs.find(
|
const existingVolume = this.$rootGetters[`${ inStore }/byId`](PVC, `${ this.metadata.namespace }/${ firstVolumeName }`);
|
||||||
(P) => P.id === `${ this.metadata.namespace }/${ firstVolumeName }`
|
|
||||||
);
|
|
||||||
|
|
||||||
if (existingVolume) {
|
if (existingVolume) {
|
||||||
return existingVolume?.metadata?.annotations?.[
|
return existingVolume?.metadata?.annotations?.[
|
||||||
@ -1316,8 +1293,7 @@ export default class VirtVm extends HarvesterResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get isBackupTargetUnavailable() {
|
get isBackupTargetUnavailable() {
|
||||||
const allSettings = this.$rootGetters['harvester/all'](HCI.SETTING) || [];
|
const backupTargetSetting = this.$rootGetters['harvester/byId'](HCI.SETTING, 'backup-target');
|
||||||
const backupTargetSetting = allSettings.find( (O) => O.id === 'backup-target');
|
|
||||||
|
|
||||||
return isBackupTargetSettingUnavailable(backupTargetSetting);
|
return isBackupTargetSettingUnavailable(backupTargetSetting);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user