From 4f08ff44aad6eba453cb203e0db26ba11819acc7 Mon Sep 17 00:00:00 2001 From: mufazalov Date: Wed, 30 Oct 2024 16:50:19 +0300 Subject: [PATCH 1/2] fix: use both BSC and Whiteboard for disks --- src/components/PDiskPopup/PDiskPopup.tsx | 26 +- src/components/VDiskPopup/VDiskPopup.tsx | 16 +- src/containers/Storage/Disks/Disks.tsx | 3 +- src/store/reducers/node/selectors.ts | 9 +- src/store/reducers/node/types.ts | 7 +- src/store/reducers/pdisk/utils.ts | 14 +- .../__test__/prepareGroupsDisks.test.ts | 414 ++++++++++++++++++ .../reducers/storage/prepareGroupsDisks.ts | 90 ++++ src/store/reducers/storage/utils.ts | 51 +-- src/store/reducers/vdisk/utils.ts | 9 +- src/types/api/pdisk.ts | 2 +- src/types/api/storage.ts | 4 +- .../__test__/calculatePDiskSeverity.test.ts | 17 +- .../__test__/calculateVDiskSeverity.test.ts | 31 +- src/utils/disks/__test__/prepareDisks.test.ts | 238 ++++++++++ src/utils/disks/calculatePDiskSeverity.ts | 11 +- src/utils/disks/calculateVDiskSeverity.ts | 16 +- src/utils/disks/helpers.ts | 5 +- src/utils/disks/prepareDisks.ts | 157 +++++-- src/utils/disks/types.ts | 19 +- 20 files changed, 976 insertions(+), 163 deletions(-) create mode 100644 src/store/reducers/storage/__test__/prepareGroupsDisks.test.ts create mode 100644 src/store/reducers/storage/prepareGroupsDisks.ts create mode 100644 src/utils/disks/__test__/prepareDisks.test.ts diff --git a/src/components/PDiskPopup/PDiskPopup.tsx b/src/components/PDiskPopup/PDiskPopup.tsx index 30dca66fe..d9dc41bb0 100644 --- a/src/components/PDiskPopup/PDiskPopup.tsx +++ b/src/components/PDiskPopup/PDiskPopup.tsx @@ -6,7 +6,6 @@ import {EFlag} from '../../types/api/enums'; import {valueIsDefined} from '../../utils'; import {EMPTY_DATA_PLACEHOLDER} from '../../utils/constants'; import {createPDiskDeveloperUILink} from '../../utils/developerUI/developerUI'; -import {getPDiskId} from '../../utils/disks/helpers'; import type {PreparedPDisk} from '../../utils/disks/types'; import {useTypedSelector} from '../../utils/hooks'; import {bytesToGB} from '../../utils/utils'; @@ -21,12 +20,23 @@ export const preparePDiskData = ( nodeHost?: string, withDeveloperUILink?: boolean, ) => { - const {AvailableSize, TotalSize, State, PDiskId, NodeId, Path, Realtime, Type, Device} = data; + const { + AvailableSize, + TotalSize, + State, + PDiskId, + NodeId, + StringifiedId, + Path, + Realtime, + Type, + Device, + } = data; const pdiskData: InfoViewerItem[] = [ { label: 'PDisk', - value: getPDiskId(NodeId, PDiskId) ?? EMPTY_DATA_PLACEHOLDER, + value: StringifiedId ?? EMPTY_DATA_PLACEHOLDER, }, {label: 'State', value: State || 'not available'}, {label: 'Type', value: Type || 'unknown'}, @@ -44,10 +54,12 @@ export const preparePDiskData = ( pdiskData.push({label: 'Path', value: Path}); } - pdiskData.push({ - label: 'Available', - value: `${bytesToGB(AvailableSize)} of ${bytesToGB(TotalSize)}`, - }); + if (!isNaN(Number(TotalSize))) { + pdiskData.push({ + label: 'Available', + value: `${bytesToGB(AvailableSize)} of ${bytesToGB(TotalSize)}`, + }); + } if (Realtime && errorColors.includes(Realtime)) { pdiskData.push({label: 'Realtime', value: Realtime}); diff --git a/src/components/VDiskPopup/VDiskPopup.tsx b/src/components/VDiskPopup/VDiskPopup.tsx index 74314044c..8181bf015 100644 --- a/src/components/VDiskPopup/VDiskPopup.tsx +++ b/src/components/VDiskPopup/VDiskPopup.tsx @@ -8,7 +8,6 @@ import {EFlag} from '../../types/api/enums'; import {valueIsDefined} from '../../utils'; import {cn} from '../../utils/cn'; import {EMPTY_DATA_PLACEHOLDER} from '../../utils/constants'; -import {stringifyVdiskId} from '../../utils/dataFormatters/dataFormatters'; import {createVDiskDeveloperUILink} from '../../utils/developerUI/developerUI'; import {isFullVDiskData} from '../../utils/disks/helpers'; import type {PreparedVDisk, UnavailableDonor} from '../../utils/disks/types'; @@ -203,22 +202,9 @@ export const VDiskPopup = ({data}: VDiskPopupProps) => { if ('Donors' in data && data.Donors) { const donors = data.Donors; for (const donor of donors) { - const isFullDonorData = isFullVDiskData(donor); donorsInfo.push({ label: 'VDisk', - value: ( - - {stringifyVdiskId( - isFullDonorData - ? donor.VDiskId - : { - NodeId: donor.NodeId, - PDiskId: donor.PDiskId, - VSlotId: donor.VSlotId, - }, - )} - - ), + value: {donor.StringifiedId}, }); } } diff --git a/src/containers/Storage/Disks/Disks.tsx b/src/containers/Storage/Disks/Disks.tsx index c4e6ce509..c450fc3ed 100644 --- a/src/containers/Storage/Disks/Disks.tsx +++ b/src/containers/Storage/Disks/Disks.tsx @@ -5,7 +5,6 @@ import {Flex, useLayoutContext} from '@gravity-ui/uikit'; import {VDisk} from '../../../components/VDisk/VDisk'; import {valueIsDefined} from '../../../utils'; import {cn} from '../../../utils/cn'; -import {getPDiskId} from '../../../utils/disks/helpers'; import type {PreparedVDisk} from '../../../utils/disks/types'; import {PDisk} from '../PDisk'; import type {StorageViewContext} from '../types'; @@ -54,7 +53,7 @@ export function Disks({vDisks = [], viewContext}: DisksProps) {
{vDisks?.map((vDisk) => ( { const vDisks = group.VDisks?.filter((el) => el.NodeId === nodeId).map( - prepareVDiskData, + prepareWhiteboardVDiskData, ); vDisks?.forEach((vd) => { const vDiskId = stringifyVdiskId(vd.VDiskId); - const preparedPDisk = preparePDiskData(vd.PDisk); - const pDiskId = preparedPDisk.PDiskId; + const pDiskId = vd.PDisk?.PDiskId; if (!structure[String(pDiskId)]) { - structure[String(pDiskId)] = {vDisks: {}, ...preparedPDisk}; + structure[String(pDiskId)] = {vDisks: {}, ...vd.PDisk}; } structure[String(pDiskId)].vDisks[vDiskId] = { ...vd, diff --git a/src/store/reducers/node/types.ts b/src/store/reducers/node/types.ts index b2e9cfc23..ef7df529e 100644 --- a/src/store/reducers/node/types.ts +++ b/src/store/reducers/node/types.ts @@ -1,14 +1,13 @@ -import type {TVDiskStateInfo} from '../../../types/api/vdisk'; -import type {PreparedPDisk} from '../../../utils/disks/types'; +import type {PreparedPDisk, PreparedVDisk} from '../../../utils/disks/types'; import type {PreparedNodeSystemState} from '../../../utils/nodes'; interface RawStructurePDisk extends PreparedPDisk { - vDisks: Record; + vDisks: Record; } export type RawNodeStructure = Record; -export interface PreparedStructureVDisk extends TVDiskStateInfo { +export interface PreparedStructureVDisk extends PreparedVDisk { id: string; order: number; } diff --git a/src/store/reducers/pdisk/utils.ts b/src/store/reducers/pdisk/utils.ts index f493a190d..41e9d8c00 100644 --- a/src/store/reducers/pdisk/utils.ts +++ b/src/store/reducers/pdisk/utils.ts @@ -1,7 +1,10 @@ import type {TPDiskInfoResponse} from '../../../types/api/pdisk'; import type {TEvSystemStateResponse} from '../../../types/api/systemState'; import {getArray, valueIsDefined} from '../../../utils'; -import {preparePDiskData, prepareVDiskData} from '../../../utils/disks/prepareDisks'; +import { + prepareWhiteboardPDiskData, + prepareWhiteboardVDiskData, +} from '../../../utils/disks/prepareDisks'; import {prepareNodeSystemState} from '../../../utils/nodes'; import type {PDiskData, SlotItem} from './types'; @@ -18,7 +21,10 @@ export function preparePDiskDataResponse([pdiskResponse = {}, nodeResponse]: [ const {PDisk: WhiteboardPDiskData = {}, VDisks: WhiteboardVDisksData = []} = Whiteboard; const {PDisk: BSCPDiskData = {}} = BSC; - const preparedPDisk = preparePDiskData(WhiteboardPDiskData, BSCPDiskData); + const preparedPDisk = prepareWhiteboardPDiskData({ + ...BSCPDiskData, + ...WhiteboardPDiskData, + }); const NodeId = preparedPDisk.NodeId ?? preparedNode.NodeId; @@ -48,7 +54,9 @@ export function preparePDiskDataResponse([pdiskResponse = {}, nodeResponse]: [ }; } - const preparedVDisks = WhiteboardVDisksData.map((disk) => prepareVDiskData({...disk, NodeId})); + const preparedVDisks = WhiteboardVDisksData.map((disk) => + prepareWhiteboardVDiskData({...disk, NodeId}), + ); preparedVDisks.sort((disk1, disk2) => Number(disk2.VDiskSlotId) - Number(disk1.VDiskSlotId)); const vdisksSlots: SlotItem<'vDisk'>[] = preparedVDisks.map((preparedVDisk) => { diff --git a/src/store/reducers/storage/__test__/prepareGroupsDisks.test.ts b/src/store/reducers/storage/__test__/prepareGroupsDisks.test.ts new file mode 100644 index 000000000..b957a0f85 --- /dev/null +++ b/src/store/reducers/storage/__test__/prepareGroupsDisks.test.ts @@ -0,0 +1,414 @@ +import type {TStoragePDisk, TStorageVDisk} from '../../../../types/api/storage'; +import {prepareGroupsPDisk, prepareGroupsVDisk} from '../prepareGroupsDisks'; + +describe('prepareGroupsVDisk', () => { + it('Should correctly parse data', () => { + const vDiksDataWithoutPDisk = { + VDiskId: '2181038134-22-0-0-0', + NodeId: 224, + AllocatedSize: '30943477760', + AvailableSize: '234461593600', + Status: 'READY', + DiskSpace: 'Green', + Whiteboard: { + VDiskId: { + GroupID: 2181038134, + GroupGeneration: 22, + Ring: 0, + Domain: 0, + VDisk: 0, + }, + ChangeTime: '1730273487988', + PDiskId: 1001, + VDiskSlotId: 1019, + Guid: '10619691988133943213', + Kind: '0', + VDiskState: 'OK', + DiskSpace: 'Green', + SatisfactionRank: { + FreshRank: { + Flag: 'Green', + }, + LevelRank: { + Flag: 'Green', + }, + }, + Replicated: true, + UnsyncedVDisks: '3', + AllocatedSize: '30943477760', + AvailableSize: '234461593600', + HasUnreadableBlobs: false, + IncarnationGuid: '14709186654400312808', + InstanceGuid: '18225898175839904663', + FrontQueues: 'Green', + StoragePoolName: '/storage/pool/name', + ReadThroughput: '0', + WriteThroughput: '0', + }, + } as const as TStorageVDisk; + + const expectedResult = { + VDiskId: { + GroupID: 2181038134, + GroupGeneration: 22, + Ring: 0, + Domain: 0, + VDisk: 0, + }, + StringifiedId: '2181038134-22-0-0-0', + NodeId: 224, + PDiskId: 1001, + VDiskSlotId: 1019, + + StoragePoolName: '/storage/pool/name', + + Kind: '0', + ChangeTime: '1730273487988', + Guid: '10619691988133943213', + IncarnationGuid: '14709186654400312808', + InstanceGuid: '18225898175839904663', + + Severity: 1, + VDiskState: 'OK', + DiskSpace: 'Green', + FrontQueues: 'Green', + Status: 'READY', + SatisfactionRank: { + FreshRank: { + Flag: 'Green', + }, + LevelRank: { + Flag: 'Green', + }, + }, + Replicated: true, + + UnsyncedVDisks: '3', + HasUnreadableBlobs: false, + + ReadThroughput: '0', + WriteThroughput: '0', + + AllocatedSize: 30943477760, + AvailableSize: 234461593600, + TotalSize: 265405071360, + AllocatedPercent: 12, + + Donors: undefined, + PDisk: undefined, + }; + + const preparedData = prepareGroupsVDisk(vDiksDataWithoutPDisk); + + expect(preparedData).toEqual(expectedResult); + }); + it('Should use BSC data when no Whiteboard data', () => { + const vDiksDataWithoutPDisk = { + VDiskId: '2181038134-22-0-0-0', + NodeId: 224, + AllocatedSize: '30943477760', + AvailableSize: '234461593600', + Status: 'READY', + DiskSpace: 'Green', + } as const as TStorageVDisk; + + const expectedResult = { + StringifiedId: '2181038134-22-0-0-0', + NodeId: 224, + + Severity: 0, + DiskSpace: 'Green', + Status: 'READY', + + AllocatedSize: 30943477760, + AvailableSize: 234461593600, + TotalSize: 265405071360, + AllocatedPercent: 12, + }; + + const preparedData = prepareGroupsVDisk(vDiksDataWithoutPDisk); + + expect(preparedData).toEqual(expectedResult); + }); + it('Should use Whiteboard data when no BSC data', () => { + const vDiksDataWithoutPDisk = { + Whiteboard: { + VDiskId: { + GroupID: 2181038134, + GroupGeneration: 22, + Ring: 0, + Domain: 0, + VDisk: 0, + }, + ChangeTime: '1730273487988', + PDiskId: 1001, + VDiskSlotId: 1019, + Guid: '10619691988133943213', + Kind: '0', + VDiskState: 'OK', + DiskSpace: 'Green', + SatisfactionRank: { + FreshRank: { + Flag: 'Green', + }, + LevelRank: { + Flag: 'Green', + }, + }, + Replicated: true, + UnsyncedVDisks: '3', + AllocatedSize: '30943477760', + AvailableSize: '234461593600', + HasUnreadableBlobs: false, + IncarnationGuid: '14709186654400312808', + InstanceGuid: '18225898175839904663', + FrontQueues: 'Green', + StoragePoolName: '/storage/pool/name', + ReadThroughput: '0', + WriteThroughput: '0', + }, + } as const as TStorageVDisk; + + const expectedResult = { + VDiskId: { + GroupID: 2181038134, + GroupGeneration: 22, + Ring: 0, + Domain: 0, + VDisk: 0, + }, + StringifiedId: '2181038134-22-0-0-0', + PDiskId: 1001, + VDiskSlotId: 1019, + + StoragePoolName: '/storage/pool/name', + + Kind: '0', + ChangeTime: '1730273487988', + Guid: '10619691988133943213', + IncarnationGuid: '14709186654400312808', + InstanceGuid: '18225898175839904663', + + Severity: 1, + VDiskState: 'OK', + DiskSpace: 'Green', + FrontQueues: 'Green', + SatisfactionRank: { + FreshRank: { + Flag: 'Green', + }, + LevelRank: { + Flag: 'Green', + }, + }, + Replicated: true, + + UnsyncedVDisks: '3', + HasUnreadableBlobs: false, + + ReadThroughput: '0', + WriteThroughput: '0', + + AllocatedSize: 30943477760, + AvailableSize: 234461593600, + TotalSize: 265405071360, + AllocatedPercent: 12, + + Donors: undefined, + PDisk: undefined, + }; + + const preparedData = prepareGroupsVDisk(vDiksDataWithoutPDisk); + + expect(preparedData).toEqual(expectedResult); + }); +}); + +describe('prepareGroupsPDisk', () => { + it('Should correctly parse data', () => { + const pDiskData = { + PDiskId: '224-1001', + NodeId: 224, + Path: '/dev/disk/by-partlabel/kikimr_nvme_04', + Type: 'ssd', + Guid: '10619691988133943213', + Category: '1', + TotalSize: '6400161873920', + AvailableSize: '5613855703040', + Status: 'ACTIVE', + DiskSpace: 'Green', + DecommitStatus: 'DECOMMIT_NONE', + SlotSize: '265405071360', + Whiteboard: { + PDiskId: 1001, + ChangeTime: '1730273451793', + Path: '/dev/disk/by-partlabel/kikimr_nvme_04', + Guid: '10619691988133943213', + Category: '1', + AvailableSize: '5613855703040', + TotalSize: '6400161873920', + State: 'Normal', + Device: 'Green', + Realtime: 'Green', + SerialNumber: 'PHLN227201336P4CGN', + SystemSize: '817889280', + LogUsedSize: '3271557120', + LogTotalSize: '27262976000', + ExpectedSlotCount: 24, + EnforcedDynamicSlotSize: '265405071360', + NumActiveSlots: 18, + }, + } as const as TStoragePDisk & { + NodeId?: number; + }; + + const expectedResult = { + NodeId: 224, + PDiskId: 1001, + StringifiedId: '224-1001', + + ChangeTime: '1730273451793', + Path: '/dev/disk/by-partlabel/kikimr_nvme_04', + Guid: '10619691988133943213', + SerialNumber: 'PHLN227201336P4CGN', + + Category: '1', + Type: 'SSD', + + State: 'Normal', + Device: 'Green', + Realtime: 'Green', + Status: 'ACTIVE', + DiskSpace: 'Green', + DecommitStatus: 'DECOMMIT_NONE', + + AvailableSize: 5613855703040, + TotalSize: 6400161873920, + AllocatedPercent: 12, + AllocatedSize: 786306170880, + Severity: 1, + + SystemSize: '817889280', + LogUsedSize: '3271557120', + LogTotalSize: '27262976000', + + NumActiveSlots: 18, + ExpectedSlotCount: 24, + SlotSize: '265405071360', + EnforcedDynamicSlotSize: '265405071360', + }; + + const preparedData = prepareGroupsPDisk(pDiskData); + + expect(preparedData).toEqual(expectedResult); + }); + it('Should use BSC data when no Whiteboard data', () => { + const pDiskData = { + PDiskId: '224-1001', + NodeId: 224, + Path: '/dev/disk/by-partlabel/kikimr_nvme_04', + Type: 'ssd', + Guid: '10619691988133943213', + Category: '1', + TotalSize: '6400161873920', + AvailableSize: '5613855703040', + Status: 'ACTIVE', + DiskSpace: 'Green', + DecommitStatus: 'DECOMMIT_NONE', + SlotSize: '265405071360', + } as const as TStoragePDisk & { + NodeId?: number; + }; + + const expectedResult = { + NodeId: 224, + StringifiedId: '224-1001', + + Path: '/dev/disk/by-partlabel/kikimr_nvme_04', + Guid: '10619691988133943213', + + Category: '1', + Type: 'SSD', + + Severity: 0, + + Status: 'ACTIVE', + DiskSpace: 'Green', + DecommitStatus: 'DECOMMIT_NONE', + + TotalSize: 6400161873920, + AvailableSize: 5613855703040, + AllocatedPercent: 12, + AllocatedSize: 786306170880, + + SlotSize: '265405071360', + }; + + const preparedData = prepareGroupsPDisk(pDiskData); + + expect(preparedData).toEqual(expectedResult); + }); + it('Should use Whiteboard data when no BSC data', () => { + const pDiskData = { + NodeId: 224, + Whiteboard: { + PDiskId: 1001, + ChangeTime: '1730273451793', + Path: '/dev/disk/by-partlabel/kikimr_nvme_04', + Guid: '10619691988133943213', + Category: '1', + AvailableSize: '5613855703040', + TotalSize: '6400161873920', + State: 'Normal', + Device: 'Green', + Realtime: 'Green', + SerialNumber: 'PHLN227201336P4CGN', + SystemSize: '817889280', + LogUsedSize: '3271557120', + LogTotalSize: '27262976000', + ExpectedSlotCount: 24, + EnforcedDynamicSlotSize: '265405071360', + NumActiveSlots: 18, + }, + } as const as TStoragePDisk & { + NodeId?: number; + }; + + const expectedResult = { + NodeId: 224, + PDiskId: 1001, + StringifiedId: '224-1001', + + ChangeTime: '1730273451793', + Path: '/dev/disk/by-partlabel/kikimr_nvme_04', + Guid: '10619691988133943213', + SerialNumber: 'PHLN227201336P4CGN', + + Category: '1', + Type: 'SSD', + + State: 'Normal', + Device: 'Green', + Realtime: 'Green', + + AvailableSize: 5613855703040, + TotalSize: 6400161873920, + AllocatedPercent: 12, + AllocatedSize: 786306170880, + Severity: 1, + + SystemSize: '817889280', + LogUsedSize: '3271557120', + LogTotalSize: '27262976000', + + NumActiveSlots: 18, + ExpectedSlotCount: 24, + SlotSize: '265405071360', + EnforcedDynamicSlotSize: '265405071360', + }; + + const preparedData = prepareGroupsPDisk(pDiskData); + + expect(preparedData).toEqual(expectedResult); + }); +}); diff --git a/src/store/reducers/storage/prepareGroupsDisks.ts b/src/store/reducers/storage/prepareGroupsDisks.ts new file mode 100644 index 000000000..1494f1723 --- /dev/null +++ b/src/store/reducers/storage/prepareGroupsDisks.ts @@ -0,0 +1,90 @@ +import type {TStoragePDisk, TStorageVDisk} from '../../../types/api/storage'; +import {stringifyVdiskId} from '../../../utils/dataFormatters/dataFormatters'; +import {calculatePDiskSeverity} from '../../../utils/disks/calculatePDiskSeverity'; +import {calculateVDiskSeverity} from '../../../utils/disks/calculateVDiskSeverity'; +import {getPDiskType} from '../../../utils/disks/getPDiskType'; +import {getPDiskId} from '../../../utils/disks/helpers'; +import {preparePDiskSizeFields, prepareVDiskSizeFields} from '../../../utils/disks/prepareDisks'; +import type {PDiskType, PreparedVDisk} from '../../../utils/disks/types'; + +export function prepareGroupsVDisk(data: TStorageVDisk = {}): PreparedVDisk { + const {Whiteboard: whiteboardVDisk = {}, PDisk, ...bscVDisk} = data; + + const mergedVDiskData = { + ...whiteboardVDisk, + ...bscVDisk, + VDiskId: whiteboardVDisk.VDiskId, + }; + + const preparedPDisk = PDisk + ? prepareGroupsPDisk({...PDisk, NodeId: mergedVDiskData.NodeId}) + : undefined; + + const PDiskId = preparedPDisk?.PDiskId ?? whiteboardVDisk?.PDiskId; + + const StringifiedId = bscVDisk.VDiskId ?? stringifyVdiskId(whiteboardVDisk.VDiskId); + + const Severity = calculateVDiskSeverity(mergedVDiskData); + + const vDiskSizeFields = prepareVDiskSizeFields({ + AvailableSize: mergedVDiskData.AvailableSize ?? PDisk?.AvailableSize, + AllocatedSize: mergedVDiskData.AllocatedSize, + }); + + const preparedDonors = bscVDisk.Donors?.map((donor) => { + return prepareGroupsVDisk({ + ...donor, + Whiteboard: {...donor.Whiteboard, DonorMode: true}, + }); + }); + + return { + ...mergedVDiskData, + ...vDiskSizeFields, + PDisk: preparedPDisk, + Donors: preparedDonors, + PDiskId, + StringifiedId, + Severity, + }; +} + +export function prepareGroupsPDisk(data: TStoragePDisk & {NodeId?: number} = {}) { + const {Whiteboard: whiteboardPDisk, ...bscPDisk} = data; + + const mergedPDiskData = { + ...whiteboardPDisk, + ...bscPDisk, + PDiskId: whiteboardPDisk?.PDiskId, + }; + + const StringifiedId = + bscPDisk.PDiskId || getPDiskId(mergedPDiskData.NodeId, mergedPDiskData.PDiskId); + + const {AllocatedPercent, AllocatedSize, AvailableSize, TotalSize} = preparePDiskSizeFields({ + AvailableSize: mergedPDiskData.AvailableSize, + TotalSize: mergedPDiskData.TotalSize, + }); + + const Type = + (bscPDisk.Type?.toUpperCase() as PDiskType) ?? getPDiskType(whiteboardPDisk?.Category); + + const Severity = calculatePDiskSeverity({ + State: whiteboardPDisk?.State, + AllocatedPercent, + }); + + const SlotSize = bscPDisk.SlotSize ?? whiteboardPDisk?.EnforcedDynamicSlotSize; + + return { + ...mergedPDiskData, + StringifiedId, + AllocatedPercent, + AllocatedSize, + AvailableSize, + TotalSize, + Type, + Severity, + SlotSize, + }; +} diff --git a/src/store/reducers/storage/utils.ts b/src/store/reducers/storage/utils.ts index 9a1d22efb..e4f3b1898 100644 --- a/src/store/reducers/storage/utils.ts +++ b/src/store/reducers/storage/utils.ts @@ -12,11 +12,15 @@ import type { import {EVDiskState} from '../../../types/api/vdisk'; import type {TVDiskStateInfo} from '../../../types/api/vdisk'; import {getColorSeverity, getSeverityColor} from '../../../utils/disks/helpers'; -import {preparePDiskData, prepareVDiskData} from '../../../utils/disks/prepareDisks'; +import { + prepareWhiteboardPDiskData, + prepareWhiteboardVDiskData, +} from '../../../utils/disks/prepareDisks'; import {prepareNodeSystemState} from '../../../utils/nodes'; import {getUsage} from '../../../utils/storage'; import {parseUsToMs} from '../../../utils/timeParsers'; +import {prepareGroupsVDisk} from './prepareGroupsDisks'; import type { PreparedStorageGroup, PreparedStorageNode, @@ -36,7 +40,7 @@ function getGroupDiskSpaceStatus(group: TStorageGroupInfo | TGroupsStorageGroupI } const prepareVDisk = (vDisk: TVDiskStateInfo, poolName: string | undefined) => { - const preparedVDisk = prepareVDiskData(vDisk); + const preparedVDisk = prepareWhiteboardVDiskData(vDisk); // VDisk doesn't have its own StoragePoolName when located inside StoragePool data return { @@ -78,7 +82,7 @@ export const prepareStorageGroupData = ( Type: PDiskType, State: PDiskState, AvailableSize: PDiskAvailableSize, - } = preparePDiskData(PDisk); + } = prepareWhiteboardPDiskData(PDisk); if (!Replicated || PDiskState !== TPDiskState.Normal || VDiskState !== EVDiskState.OK) { missing += 1; @@ -192,13 +196,13 @@ const prepareStorageNodeData = (node: TNodeInfo): PreparedStorageNode => { const pDisks = node.PDisks?.map((pDisk) => { return { - ...preparePDiskData(pDisk), + ...prepareWhiteboardPDiskData(pDisk), NodeId: node.NodeId, }; }); const vDisks = node.VDisks?.map((vDisk) => { return { - ...prepareVDiskData(vDisk), + ...prepareWhiteboardVDiskData(vDisk), NodeId: node.NodeId, }; }); @@ -268,42 +272,7 @@ export function prepareGroupsResponse(data: StorageGroupsResponse): PreparedStor LatencyGetFast, } = group; - const vDisks = VDisks.map((disk) => { - const { - Whiteboard: whiteboardVDisk, - PDisk, - VDiskId, - NodeId, - AllocatedSize, - AvailableSize, - DiskSpace, - Status, - } = disk; - const whiteboardPDisk = PDisk?.Whiteboard; - - const PDiskId = whiteboardPDisk?.PDiskId; - - const whiteboardVDiskData = { - ...whiteboardVDisk, - PDiskId, - NodeId, - AllocatedSize, - AvailableSize, - DiskSpace, - Status, - PDisk: {...whiteboardPDisk, NodeId}, - }; - - const preparedVDiskData = prepareVDiskData(whiteboardVDiskData); - - return { - ...preparedVDiskData, - // There might be no Whiteboard data if cluster is not healthy - // StringifiedId is formed from Whiteboard.VDiskId object - // Use VDiskId string from backend in such case - StringifiedId: preparedVDiskData.StringifiedId || VDiskId, - }; - }); + const vDisks = VDisks.map(prepareGroupsVDisk); const diskSpaceStatus = getGroupDiskSpaceStatus(group); diff --git a/src/store/reducers/vdisk/utils.ts b/src/store/reducers/vdisk/utils.ts index 9726e5a71..e2168bc3a 100644 --- a/src/store/reducers/vdisk/utils.ts +++ b/src/store/reducers/vdisk/utils.ts @@ -1,7 +1,10 @@ import type {TEvPDiskStateResponse} from '../../../types/api/pdisk'; import type {TEvSystemStateResponse} from '../../../types/api/systemState'; import type {TEvVDiskStateResponse} from '../../../types/api/vdisk'; -import {preparePDiskData, prepareVDiskData} from '../../../utils/disks/prepareDisks'; +import { + prepareWhiteboardPDiskData, + prepareWhiteboardVDiskData, +} from '../../../utils/disks/prepareDisks'; import {prepareNodeSystemState} from '../../../utils/nodes'; import type {VDiskData} from './types'; @@ -12,10 +15,10 @@ export function prepareVDiskDataResponse([vDiskResponse, pDiskResponse, nodeResp TEvSystemStateResponse, ]): VDiskData { const rawVDisk = vDiskResponse.VDiskStateInfo?.[0]; - const preparedVDisk = prepareVDiskData(rawVDisk); + const preparedVDisk = prepareWhiteboardVDiskData(rawVDisk); const rawPDisk = pDiskResponse.PDiskStateInfo?.[0]; - const preparedPDisk = preparePDiskData(rawPDisk); + const preparedPDisk = prepareWhiteboardPDiskData(rawPDisk); const rawNode = nodeResponse.SystemStateInfo?.[0]; const preparedNode = prepareNodeSystemState(rawNode); diff --git a/src/types/api/pdisk.ts b/src/types/api/pdisk.ts index dae9506c0..5d6aea274 100644 --- a/src/types/api/pdisk.ts +++ b/src/types/api/pdisk.ts @@ -88,7 +88,7 @@ interface TPDiskInfoWhiteboard { interface TPDiskInfoBSC { PDisk?: TPDiskInfo; - VDisks?: TVSlotEntry; + VDisks?: TVSlotEntry[]; } /** diff --git a/src/types/api/storage.ts b/src/types/api/storage.ts index 87e095163..094e447f1 100644 --- a/src/types/api/storage.ts +++ b/src/types/api/storage.ts @@ -193,7 +193,7 @@ export interface TStorageVDisk { AvailableSize?: string; Status?: EVDiskStatus; DiskSpace?: EFlag; - Donors?: Omit; + Donors?: TStorageVDisk[]; PDisk?: TStoragePDisk; Whiteboard?: TVDiskStateInfo; } @@ -201,7 +201,7 @@ export interface TStorageVDisk { /** * PDisk data from storage/groups handler */ -interface TStoragePDisk { +export interface TStoragePDisk { PDiskId?: string; Path?: string; Type?: 'hdd' | 'ssd' | 'nvme'; diff --git a/src/utils/disks/__test__/calculatePDiskSeverity.test.ts b/src/utils/disks/__test__/calculatePDiskSeverity.test.ts index 726384b42..2a9671f42 100644 --- a/src/utils/disks/__test__/calculatePDiskSeverity.test.ts +++ b/src/utils/disks/__test__/calculatePDiskSeverity.test.ts @@ -11,9 +11,9 @@ describe('PDisk state', () => { }); it('Should determine severity based on space utilization if state severity is OK', () => { - const severity1 = calculatePDiskSeverity({State: TPDiskState.Normal}, 0); - const severity2 = calculatePDiskSeverity({State: TPDiskState.Normal}, 86); - const severity3 = calculatePDiskSeverity({State: TPDiskState.Normal}, 96); + const severity1 = calculatePDiskSeverity({State: TPDiskState.Normal, AllocatedPercent: 0}); + const severity2 = calculatePDiskSeverity({State: TPDiskState.Normal, AllocatedPercent: 86}); + const severity3 = calculatePDiskSeverity({State: TPDiskState.Normal, AllocatedPercent: 96}); expect(severity1).toEqual(DISK_COLOR_STATE_TO_NUMERIC_SEVERITY.Green); expect(severity2).toEqual(DISK_COLOR_STATE_TO_NUMERIC_SEVERITY.Yellow); @@ -21,8 +21,11 @@ describe('PDisk state', () => { }); it('Should determine severity based on max severity of state and space utilization ', () => { - const severity1 = calculatePDiskSeverity({State: TPDiskState.ChunkQuotaError}, 0); - const severity2 = calculatePDiskSeverity({State: TPDiskState.Normal}, 96); + const severity1 = calculatePDiskSeverity({ + State: TPDiskState.ChunkQuotaError, + AllocatedPercent: 0, + }); + const severity2 = calculatePDiskSeverity({State: TPDiskState.Normal, AllocatedPercent: 96}); expect(severity1).toEqual(DISK_COLOR_STATE_TO_NUMERIC_SEVERITY.Red); expect(severity2).toEqual(DISK_COLOR_STATE_TO_NUMERIC_SEVERITY.Red); @@ -37,8 +40,8 @@ describe('PDisk state', () => { }); it('Should display as unavailabe when no State is provided event if space severity is not OK', () => { - const severity1 = calculatePDiskSeverity({}, 86); - const severity2 = calculatePDiskSeverity({}, 96); + const severity1 = calculatePDiskSeverity({AllocatedPercent: 86}); + const severity2 = calculatePDiskSeverity({AllocatedPercent: 96}); expect(severity1).toEqual(DISK_COLOR_STATE_TO_NUMERIC_SEVERITY.Grey); expect(severity2).toEqual(DISK_COLOR_STATE_TO_NUMERIC_SEVERITY.Grey); diff --git a/src/utils/disks/__test__/calculateVDiskSeverity.test.ts b/src/utils/disks/__test__/calculateVDiskSeverity.test.ts index 3dddca95d..fe48e0b0b 100644 --- a/src/utils/disks/__test__/calculateVDiskSeverity.test.ts +++ b/src/utils/disks/__test__/calculateVDiskSeverity.test.ts @@ -6,19 +6,16 @@ import {DISK_COLOR_STATE_TO_NUMERIC_SEVERITY} from '../constants'; describe('VDisk state', () => { it('Should determine severity based on the highest value among VDiskState, DiskSpace and FrontQueues', () => { const severity1 = calculateVDiskSeverity({ - VDiskId: {Domain: 1}, VDiskState: EVDiskState.OK, // severity 1, green DiskSpace: EFlag.Yellow, // severity 3, yellow FrontQueues: EFlag.Green, // severity 1, green }); const severity2 = calculateVDiskSeverity({ - VDiskId: {Domain: 2}, VDiskState: EVDiskState.PDiskError, // severity 5, red DiskSpace: EFlag.Yellow, // severity 3, yellow FrontQueues: EFlag.Green, // severity 1, green }); const severity3 = calculateVDiskSeverity({ - VDiskId: {Domain: 3}, VDiskState: EVDiskState.OK, // severity 1, green DiskSpace: EFlag.Yellow, // severity 3, yellow FrontQueues: EFlag.Orange, // severity 4, orange @@ -31,13 +28,11 @@ describe('VDisk state', () => { it('Should not pick the highest severity based on FrontQueues value', () => { const severity1 = calculateVDiskSeverity({ - VDiskId: {Domain: 1}, VDiskState: EVDiskState.OK, // severity 1, green DiskSpace: EFlag.Green, // severity 1, green FrontQueues: EFlag.Red, // severity 5, red }); const severity2 = calculateVDiskSeverity({ - VDiskId: {Domain: 2}, VDiskState: EVDiskState.OK, // severity 1, green DiskSpace: EFlag.Red, // severity 5, red FrontQueues: EFlag.Red, // severity 5, red @@ -49,29 +44,27 @@ describe('VDisk state', () => { // prettier-ignore it('Should display as unavailable when no VDiskState is provided', () => { - const severity1 = calculateVDiskSeverity({ - VDiskId: {Domain: 1} - }); + const severity1 = calculateVDiskSeverity({}); const severity2 = calculateVDiskSeverity({ - VDiskId: {Domain: 2}, VDiskState: EVDiskState.OK + VDiskState: EVDiskState.OK }); const severity3 = calculateVDiskSeverity({ - VDiskId: {Domain: 3}, DiskSpace: EFlag.Green + DiskSpace: EFlag.Green }); const severity4 = calculateVDiskSeverity({ - VDiskId: {Domain: 4}, FrontQueues: EFlag.Green + FrontQueues: EFlag.Green }); const severity5 = calculateVDiskSeverity({ - VDiskId: {Domain: 5}, VDiskState: EVDiskState.OK, DiskSpace: EFlag.Green + VDiskState: EVDiskState.OK, DiskSpace: EFlag.Green }); const severity6 = calculateVDiskSeverity({ - VDiskId: {Domain: 6}, VDiskState: EVDiskState.OK, FrontQueues: EFlag.Green + VDiskState: EVDiskState.OK, FrontQueues: EFlag.Green }); const severity7 = calculateVDiskSeverity({ - VDiskId: {Domain: 7}, DiskSpace: EFlag.Green, FrontQueues: EFlag.Green + DiskSpace: EFlag.Green, FrontQueues: EFlag.Green }); const severity8 = calculateVDiskSeverity({ - VDiskId: {Domain: 8}, VDiskState: EVDiskState.OK, DiskSpace: EFlag.Green, FrontQueues: EFlag.Green + VDiskState: EVDiskState.OK, DiskSpace: EFlag.Green, FrontQueues: EFlag.Green }); // unavailable disks display with the grey color @@ -87,7 +80,6 @@ describe('VDisk state', () => { it('Should display as unavailable when no VDiskState is provided even if DiskSpace or FrontQueues flags are not green', () => { const severity1 = calculateVDiskSeverity({ - VDiskId: {Domain: 1}, DiskSpace: EFlag.Red, FrontQueues: EFlag.Yellow, }); @@ -98,12 +90,10 @@ describe('VDisk state', () => { it('Should display replicating VDisks in OK state with a distinct color', () => { const severity1 = calculateVDiskSeverity({ - VDiskId: {Domain: 1}, VDiskState: EVDiskState.OK, // severity 1, green Replicated: false, }); const severity2 = calculateVDiskSeverity({ - VDiskId: {Domain: 2}, VDiskState: EVDiskState.OK, // severity 1, green Replicated: true, }); @@ -114,12 +104,10 @@ describe('VDisk state', () => { it('Should display replicating VDisks in a not-OK state with a regular color', () => { const severity1 = calculateVDiskSeverity({ - VDiskId: {Domain: 1}, VDiskState: EVDiskState.Initial, // severity 3, yellow Replicated: false, }); const severity2 = calculateVDiskSeverity({ - VDiskId: {Domain: 2}, VDiskState: EVDiskState.PDiskError, // severity 5, red Replicated: false, }); @@ -130,19 +118,16 @@ describe('VDisk state', () => { it('Should always display donor VDisks with a regular color', () => { const severity1 = calculateVDiskSeverity({ - VDiskId: {Domain: 1}, VDiskState: EVDiskState.OK, // severity 1, green Replicated: false, // donors are always in the not replicated state since they are leftovers DonorMode: true, }); const severity2 = calculateVDiskSeverity({ - VDiskId: {Domain: 2}, VDiskState: EVDiskState.Initial, // severity 3, yellow Replicated: false, DonorMode: true, }); const severity3 = calculateVDiskSeverity({ - VDiskId: {Domain: 3}, VDiskState: EVDiskState.PDiskError, // severity 5, red Replicated: false, DonorMode: true, diff --git a/src/utils/disks/__test__/prepareDisks.test.ts b/src/utils/disks/__test__/prepareDisks.test.ts new file mode 100644 index 000000000..1d9272f78 --- /dev/null +++ b/src/utils/disks/__test__/prepareDisks.test.ts @@ -0,0 +1,238 @@ +import type {TPDiskStateInfo} from '../../../types/api/pdisk'; +import type {TVDiskStateInfo, TVSlotId} from '../../../types/api/vdisk'; +import { + preparePDiskSizeFields, + prepareVDiskSizeFields, + prepareWhiteboardPDiskData, + prepareWhiteboardVDiskData, +} from '../prepareDisks'; + +describe('prepareWhiteboardVDiskData', () => { + it('Should correctly parse data', () => { + const data = { + VDiskId: { + GroupID: 0, + GroupGeneration: 1, + Ring: 0, + Domain: 0, + VDisk: 0, + }, + ChangeTime: '1730384311105', + PDiskId: 1, + VDiskSlotId: 0, + Guid: '3910585916831022250', + Kind: '0', + NodeId: 1, + VDiskState: 'OK', + DiskSpace: 'Green', + SatisfactionRank: { + FreshRank: { + Flag: 'Green', + }, + LevelRank: { + Flag: 'Green', + }, + }, + Replicated: true, + ReplicationProgress: 1, + ReplicationSecondsRemaining: 0, + AllocatedSize: '8996782080', + AvailableSize: '188523479040', + HasUnreadableBlobs: false, + IncarnationGuid: '719472956608975753', + InstanceGuid: '11219959563151194061', + FrontQueues: 'Green', + StoragePoolName: 'static', + ReadThroughput: '0', + WriteThroughput: '447', + } as const as TVDiskStateInfo; + + const expectedResult = { + StringifiedId: '0-1-0-0-0', + VDiskId: { + GroupID: 0, + GroupGeneration: 1, + Ring: 0, + Domain: 0, + VDisk: 0, + }, + NodeId: 1, + PDiskId: 1, + VDiskSlotId: 0, + StoragePoolName: 'static', + + ChangeTime: '1730384311105', + + Guid: '3910585916831022250', + Kind: '0', + IncarnationGuid: '719472956608975753', + InstanceGuid: '11219959563151194061', + + Severity: 1, + VDiskState: 'OK', + DiskSpace: 'Green', + FrontQueues: 'Green', + SatisfactionRank: { + FreshRank: { + Flag: 'Green', + }, + LevelRank: { + Flag: 'Green', + }, + }, + Replicated: true, + ReplicationProgress: 1, + ReplicationSecondsRemaining: 0, + HasUnreadableBlobs: false, + + ReadThroughput: '0', + WriteThroughput: '447', + + AvailableSize: 188523479040, + AllocatedSize: 8996782080, + TotalSize: 197520261120, + AllocatedPercent: 5, + }; + + const preparedData = prepareWhiteboardVDiskData(data); + + expect(preparedData).toEqual(expectedResult); + }); + it('Should parse unavailable donors', () => { + const data = { + NodeId: 1, + PDiskId: 2, + VSlotId: 3, + } as const as TVSlotId; + + const expectedResult = { + NodeId: 1, + PDiskId: 2, + VDiskSlotId: 3, + StringifiedId: '1-2-3', + }; + + const preparedData = prepareWhiteboardVDiskData(data); + + expect(preparedData).toEqual(expectedResult); + }); +}); + +describe('prepareWhiteboardPDiskData', () => { + it('Should correctly parse data', () => { + const data = { + PDiskId: 1, + ChangeTime: '1730383540716', + Path: '/dev/disk/by-partlabel/kikimr_nvme_01', + Guid: '3910585916831022250', + Category: '1', + AvailableSize: '3107979264000', + TotalSize: '3199556648960', + State: 'Normal', + NodeId: 1, + Device: 'Green', + Realtime: 'Green', + SerialNumber: '', + SystemSize: '817889280', + LogUsedSize: '1772093440', + LogTotalSize: '36805017600', + ExpectedSlotCount: 16, + EnforcedDynamicSlotSize: '197520261120', + NumActiveSlots: 10, + } as const as TPDiskStateInfo; + + const expectedResult = { + PDiskId: 1, + NodeId: 1, + StringifiedId: '1-1', + + Type: 'SSD', + Category: '1', + Path: '/dev/disk/by-partlabel/kikimr_nvme_01', + Guid: '3910585916831022250', + SerialNumber: '', + + ChangeTime: '1730383540716', + + Severity: 1, + Device: 'Green', + Realtime: 'Green', + State: 'Normal', + + AvailableSize: 3107979264000, + TotalSize: 3199556648960, + AllocatedSize: 91577384960, + AllocatedPercent: 3, + + ExpectedSlotCount: 16, + NumActiveSlots: 10, + SlotSize: '197520261120', + + SystemSize: '817889280', + LogUsedSize: '1772093440', + LogTotalSize: '36805017600', + }; + + const preparedData = prepareWhiteboardPDiskData(data); + + expect(preparedData).toEqual(expectedResult); + }); +}); + +describe('prepareVDiskSizeFields', () => { + it('Should prepare VDisk size fields', () => { + expect( + prepareVDiskSizeFields({ + AvailableSize: '400', + AllocatedSize: '100', + }), + ).toEqual({ + AvailableSize: 400, + AllocatedSize: 100, + TotalSize: 500, + AllocatedPercent: 20, + }); + }); + it('Returns NaN if on undefined data', () => { + expect( + prepareVDiskSizeFields({ + AvailableSize: undefined, + AllocatedSize: undefined, + }), + ).toEqual({ + AvailableSize: NaN, + AllocatedSize: NaN, + TotalSize: NaN, + AllocatedPercent: NaN, + }); + }); +}); + +describe('preparePDiskSizeFields', () => { + it('Should prepare PDisk size fields', () => { + expect( + preparePDiskSizeFields({ + AvailableSize: '400', + TotalSize: '500', + }), + ).toEqual({ + AvailableSize: 400, + AllocatedSize: 100, + TotalSize: 500, + AllocatedPercent: 20, + }); + }); + it('Returns NaN if on undefined data', () => { + expect( + preparePDiskSizeFields({ + AvailableSize: undefined, + TotalSize: undefined, + }), + ).toEqual({ + AvailableSize: NaN, + AllocatedSize: NaN, + TotalSize: NaN, + AllocatedPercent: NaN, + }); + }); +}); diff --git a/src/utils/disks/calculatePDiskSeverity.ts b/src/utils/disks/calculatePDiskSeverity.ts index de4ddcd29..21f54a62c 100644 --- a/src/utils/disks/calculatePDiskSeverity.ts +++ b/src/utils/disks/calculatePDiskSeverity.ts @@ -1,5 +1,5 @@ import {EFlag} from '../../types/api/enums'; -import type {TPDiskState, TPDiskStateInfo} from '../../types/api/pdisk'; +import type {TPDiskState} from '../../types/api/pdisk'; import {generateEvaluator} from '../generateEvaluator'; import { @@ -10,9 +10,14 @@ import { const getUsageSeverityForPDisk = generateEvaluator(85, 95, [EFlag.Green, EFlag.Yellow, EFlag.Red]); -export function calculatePDiskSeverity(pDisk: TPDiskStateInfo, allocatedPercent = 0) { +export function calculatePDiskSeverity< + T extends { + State?: TPDiskState; + AllocatedPercent?: number; + }, +>(pDisk: T) { const stateSeverity = getStateSeverity(pDisk.State); - const spaceSeverityFlag = getUsageSeverityForPDisk(allocatedPercent); + const spaceSeverityFlag = getUsageSeverityForPDisk(pDisk.AllocatedPercent || 0); if (stateSeverity === NOT_AVAILABLE_SEVERITY || !spaceSeverityFlag) { return stateSeverity; diff --git a/src/utils/disks/calculateVDiskSeverity.ts b/src/utils/disks/calculateVDiskSeverity.ts index 54d71b89f..06808dd11 100644 --- a/src/utils/disks/calculateVDiskSeverity.ts +++ b/src/utils/disks/calculateVDiskSeverity.ts @@ -1,18 +1,20 @@ import type {EFlag} from '../../types/api/enums'; -import type {EVDiskState, TVDiskStateInfo} from '../../types/api/vdisk'; +import type {EVDiskState} from '../../types/api/vdisk'; import { DISK_COLOR_STATE_TO_NUMERIC_SEVERITY, NOT_AVAILABLE_SEVERITY, VDISK_STATE_SEVERITY, } from './constants'; -import {isFullVDiskData} from './helpers'; - -export function calculateVDiskSeverity(vDisk: TVDiskStateInfo) { - if (!isFullVDiskData(vDisk)) { - return NOT_AVAILABLE_SEVERITY; - } +export function calculateVDiskSeverity< + T extends { + DiskSpace?: EFlag; + VDiskState?: EVDiskState; + FrontQueues?: EFlag; + Replicated?: boolean; + }, +>(vDisk: T) { const {DiskSpace, VDiskState, FrontQueues, Replicated} = vDisk; // if the disk is not available, this determines its status severity regardless of other features diff --git a/src/utils/disks/helpers.ts b/src/utils/disks/helpers.ts index 51719e481..8528e4cb6 100644 --- a/src/utils/disks/helpers.ts +++ b/src/utils/disks/helpers.ts @@ -7,8 +7,11 @@ import { DISK_NUMERIC_SEVERITY_TO_STATE_COLOR, NOT_AVAILABLE_SEVERITY_COLOR, } from './constants'; +import type {PreparedVDisk} from './types'; -export function isFullVDiskData(disk: TVDiskStateInfo | TVSlotId): disk is TVDiskStateInfo { +export function isFullVDiskData( + disk: PreparedVDisk | TVDiskStateInfo | TVSlotId, +): disk is PreparedVDisk | TVDiskStateInfo { return 'VDiskId' in disk; } diff --git a/src/utils/disks/prepareDisks.ts b/src/utils/disks/prepareDisks.ts index 745a0a981..3d573e827 100644 --- a/src/utils/disks/prepareDisks.ts +++ b/src/utils/disks/prepareDisks.ts @@ -1,70 +1,155 @@ -import type {TPDiskInfo, TPDiskStateInfo} from '../../types/api/pdisk'; -import type {TVDiskStateInfo} from '../../types/api/vdisk'; +import type {TPDiskStateInfo} from '../../types/api/pdisk'; +import type {TVDiskStateInfo, TVSlotId} from '../../types/api/vdisk'; import {stringifyVdiskId} from '../dataFormatters/dataFormatters'; import {calculatePDiskSeverity} from './calculatePDiskSeverity'; import {calculateVDiskSeverity} from './calculateVDiskSeverity'; import {getPDiskType} from './getPDiskType'; +import {getPDiskId, isFullVDiskData} from './helpers'; import type {PreparedPDisk, PreparedVDisk} from './types'; -export function prepareVDiskData(vdiskState: TVDiskStateInfo = {}): PreparedVDisk { +export function prepareWhiteboardVDiskData( + vDiskState: TVDiskStateInfo | TVSlotId = {}, +): PreparedVDisk { + if (!isFullVDiskData(vDiskState)) { + const {NodeId, PDiskId, VSlotId} = vDiskState; + + const StringifiedId = stringifyVdiskId({ + NodeId, + PDiskId, + VSlotId, + }); + + return { + StringifiedId, + NodeId, + PDiskId, + // Replace VSlotId with VDiskSlotId to match with PreparedVDisk type + VDiskSlotId: VSlotId, + }; + } + + const { + PDisk, + PDiskId, + VDiskId, + NodeId, + Donors, + AvailableSize, + AllocatedSize, + ...restVDiskFields + } = vDiskState; + // Prepare PDisk only if it is present - const PDisk = vdiskState.PDisk - ? preparePDiskData({ - ...vdiskState.PDisk, - NodeId: vdiskState.PDisk.NodeId ?? vdiskState.NodeId, - }) + const preparedPDisk = PDisk + ? prepareWhiteboardPDiskData({...PDisk, NodeId: PDisk?.NodeId ?? NodeId}) : undefined; - const PDiskId = vdiskState.PDiskId ?? PDisk?.PDiskId; + const actualPDiskId = PDiskId ?? PDisk?.PDiskId; - const available = Number(vdiskState.AvailableSize ?? PDisk?.AvailableSize); - const allocated = Number(vdiskState.AllocatedSize); - const total = allocated + available; - const allocatedPercent = Math.round((allocated * 100) / total); + const vDiskSizeFields = prepareVDiskSizeFields({ + AvailableSize: AvailableSize ?? PDisk?.AvailableSize, + AllocatedSize: AllocatedSize, + }); + + const Severity = calculateVDiskSeverity(vDiskState); + + const StringifiedId = stringifyVdiskId(VDiskId); - const Donors = vdiskState.Donors?.map((donor) => { - return prepareVDiskData({...donor, DonorMode: true}); + const preparedDonors = Donors?.map((donor) => { + return prepareWhiteboardVDiskData({...donor, DonorMode: true}); }); - const Severity = calculateVDiskSeverity(vdiskState); + return { + ...restVDiskFields, + ...vDiskSizeFields, + + VDiskId, + NodeId, + PDiskId: actualPDiskId, + PDisk: preparedPDisk, + Donors: preparedDonors, + + Severity, + StringifiedId, + }; +} + +export function prepareWhiteboardPDiskData(pdiskState: TPDiskStateInfo = {}): PreparedPDisk { + const { + AvailableSize, + TotalSize, + Category, + State, + PDiskId, + NodeId, + EnforcedDynamicSlotSize, + ...restPDiskFields + } = pdiskState; + + const StringifiedId = getPDiskId(PDiskId, NodeId); + + const Type = getPDiskType(Category); + + const pdiskPreparedSizeFields = preparePDiskSizeFields({ + AvailableSize, + TotalSize, + }); - const StringifiedId = stringifyVdiskId(vdiskState.VDiskId); + const Severity = calculatePDiskSeverity({ + State, + AllocatedPercent: pdiskPreparedSizeFields.AllocatedPercent, + }); return { - ...vdiskState, - PDisk, + ...restPDiskFields, + ...pdiskPreparedSizeFields, PDiskId, - Donors, - Severity, + NodeId, StringifiedId, + Type, + Category, + State, + Severity, + SlotSize: EnforcedDynamicSlotSize, + }; +} + +export function prepareVDiskSizeFields({ + AvailableSize, + AllocatedSize, +}: { + AvailableSize: string | number | undefined; + AllocatedSize: string | number | undefined; +}) { + const available = Number(AvailableSize); + const allocated = Number(AllocatedSize); + const total = allocated + available; + const allocatedPercent = Math.round((allocated * 100) / total); + return { + AvailableSize: available, + AllocatedSize: allocated, TotalSize: total, AllocatedPercent: allocatedPercent, }; } -export function preparePDiskData( - pdiskState: TPDiskStateInfo = {}, - bscPDiskInfo: TPDiskInfo = {}, -): PreparedPDisk { - const {AvailableSize, TotalSize, Category} = pdiskState; - - const Type = getPDiskType(Category); - +export function preparePDiskSizeFields({ + AvailableSize, + TotalSize, +}: { + AvailableSize: string | number | undefined; + TotalSize: string | number | undefined; +}) { const available = Number(AvailableSize); const total = Number(TotalSize); const allocated = total - available; const allocatedPercent = Math.round((allocated * 100) / total); - const Severity = calculatePDiskSeverity(pdiskState, allocatedPercent); - return { - ...bscPDiskInfo, - ...pdiskState, - Type, - Severity, - + AvailableSize: available, + TotalSize: total, AllocatedSize: allocated, AllocatedPercent: allocatedPercent, }; diff --git a/src/utils/disks/types.ts b/src/utils/disks/types.ts index ad7e93d26..45371819f 100644 --- a/src/utils/disks/types.ts +++ b/src/utils/disks/types.ts @@ -4,22 +4,35 @@ import type {ValueOf} from '../../types/common'; import type {PDISK_TYPES} from './getPDiskType'; -export type PreparedPDisk = TPDiskStateInfo & - Omit, 'Type'> & { +export type PreparedPDisk = Omit< + TPDiskStateInfo, + 'AvailableSize' | 'TotalSize' | 'EnforcedDynamicSlotSize' +> & + Omit, 'Type' | 'AvailableSize' | 'TotalSize'> & { Type?: PDiskType; Severity?: number; + StringifiedId?: string; + AvailableSize?: number; + TotalSize?: number; AllocatedSize?: number; AllocatedPercent?: number; + + SlotSize?: string; }; -export interface PreparedVDisk extends TVDiskStateInfo { +export interface PreparedVDisk + extends Omit { PDisk?: PreparedPDisk; Severity?: number; StringifiedId?: string; + AvailableSize?: number; TotalSize?: number; + AllocatedSize?: number; AllocatedPercent?: number; + + Donors?: PreparedVDisk[]; } export type PDiskType = ValueOf; From fc25da35664db451589531cfd868e98622fd9773 Mon Sep 17 00:00:00 2001 From: mufazalov Date: Tue, 5 Nov 2024 16:27:35 +0300 Subject: [PATCH 2/2] small fixes --- src/components/PDiskPopup/PDiskPopup.tsx | 4 ++-- src/utils/disks/prepareDisks.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/PDiskPopup/PDiskPopup.tsx b/src/components/PDiskPopup/PDiskPopup.tsx index d9dc41bb0..b8827359f 100644 --- a/src/components/PDiskPopup/PDiskPopup.tsx +++ b/src/components/PDiskPopup/PDiskPopup.tsx @@ -8,7 +8,7 @@ import {EMPTY_DATA_PLACEHOLDER} from '../../utils/constants'; import {createPDiskDeveloperUILink} from '../../utils/developerUI/developerUI'; import type {PreparedPDisk} from '../../utils/disks/types'; import {useTypedSelector} from '../../utils/hooks'; -import {bytesToGB} from '../../utils/utils'; +import {bytesToGB, isNumeric} from '../../utils/utils'; import {InfoViewer} from '../InfoViewer'; import type {InfoViewerItem} from '../InfoViewer'; import {LinkWithIcon} from '../LinkWithIcon/LinkWithIcon'; @@ -54,7 +54,7 @@ export const preparePDiskData = ( pdiskData.push({label: 'Path', value: Path}); } - if (!isNaN(Number(TotalSize))) { + if (isNumeric(TotalSize)) { pdiskData.push({ label: 'Available', value: `${bytesToGB(AvailableSize)} of ${bytesToGB(TotalSize)}`, diff --git a/src/utils/disks/prepareDisks.ts b/src/utils/disks/prepareDisks.ts index 3d573e827..dbd6f486b 100644 --- a/src/utils/disks/prepareDisks.ts +++ b/src/utils/disks/prepareDisks.ts @@ -45,7 +45,7 @@ export function prepareWhiteboardVDiskData( ? prepareWhiteboardPDiskData({...PDisk, NodeId: PDisk?.NodeId ?? NodeId}) : undefined; - const actualPDiskId = PDiskId ?? PDisk?.PDiskId; + const actualPDiskId = PDiskId ?? preparedPDisk?.PDiskId; const vDiskSizeFields = prepareVDiskSizeFields({ AvailableSize: AvailableSize ?? PDisk?.AvailableSize,