Skip to content

Commit c4cdd35

Browse files
authored
feat(TenantOverview): add storage tab to tenant diagnostics (#541)
1 parent f46b03d commit c4cdd35

File tree

28 files changed

+1013
-283
lines changed

28 files changed

+1013
-283
lines changed

src/components/ProgressViewer/ProgressViewer.tsx

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ interface ProgressViewerProps {
4646

4747
export function ProgressViewer({
4848
value,
49-
capacity = 100,
49+
capacity,
5050
formatValues,
5151
percents,
5252
className,
@@ -84,13 +84,19 @@ export function ProgressViewer({
8484

8585
const text = fillWidth > 60 ? 'contrast0' : 'contrast70';
8686

87-
if (!isNaN(fillWidth)) {
87+
const renderContent = () => {
88+
if (capacityText) {
89+
return `${valueText} ${divider} ${capacityText}`;
90+
}
91+
92+
return valueText;
93+
};
94+
95+
if (!isNaN(Number(value))) {
8896
return (
8997
<div className={b({size}, className)}>
9098
<div className={b('line', {bg})} style={lineStyle}></div>
91-
<span
92-
className={b('text', {text})}
93-
>{`${valueText} ${divider} ${capacityText}`}</span>
99+
<span className={b('text', {text})}>{renderContent()}</span>
94100
</div>
95101
);
96102
}

src/containers/Nodes/getNodesColumns.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ export function getNodesColumns({
120120
row.LoadAverage && row.LoadAverage.length > 0 ? (
121121
<ProgressViewer
122122
value={row.LoadAverage[0]}
123+
capacity={100}
123124
percents={true}
124125
colorizeProgress={true}
125126
/>

src/containers/Storage/PDiskPopup/PDiskPopup.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export const preparePDiskData = (data: TPDiskStateInfo, nodes?: NodesMap) => {
2323
const {AvailableSize, TotalSize, State, PDiskId, NodeId, Path, Realtime, Device} = data;
2424

2525
const pdiskData: InfoViewerItem[] = [
26-
{label: 'PDisk', value: getPDiskId({NodeId, PDiskId})},
26+
{label: 'PDisk', value: getPDiskId({NodeId, PDiskId}) || '-'},
2727
{label: 'State', value: State || 'not available'},
2828
{label: 'Type', value: getPDiskType(data) || 'unknown'},
2929
];

src/containers/Storage/StorageGroups/StorageGroups.tsx

Lines changed: 3 additions & 232 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,12 @@
1-
import cn from 'bem-cn-lite';
2-
31
import DataTable, {Column, Settings, SortOrder} from '@gravity-ui/react-data-table';
4-
import {Icon, Label, Popover, PopoverBehavior} from '@gravity-ui/uikit';
52

6-
import type {ValueOf} from '../../../types/common';
73
import type {NodesMap} from '../../../types/store/nodesList';
84
import type {PreparedStorageGroup, VisibleEntities} from '../../../store/reducers/storage/types';
95
import type {HandleSort} from '../../../utils/hooks/useTableSort';
10-
116
import {VISIBLE_ENTITIES} from '../../../store/reducers/storage/constants';
12-
import {bytesToGB, bytesToSpeed} from '../../../utils/utils';
13-
import {stringifyVdiskId} from '../../../utils/dataFormatters/dataFormatters';
14-
import {getUsage, isFullVDiskData, isSortableStorageProperty} from '../../../utils/storage';
15-
16-
import shieldIcon from '../../../assets/icons/shield.svg';
17-
import {Stack} from '../../../components/Stack/Stack';
18-
import EntityStatus from '../../../components/EntityStatus/EntityStatus';
19-
7+
import {isSortableStorageProperty} from '../../../utils/storage';
208
import {EmptyFilter} from '../EmptyFilter/EmptyFilter';
21-
import {VDisk} from '../VDisk';
22-
import {getDegradedSeverity, getUsageSeverityForStorageGroup} from '../utils';
9+
import {getStorageGroupsColumns} from './getStorageGroupsColumns';
2310

2411
import i18n from './i18n';
2512
import './StorageGroups.scss';
@@ -39,8 +26,6 @@ const TableColumnsIds = {
3926
Degraded: 'Degraded',
4027
} as const;
4128

42-
type TableColumnId = ValueOf<typeof TableColumnsIds>;
43-
4429
interface StorageGroupsProps {
4530
data: PreparedStorageGroup[];
4631
nodes?: NodesMap;
@@ -51,23 +36,6 @@ interface StorageGroupsProps {
5136
handleSort?: HandleSort;
5237
}
5338

54-
const tableColumnsNames: Record<TableColumnId, string> = {
55-
PoolName: 'Pool Name',
56-
Kind: 'Type',
57-
Erasure: 'Erasure',
58-
GroupId: 'Group ID',
59-
Used: 'Used',
60-
Limit: 'Limit',
61-
UsedSpaceFlag: 'Space',
62-
Usage: 'Usage',
63-
Read: 'Read',
64-
Write: 'Write',
65-
VDisks: 'VDisks',
66-
Degraded: 'Degraded',
67-
};
68-
69-
const b = cn('global-storage-groups');
70-
7139
export function StorageGroups({
7240
data,
7341
tableSettings,
@@ -77,204 +45,7 @@ export function StorageGroups({
7745
sort,
7846
handleSort,
7947
}: StorageGroupsProps) {
80-
const rawColumns: Column<PreparedStorageGroup>[] = [
81-
{
82-
name: TableColumnsIds.PoolName,
83-
header: tableColumnsNames[TableColumnsIds.PoolName],
84-
width: 250,
85-
render: ({row}) => {
86-
const splitted = row.PoolName?.split('/');
87-
return (
88-
<div className={b('pool-name-wrapper')}>
89-
{splitted && (
90-
<Popover
91-
content={row.PoolName}
92-
placement={['right']}
93-
behavior={PopoverBehavior.Immediate}
94-
>
95-
<span className={b('pool-name')}>
96-
{splitted[splitted.length - 1]}
97-
</span>
98-
</Popover>
99-
)}
100-
</div>
101-
);
102-
},
103-
align: DataTable.LEFT,
104-
},
105-
{
106-
name: TableColumnsIds.Kind,
107-
header: tableColumnsNames[TableColumnsIds.Kind],
108-
// prettier-ignore
109-
render: ({row}) => (
110-
<>
111-
<Label>{row.Kind || '—'}</Label>
112-
{' '}
113-
{row.Encryption && (
114-
<Popover
115-
content={i18n('encrypted')}
116-
placement="right"
117-
behavior={PopoverBehavior.Immediate}
118-
>
119-
<Label>
120-
<Icon data={shieldIcon} />
121-
</Label>
122-
</Popover>
123-
)}
124-
</>
125-
),
126-
},
127-
{
128-
name: TableColumnsIds.Erasure,
129-
header: tableColumnsNames[TableColumnsIds.Erasure],
130-
render: ({row}) => (row.ErasureSpecies ? row.ErasureSpecies : '-'),
131-
align: DataTable.LEFT,
132-
},
133-
{
134-
name: TableColumnsIds.Degraded,
135-
header: tableColumnsNames[TableColumnsIds.Degraded],
136-
width: 100,
137-
render: ({row}) =>
138-
row.Degraded ? (
139-
<Label theme={getDegradedSeverity(row)}>Degraded: {row.Degraded}</Label>
140-
) : (
141-
'-'
142-
),
143-
align: DataTable.LEFT,
144-
defaultOrder: DataTable.DESCENDING,
145-
},
146-
{
147-
name: TableColumnsIds.Usage,
148-
header: tableColumnsNames[TableColumnsIds.Usage],
149-
width: 100,
150-
render: ({row}) => {
151-
// without a limit the usage can be evaluated as 0,
152-
// but the absence of a value is more clear
153-
return row.Limit ? (
154-
<Label
155-
theme={getUsageSeverityForStorageGroup(row.Usage)}
156-
className={b('usage-label', {overload: row.Usage >= 90})}
157-
>
158-
{row.Usage}%
159-
</Label>
160-
) : (
161-
'-'
162-
);
163-
},
164-
// without a limit exclude usage from sort to display at the bottom
165-
sortAccessor: (row) => (row.Limit ? getUsage(row) : null),
166-
align: DataTable.LEFT,
167-
},
168-
{
169-
name: TableColumnsIds.GroupId,
170-
header: tableColumnsNames[TableColumnsIds.GroupId],
171-
width: 130,
172-
render: ({row}) => {
173-
return <span className={b('group-id')}>{row.GroupID}</span>;
174-
},
175-
sortAccessor: (row) => Number(row.GroupID),
176-
align: DataTable.RIGHT,
177-
},
178-
{
179-
name: TableColumnsIds.Used,
180-
header: tableColumnsNames[TableColumnsIds.Used],
181-
width: 100,
182-
render: ({row}) => {
183-
return bytesToGB(row.Used, true);
184-
},
185-
align: DataTable.RIGHT,
186-
},
187-
{
188-
name: TableColumnsIds.Limit,
189-
header: tableColumnsNames[TableColumnsIds.Limit],
190-
width: 100,
191-
render: ({row}) => {
192-
return bytesToGB(row.Limit);
193-
},
194-
align: DataTable.RIGHT,
195-
},
196-
{
197-
name: TableColumnsIds.UsedSpaceFlag,
198-
header: tableColumnsNames[TableColumnsIds.UsedSpaceFlag],
199-
width: 110,
200-
render: ({row}) => {
201-
const value = row.UsedSpaceFlag;
202-
203-
let color = 'Red';
204-
205-
if (value < 100) {
206-
color = 'Green';
207-
} else if (value < 10000) {
208-
color = 'Yellow';
209-
} else if (value < 1000000) {
210-
color = 'Orange';
211-
}
212-
return <EntityStatus status={color} />;
213-
},
214-
align: DataTable.CENTER,
215-
},
216-
217-
{
218-
name: TableColumnsIds.Read,
219-
header: tableColumnsNames[TableColumnsIds.Read],
220-
width: 100,
221-
render: ({row}) => {
222-
return row.Read ? bytesToSpeed(row.Read) : '-';
223-
},
224-
align: DataTable.RIGHT,
225-
},
226-
{
227-
name: TableColumnsIds.Write,
228-
header: tableColumnsNames[TableColumnsIds.Write],
229-
width: 100,
230-
render: ({row}) => {
231-
return row.Write ? bytesToSpeed(row.Write) : '-';
232-
},
233-
align: DataTable.RIGHT,
234-
},
235-
{
236-
name: TableColumnsIds.VDisks,
237-
className: b('vdisks-column'),
238-
header: tableColumnsNames[TableColumnsIds.VDisks],
239-
render: ({row}) => (
240-
<div className={b('vdisks-wrapper')}>
241-
{row.VDisks?.map((vDisk) => {
242-
const donors = vDisk.Donors;
243-
244-
return donors && donors.length > 0 ? (
245-
<Stack
246-
className={b('vdisks-item')}
247-
key={stringifyVdiskId(vDisk.VDiskId)}
248-
>
249-
<VDisk data={vDisk} nodes={nodes} />
250-
{donors.map((donor) => {
251-
const isFullData = isFullVDiskData(donor);
252-
253-
return (
254-
<VDisk
255-
data={isFullData ? donor : {...donor, DonorMode: true}}
256-
// donor and acceptor are always in the same group
257-
nodes={nodes}
258-
key={stringifyVdiskId(
259-
isFullData ? donor.VDiskId : donor,
260-
)}
261-
/>
262-
);
263-
})}
264-
</Stack>
265-
) : (
266-
<div className={b('vdisks-item')} key={stringifyVdiskId(vDisk.VDiskId)}>
267-
<VDisk data={vDisk} nodes={nodes} />
268-
</div>
269-
);
270-
})}
271-
</div>
272-
),
273-
align: DataTable.CENTER,
274-
sortable: false,
275-
width: 900,
276-
},
277-
];
48+
const rawColumns: Column<PreparedStorageGroup>[] = getStorageGroupsColumns(nodes);
27849

27950
let columns = rawColumns.map((column) => ({
28051
...column,

0 commit comments

Comments
 (0)