Skip to content

feat(PDiskPage): add pdisk attributes, display in 2 columns #1069

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/components/PDiskInfo/PDiskInfo.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
.ydb-pdisk-info {
&__wrapper {
display: flex;
flex-flow: row wrap;
gap: 7px;
}

&__col {
display: flex;
flex-direction: column;
gap: 7px;

width: 500px;
}

&__links {
display: flex;
flex-flow: row wrap;
Expand Down
169 changes: 133 additions & 36 deletions src/components/PDiskInfo/PDiskInfo.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import {getPDiskPagePath} from '../../routes';
import {valueIsDefined} from '../../utils';
import {formatBytes} from '../../utils/bytesParsers';
import {cn} from '../../utils/cn';
import {EMPTY_DATA_PLACEHOLDER} from '../../utils/constants';
import {formatStorageValuesToGb} from '../../utils/dataFormatters/dataFormatters';
import {createPDiskDeveloperUILink} from '../../utils/developerUI/developerUI';
import type {PreparedPDisk} from '../../utils/disks/types';
import {useTypedSelector} from '../../utils/hooks';
import {EntityStatus} from '../EntityStatus/EntityStatus';
import type {InfoViewerItem} from '../InfoViewer';
import {InfoViewer} from '../InfoViewer/InfoViewer';
import type {InfoViewerProps} from '../InfoViewer/InfoViewer';
import {LinkWithIcon} from '../LinkWithIcon/LinkWithIcon';
import {ProgressViewer} from '../ProgressViewer/ProgressViewer';

Expand All @@ -18,18 +18,20 @@ import './PDiskInfo.scss';

const b = cn('ydb-pdisk-info');

interface PDiskInfoProps<T extends PreparedPDisk> extends Omit<InfoViewerProps, 'info'> {
interface GetPDiskInfoOptions<T extends PreparedPDisk> {
pDisk?: T;
nodeId?: number | string | null;
isPDiskPage?: boolean;
isUserAllowedToMakeChanges?: boolean;
}

export function PDiskInfo<T extends PreparedPDisk>({
// eslint-disable-next-line complexity
function getPDiskInfo<T extends PreparedPDisk>({
pDisk,
nodeId,
isPDiskPage = false,
...infoViewerProps
}: PDiskInfoProps<T>) {
isUserAllowedToMakeChanges,
}: GetPDiskInfoOptions<T>) {
const {
PDiskId,
Path,
Expand All @@ -42,22 +44,72 @@ export function PDiskInfo<T extends PreparedPDisk>({
SerialNumber,
TotalSize,
AllocatedSize,
DecommitStatus,
StatusV2,
NumActiveSlots,
ExpectedSlotCount,
LogUsedSize,
LogTotalSize,
SystemSize,
SharedWithOs,
} = pDisk || {};

const pdiskInfo: InfoViewerItem[] = [];
const generalInfo: InfoViewerItem[] = [];

if (valueIsDefined(DecommitStatus)) {
generalInfo.push({
label: pDiskInfoKeyset('decomission-status'),
value: DecommitStatus.replace('DECOMMIT_', ''),
});
}
if (valueIsDefined(Category)) {
generalInfo.push({label: pDiskInfoKeyset('type'), value: Type});
}
if (valueIsDefined(Path)) {
pdiskInfo.push({label: pDiskInfoKeyset('path'), value: Path});
generalInfo.push({label: pDiskInfoKeyset('path'), value: Path});
}
if (valueIsDefined(Guid)) {
pdiskInfo.push({label: pDiskInfoKeyset('guid'), value: Guid});
generalInfo.push({label: pDiskInfoKeyset('guid'), value: Guid});
}
if (valueIsDefined(Category)) {
pdiskInfo.push({label: pDiskInfoKeyset('category'), value: Category});
pdiskInfo.push({label: pDiskInfoKeyset('type'), value: Type});
// SerialNumber could be an empty string ""
if (SerialNumber) {
generalInfo.push({
label: pDiskInfoKeyset('serial-number'),
value: SerialNumber,
});
}
if (valueIsDefined(SharedWithOs)) {
generalInfo.push({
label: pDiskInfoKeyset('shared-with-os'),
value: pDiskInfoKeyset('yes'),
});
}

const statusInfo: InfoViewerItem[] = [];

if (valueIsDefined(StatusV2)) {
statusInfo.push({label: pDiskInfoKeyset('drive-status'), value: StatusV2});
}
pdiskInfo.push({
label: pDiskInfoKeyset('size'),
if (valueIsDefined(State)) {
statusInfo.push({label: pDiskInfoKeyset('state'), value: State});
}
if (valueIsDefined(Device)) {
statusInfo.push({
label: pDiskInfoKeyset('device'),
value: <EntityStatus status={Device} />,
});
}
if (valueIsDefined(Realtime)) {
statusInfo.push({
label: pDiskInfoKeyset('realtime'),
value: <EntityStatus status={Realtime} />,
});
}

const spaceInfo: InfoViewerItem[] = [];

spaceInfo.push({
label: pDiskInfoKeyset('space'),
value: (
<ProgressViewer
value={AllocatedSize}
Expand All @@ -67,36 +119,46 @@ export function PDiskInfo<T extends PreparedPDisk>({
/>
),
});
if (valueIsDefined(State)) {
pdiskInfo.push({label: pDiskInfoKeyset('state'), value: State});
}
if (valueIsDefined(Device)) {
pdiskInfo.push({
label: pDiskInfoKeyset('device'),
value: <EntityStatus status={Device} />,
if (valueIsDefined(NumActiveSlots) && valueIsDefined(ExpectedSlotCount)) {
spaceInfo.push({
label: pDiskInfoKeyset('slots'),
value: <ProgressViewer value={NumActiveSlots} capacity={ExpectedSlotCount} />,
});
}
if (valueIsDefined(Realtime)) {
pdiskInfo.push({
label: pDiskInfoKeyset('realtime'),
value: <EntityStatus status={Realtime} />,
if (valueIsDefined(LogUsedSize) && valueIsDefined(LogTotalSize)) {
spaceInfo.push({
label: pDiskInfoKeyset('log-size'),
value: (
<ProgressViewer
value={LogUsedSize}
capacity={LogTotalSize}
formatValues={formatStorageValuesToGb}
/>
),
});
}
if (valueIsDefined(SerialNumber)) {
pdiskInfo.push({
label: pDiskInfoKeyset('serial-number'),
value: SerialNumber || EMPTY_DATA_PLACEHOLDER,
if (valueIsDefined(SystemSize)) {
spaceInfo.push({
label: pDiskInfoKeyset('system-size'),
value: formatBytes({value: SystemSize}),
});
}

if (valueIsDefined(PDiskId) && valueIsDefined(nodeId)) {
const additionalInfo: InfoViewerItem[] = [];

const shouldDisplayLinks =
(!isPDiskPage || isUserAllowedToMakeChanges) &&
valueIsDefined(PDiskId) &&
valueIsDefined(nodeId);

if (shouldDisplayLinks) {
const pDiskPagePath = getPDiskPagePath(PDiskId, nodeId);
const pDiskInternalViewerPath = createPDiskDeveloperUILink({
nodeId,
pDiskId: PDiskId,
});

pdiskInfo.push({
additionalInfo.push({
label: pDiskInfoKeyset('links'),
value: (
<span className={b('links')}>
Expand All @@ -107,14 +169,49 @@ export function PDiskInfo<T extends PreparedPDisk>({
external={false}
/>
)}
<LinkWithIcon
title={pDiskInfoKeyset('developer-ui')}
url={pDiskInternalViewerPath}
/>
{isUserAllowedToMakeChanges && (
<LinkWithIcon
title={pDiskInfoKeyset('developer-ui')}
url={pDiskInternalViewerPath}
/>
)}
</span>
),
});
}

return <InfoViewer info={pdiskInfo} {...infoViewerProps} />;
return [generalInfo, statusInfo, spaceInfo, additionalInfo];
}

interface PDiskInfoProps<T extends PreparedPDisk> extends GetPDiskInfoOptions<T> {
className?: string;
}

export function PDiskInfo<T extends PreparedPDisk>({
pDisk,
nodeId,
isPDiskPage = false,
className,
}: PDiskInfoProps<T>) {
const {isUserAllowedToMakeChanges} = useTypedSelector((state) => state.authentication);

const [generalInfo, statusInfo, spaceInfo, additionalInfo] = getPDiskInfo({
pDisk,
nodeId,
isPDiskPage,
isUserAllowedToMakeChanges,
});

return (
<div className={b('wrapper', className)}>
<div className={b('col')}>
<InfoViewer info={generalInfo} renderEmptyState={() => null} />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think if we start using DefinitionList from @gravity-ui/components?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I try to use it for new components (e.g. ViewInfo), but don't refactor old components (maybe someday I'll do it separately, currently I don't want to mix features with refactoring)

<InfoViewer info={spaceInfo} renderEmptyState={() => null} />
</div>
<div className={b('col')}>
<InfoViewer info={statusInfo} renderEmptyState={() => null} />
<InfoViewer info={additionalInfo} renderEmptyState={() => null} />
</div>
</div>
);
}
20 changes: 15 additions & 5 deletions src/components/PDiskInfo/i18n/en.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
{
"decomission-status": "Decomission Status",
"type": "Type",
"path": "Path",
"guid": "GUID",
"category": "Category",
"type": "Type",
"size": "Size",
"serial-number": "Serial Number",
"shared-with-os": "SharedWithOs",

"drive-status": "Drive Status",
"state": "State",
"device": "Device",
"realtime": "Realtime",
"serial-number": "SerialNumber",

"space": "Space",
"slots": "Slots",
"log-size": "Log Size",
"system-size": "System Size",

"links": "Links",

"developer-ui": "Developer UI",
"pdisk-page": "PDisk page"
"pdisk-page": "PDisk page",

"yes": "Yes"
}
53 changes: 33 additions & 20 deletions src/store/reducers/pdisk/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type {TPDiskInfoResponse} from '../../../types/api/pdisk';
import type {TStorageInfo} from '../../../types/api/storage';
import type {TEvSystemStateResponse} from '../../../types/api/systemState';
import {getArray} from '../../../utils';
import {getArray, valueIsDefined} from '../../../utils';
import {preparePDiskData, prepareVDiskData} from '../../../utils/disks/prepareDisks';
import {prepareNodeSystemState} from '../../../utils/nodes';
import type {PreparedStorageGroup} from '../storage/types';
Expand All @@ -18,24 +18,33 @@ export function preparePDiskDataResponse([pdiskResponse = {}, nodeResponse]: [
const {PDisk: WhiteboardPDiskData = {}, VDisks: WhiteboardVDisksData = []} = Whiteboard;
const {PDisk: BSCPDiskData = {}} = BSC;

const {ExpectedSlotCount, EnforcedDynamicSlotSize} = BSCPDiskData;

const preparedPDisk = preparePDiskData(WhiteboardPDiskData);

const {LogUsedSize, LogTotalSize, TotalSize: PDiskTotalSize, SystemSize} = preparedPDisk;

const logSlot: SlotItem<'log'> = {
SlotType: 'log',
Used: Number(LogUsedSize),
Total: Number(LogTotalSize),
UsagePercent: (Number(LogUsedSize) * 100) / Number(LogTotalSize),
Severity: 1,
SlotData: {
LogUsedSize,
LogTotalSize,
SystemSize,
},
};
const preparedPDisk = preparePDiskData(WhiteboardPDiskData, BSCPDiskData);

const {
LogUsedSize,
LogTotalSize,
TotalSize: PDiskTotalSize,
SystemSize,
ExpectedSlotCount,
EnforcedDynamicSlotSize,
} = preparedPDisk;

let logSlot: SlotItem<'log'> | undefined;

if (valueIsDefined(LogTotalSize)) {
logSlot = {
SlotType: 'log',
Used: Number(LogUsedSize),
Total: Number(LogTotalSize),
UsagePercent: (Number(LogUsedSize) * 100) / Number(LogTotalSize),
Severity: 1,
SlotData: {
LogUsedSize,
LogTotalSize,
SystemSize,
},
};
}

const preparedVDisks = WhiteboardVDisksData.map(prepareVDiskData).sort(
(disk1, disk2) => Number(disk2.VDiskSlotId) - Number(disk1.VDiskSlotId),
Expand Down Expand Up @@ -82,7 +91,11 @@ export function preparePDiskDataResponse([pdiskResponse = {}, nodeResponse]: [
});
}

const diskSlots = [logSlot, ...vdisksSlots, ...emptySlots];
const diskSlots: PDiskData['SlotItems'] = [...vdisksSlots, ...emptySlots];

if (logSlot) {
diskSlots.unshift(logSlot);
}

const rawNode = nodeResponse.SystemStateInfo?.[0];
const preparedNode = prepareNodeSystemState(rawNode);
Expand Down
8 changes: 6 additions & 2 deletions src/utils/disks/prepareDisks.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type {TPDiskStateInfo} from '../../types/api/pdisk';
import type {TPDiskInfo, TPDiskStateInfo} from '../../types/api/pdisk';
import type {TVDiskStateInfo} from '../../types/api/vdisk';

import {calculatePDiskSeverity} from './calculatePDiskSeverity';
Expand Down Expand Up @@ -35,7 +35,10 @@ export function prepareVDiskData(vdiskState: TVDiskStateInfo = {}): PreparedVDis
};
}

export function preparePDiskData(pdiskState: TPDiskStateInfo = {}): PreparedPDisk {
export function preparePDiskData(
pdiskState: TPDiskStateInfo = {},
bscPDiskInfo: TPDiskInfo = {},
): PreparedPDisk {
const {AvailableSize, TotalSize, Category} = pdiskState;

const Type = getPDiskType(Category);
Expand All @@ -48,6 +51,7 @@ export function preparePDiskData(pdiskState: TPDiskStateInfo = {}): PreparedPDis
const Severity = calculatePDiskSeverity(pdiskState, allocatedPercent);

return {
...bscPDiskInfo,
...pdiskState,
Type,
Severity,
Expand Down
Loading
Loading