Skip to content
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
7 changes: 6 additions & 1 deletion src/components/DiskStatus/DiskStatus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ export interface DiskStatusLabelData {
dangerHeavy?: boolean;
}

export type TitledDiskStatusLabelData = Omit<DiskStatusLabelData, 'title' | 'value'> & {
title: string;
value?: string;
};

export function DiskStatusLabel({
value,
theme = 'normal',
Expand All @@ -37,7 +42,7 @@ export function DiskStatusLabel({
dangerHeavy,
size = 's',
className,
}: DiskStatusLabelData & Pick<LabelProps, 'size' | 'className'>) {
}: (DiskStatusLabelData | TitledDiskStatusLabelData) & Pick<LabelProps, 'size' | 'className'>) {
const label = (
<Label
size={size}
Expand Down
15 changes: 5 additions & 10 deletions src/components/PDiskInfo/statuses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@ import {Ban, Check, CircleQuestionFill, CircleXmarkFill} from '@gravity-ui/icons

import type {EDecommitStatus, EDriveStatus, EMaintenanceStatus} from '../../types/api/pdisk';
import {TPDiskState} from '../../types/api/pdisk';
import {
NOT_AVAILABLE_SEVERITY,
NUMERIC_SEVERITY_TO_LABEL_VIEW,
SOLID_RED_SEVERITY,
} from '../../utils/disks/constants';
import {NOT_AVAILABLE_SEVERITY, NUMERIC_SEVERITY_TO_LABEL_VIEW} from '../../utils/disks/constants';
import {
getPDiskDecommitDisplayState,
getPDiskDriveDisplayState,
Expand Down Expand Up @@ -35,13 +31,12 @@ function getLabelView({
DiskStatusLabelData,
'theme' | 'icon' | 'dangerHeavy'
> {
const labelView = NUMERIC_SEVERITY_TO_LABEL_VIEW[severity];

return {
theme:
severity === NOT_AVAILABLE_SEVERITY
? 'unknown'
: NUMERIC_SEVERITY_TO_LABEL_VIEW[severity]?.theme,
theme: severity === NOT_AVAILABLE_SEVERITY ? 'unknown' : labelView?.theme,
icon,
dangerHeavy: severity === SOLID_RED_SEVERITY,
dangerHeavy: Boolean(labelView?.dangerHeavy),
};
}

Expand Down
7 changes: 4 additions & 3 deletions src/components/VDiskStatus/statuses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {EVDiskDetailedReplicationStatus, EVDiskState} from '../../types/api/vdis
import {
NOT_AVAILABLE_SEVERITY,
NUMERIC_SEVERITY_TO_LABEL_VIEW,
SOLID_RED_SEVERITY,
VDISK_STATE_SEVERITY_FOR_STATE_MODE,
} from '../../utils/disks/constants';
import {calculateStateIcon} from '../../utils/disks/iconCalculators';
Expand Down Expand Up @@ -62,11 +61,13 @@ export function getVDiskStateLabel(data: PreparedVDisk): DiskStatusLabelData {
},
};

const labelView = NUMERIC_SEVERITY_TO_LABEL_VIEW[severity];

return {
...labels[state],
theme: NUMERIC_SEVERITY_TO_LABEL_VIEW[severity].theme,
theme: labelView.theme,
icon: state === EVDiskState.OK ? Check : calculateStateIcon(data),
dangerHeavy: severity === SOLID_RED_SEVERITY,
dangerHeavy: Boolean(labelView.dangerHeavy),
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {Flex, Icon, Label, Popover} from '@gravity-ui/uikit';
import {isNil} from 'lodash';

import {CellWithPopover} from '../../../../components/CellWithPopover/CellWithPopover';
import {DiskStatusLabel} from '../../../../components/DiskStatus/DiskStatus';
import {EntityName} from '../../../../components/EntityName/EntityName';
import {StatusIcon} from '../../../../components/StatusIcon/StatusIcon';
import {TitleWithHelpMark} from '../../../../components/TitleWithHelpmark/TitleWithHelpmark';
Expand Down Expand Up @@ -35,6 +36,7 @@ import {
} from '../../Disks/constants';
import {VDisks} from '../../VDisks/VDisks';
import {getDegradedSeverity} from '../../utils';
import {getStorageGroupStateLabel} from '../../utils/getStorageGroupStateLabel';
import i18n from '../i18n';

import {
Expand Down Expand Up @@ -122,9 +124,14 @@ const degradedColumn: StorageGroupsColumn = {
resizeMinWidth: 110,
render: ({row}) =>
row.Degraded ? (
<Label theme={getDegradedSeverity(row)}>Degraded: {row.Degraded}</Label>
<DiskStatusLabel
size="xs"
title="Degraded"
value={String(row.Degraded)}
theme={getDegradedSeverity(row)}
/>
) : (
'-'
EMPTY_DATA_PLACEHOLDER
),
align: DataTable.LEFT,
defaultOrder: DataTable.DESCENDING,
Expand All @@ -133,7 +140,21 @@ const stateColumn: StorageGroupsColumn = {
name: STORAGE_GROUPS_COLUMNS_IDS.State,
header: STORAGE_GROUPS_COLUMNS_TITLES.State,
width: 150,
render: ({row}) => row.State ?? EMPTY_DATA_PLACEHOLDER,
render: ({row}) => {
const state = getStorageGroupStateLabel(row.State);

return state ? (
<DiskStatusLabel
size="xs"
title={state.title}
value={state.value}
theme={state.theme}
dangerHeavy={state.dangerHeavy}
/>
) : (
EMPTY_DATA_PLACEHOLDER
);
},
align: DataTable.LEFT,
defaultOrder: DataTable.DESCENDING,
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import {getStorageGroupStateLabel} from '../getStorageGroupStateLabel';

describe('getStorageGroupStateLabel', () => {
test.each([
{state: 'ok', title: 'Ok', value: undefined, theme: 'success', dangerHeavy: false},
{
state: 'replicating',
title: 'Replicating',
value: undefined,
theme: 'info',
dangerHeavy: false,
},
{
state: 'starting',
title: 'Starting',
value: undefined,
theme: 'warning',
dangerHeavy: false,
},
{state: 'dead', title: 'Dead', value: undefined, theme: 'danger', dangerHeavy: true},
{
state: 'replicating:3',
title: 'Replicating',
value: '3',
theme: 'info',
dangerHeavy: false,
},
{state: 'starting:2', title: 'Starting', value: '2', theme: 'warning', dangerHeavy: false},
{state: 'degraded:1', title: 'Degraded', value: '1', theme: 'warning', dangerHeavy: false},
{state: 'degraded:2', title: 'Degraded', value: '2', theme: 'danger', dangerHeavy: false},
{state: 'dead:3', title: 'Dead', value: '3', theme: 'danger', dangerHeavy: true},
{
state: 'replicating:1(2)',
title: 'Replicating',
value: '1 (2)',
theme: 'info',
dangerHeavy: false,
},
{
state: 'starting:1(3)',
title: 'Starting',
value: '1 (3)',
theme: 'warning',
dangerHeavy: false,
},
{
state: 'degraded:1(3)',
title: 'Degraded',
value: '1 (3)',
theme: 'warning',
dangerHeavy: false,
},
{
state: 'degraded:2(3,1)',
title: 'Degraded',
value: '2 (3,1)',
theme: 'danger',
dangerHeavy: false,
},
{
state: 'dead:3(3,1,1)',
title: 'Dead',
value: '3 (3,1,1)',
theme: 'danger',
dangerHeavy: true,
},
])('formats $state with its expected appearance', ({state, ...expected}) => {
expect(getStorageGroupStateLabel(state)).toEqual(expected);
});

test.each([
{state: 'degraded', title: 'Degraded', value: undefined},
{state: 'degraded:0', title: 'Degraded', value: '0'},
{state: 'degraded:3', title: 'Degraded', value: '3'},
{state: 'degraded:10', title: 'Degraded', value: '10'},
{state: 'degraded:20', title: 'Degraded', value: '20'},
{state: 'degraded:3(3,1,1)', title: 'Degraded', value: '3 (3,1,1)'},
{state: 'degraded:unknown', title: 'Degraded', value: 'unknown'},
])('keeps $state visibly degraded when the count is unsupported', ({state, ...expected}) => {
expect(getStorageGroupStateLabel(state)).toEqual({
...expected,
theme: 'warning',
dangerHeavy: false,
});
});

test.each([undefined, null, '', ' \n\t '])('returns no label for %p', (state) => {
expect(getStorageGroupStateLabel(state)).toBeUndefined();
});

test('normalizes surrounding whitespace and spacing before the details', () => {
expect(getStorageGroupStateLabel(' degraded: 2 (3, 1) ')).toEqual({
title: 'Degraded',
value: '2 (3, 1)',
theme: 'danger',
dangerHeavy: false,
});
});

test('preserves an unknown status and its details with a neutral appearance', () => {
expect(getStorageGroupStateLabel('future-state:7(2,1)')).toEqual({
title: 'Future-state',
value: '7 (2,1)',
theme: 'normal',
dangerHeavy: false,
});
});
});
49 changes: 49 additions & 0 deletions src/containers/Storage/utils/getStorageGroupStateLabel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import type {TitledDiskStatusLabelData} from '../../../components/DiskStatus/DiskStatus';
import {
DATA_SEVERITY,
NUMERIC_SEVERITY_TO_LABEL_VIEW,
SOLID_RED_SEVERITY,
} from '../../../utils/disks/constants';
import type {DisplaySeverity} from '../../../utils/disks/types';

const stateSeverities = new Map<string, DisplaySeverity>([
['ok', DATA_SEVERITY.GREEN],
['replicating', DATA_SEVERITY.BLUE],
['starting', DATA_SEVERITY.YELLOW],
['degraded', DATA_SEVERITY.YELLOW],
['dead', SOLID_RED_SEVERITY],
]);

export function getStorageGroupStateLabel(
state?: string | null,
): TitledDiskStatusLabelData | undefined {
const normalizedState = state?.trim();

if (!normalizedState) {
return undefined;
}

const match = normalizedState.match(/^([^:]+)(?::\s*(.+))?$/);
const status = (match?.[1] ?? normalizedState).trim();
const value = match?.[2]?.trim();
let severity = stateSeverities.get(status) ?? DATA_SEVERITY.GREY;

if (status === 'degraded') {
const count = value?.split('(', 1)[0].trim();
Comment thread
kkdras marked this conversation as resolved.

if (count === '2') {
severity = DATA_SEVERITY.RED;
}
}

const labelView = NUMERIC_SEVERITY_TO_LABEL_VIEW[severity];
const title = status[0].toUpperCase() + status.slice(1);
Comment thread
kkdras marked this conversation as resolved.
Comment thread
kkdras marked this conversation as resolved.
const formattedValue = value?.replace(/\s*\(/g, ' (');

return {
title,
value: formattedValue,
theme: labelView.theme,
dangerHeavy: Boolean(labelView.dangerHeavy),
};
}
2 changes: 2 additions & 0 deletions src/utils/disks/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ export const PDISK_STATE_SEVERITY = {
export interface LabelVisualConfig {
theme: LabelProps['theme'];
icon?: IconData;
dangerHeavy?: boolean;
}

export const NUMERIC_SEVERITY_TO_LABEL_VIEW: Record<number, LabelVisualConfig> = {
Expand All @@ -159,6 +160,7 @@ export const NUMERIC_SEVERITY_TO_LABEL_VIEW: Record<number, LabelVisualConfig> =
},
[SOLID_RED_SEVERITY]: {
theme: 'danger',
dangerHeavy: true,
},
[DISK_COLOR_STATE_TO_NUMERIC_SEVERITY.Blue]: {
theme: 'info',
Expand Down
Loading