Skip to content

Commit 90a3403

Browse files
feat(StorageNodes): add columns, use the same nodes columns (#1396)
1 parent 0a4734e commit 90a3403

File tree

26 files changed

+565
-526
lines changed

26 files changed

+565
-526
lines changed

src/components/NodeHostWrapper/NodeHostWrapper.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,23 @@
11
import {PopoverBehavior} from '@gravity-ui/uikit';
22

33
import {getDefaultNodePath} from '../../containers/Node/NodePages';
4-
import type {NodesPreparedEntity} from '../../store/reducers/nodes/types';
54
import type {NodeAddress} from '../../types/additionalProps';
5+
import type {TSystemStateInfo} from '../../types/api/nodes';
66
import {createDeveloperUILinkWithNodeId} from '../../utils/developerUI/developerUI';
77
import {isUnavailableNode} from '../../utils/nodes';
88
import {CellWithPopover} from '../CellWithPopover/CellWithPopover';
99
import {DeveloperUILinkButton} from '../DeveloperUILinkButton/DeveloperUILinkButton';
1010
import {EntityStatus} from '../EntityStatus/EntityStatus';
1111
import {NodeEndpointsTooltipContent} from '../TooltipsContent';
1212

13+
export type NodeHostData = NodeAddress &
14+
Pick<TSystemStateInfo, 'SystemState'> & {
15+
NodeId: string | number;
16+
TenantName?: string;
17+
};
18+
1319
interface NodeHostWrapperProps {
14-
node: NodesPreparedEntity;
20+
node: NodeHostData;
1521
getNodeRef?: (node?: NodeAddress) => string | null;
1622
database?: string;
1723
}

src/components/TabletsStatistic/TabletsStatistic.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,23 @@ const prepareTablets = (tablets: TTabletStateInfo[]) => {
2626

2727
interface TabletsStatisticProps {
2828
tablets: TTabletStateInfo[];
29-
path: string | undefined;
29+
tenantName: string | undefined;
3030
nodeId: string | number;
3131
backend?: string;
3232
}
3333

34-
export const TabletsStatistic = ({tablets = [], path, nodeId, backend}: TabletsStatisticProps) => {
34+
export const TabletsStatistic = ({
35+
tablets = [],
36+
tenantName,
37+
nodeId,
38+
backend,
39+
}: TabletsStatisticProps) => {
3540
const renderTabletInfo = (item: ReturnType<typeof prepareTablets>[number], index: number) => {
3641
const tabletsPath = createHref(
3742
routes.node,
3843
{id: nodeId, activeTab: TABLETS},
3944
{
40-
tenantName: path,
45+
tenantName,
4146
backend,
4247
},
4348
);
Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
import DataTable from '@gravity-ui/react-data-table';
2+
3+
import {getLoadSeverityForNode} from '../../store/reducers/nodes/utils';
4+
import type {TPoolStats} from '../../types/api/nodes';
5+
import type {TTabletStateInfo} from '../../types/api/tablet';
6+
import {EMPTY_DATA_PLACEHOLDER} from '../../utils/constants';
7+
import {formatStorageValuesToGb} from '../../utils/dataFormatters/dataFormatters';
8+
import {CellWithPopover} from '../CellWithPopover/CellWithPopover';
9+
import {NodeHostWrapper} from '../NodeHostWrapper/NodeHostWrapper';
10+
import type {NodeHostData} from '../NodeHostWrapper/NodeHostWrapper';
11+
import {PoolsGraph} from '../PoolsGraph/PoolsGraph';
12+
import {ProgressViewer} from '../ProgressViewer/ProgressViewer';
13+
import {TabletsStatistic} from '../TabletsStatistic';
14+
import {UsageLabel} from '../UsageLabel/UsageLabel';
15+
16+
import {NODES_COLUMNS_IDS, NODES_COLUMNS_TITLES} from './constants';
17+
import type {Column, GetNodesColumnsParams} from './types';
18+
19+
export function getNodeIdColumn<T extends {NodeId?: string | number}>(): Column<T> {
20+
return {
21+
name: NODES_COLUMNS_IDS.NodeId,
22+
header: '#',
23+
width: 80,
24+
render: ({row}) => row.NodeId,
25+
align: DataTable.RIGHT,
26+
};
27+
}
28+
export function getHostColumn<T extends NodeHostData>({
29+
getNodeRef,
30+
database,
31+
}: GetNodesColumnsParams): Column<T> {
32+
return {
33+
name: NODES_COLUMNS_IDS.Host,
34+
header: NODES_COLUMNS_TITLES.Host,
35+
render: ({row}) => {
36+
return <NodeHostWrapper node={row} getNodeRef={getNodeRef} database={database} />;
37+
},
38+
width: 350,
39+
align: DataTable.LEFT,
40+
};
41+
}
42+
export function getNodeNameColumn<T extends {NodeName?: string}>(): Column<T> {
43+
return {
44+
name: NODES_COLUMNS_IDS.NodeName,
45+
header: NODES_COLUMNS_TITLES.NodeName,
46+
align: DataTable.LEFT,
47+
render: ({row}) => row.NodeName || EMPTY_DATA_PLACEHOLDER,
48+
width: 200,
49+
};
50+
}
51+
export function getDataCenterColumn<T extends {DC?: string}>(): Column<T> {
52+
return {
53+
name: NODES_COLUMNS_IDS.DC,
54+
header: NODES_COLUMNS_TITLES.DC,
55+
align: DataTable.LEFT,
56+
render: ({row}) => row.DC || EMPTY_DATA_PLACEHOLDER,
57+
width: 60,
58+
};
59+
}
60+
export function getRackColumn<T extends {Rack?: string}>(): Column<T> {
61+
return {
62+
name: NODES_COLUMNS_IDS.Rack,
63+
header: NODES_COLUMNS_TITLES.Rack,
64+
align: DataTable.LEFT,
65+
render: ({row}) => row.Rack || EMPTY_DATA_PLACEHOLDER,
66+
width: 100,
67+
};
68+
}
69+
export function getVersionColumn<T extends {Version?: string}>(): Column<T> {
70+
return {
71+
name: NODES_COLUMNS_IDS.Version,
72+
header: NODES_COLUMNS_TITLES.Version,
73+
width: 200,
74+
align: DataTable.LEFT,
75+
render: ({row}) => {
76+
return <CellWithPopover content={row.Version}>{row.Version}</CellWithPopover>;
77+
},
78+
};
79+
}
80+
export function getUptimeColumn<T extends {StartTime?: string; Uptime?: string}>(): Column<T> {
81+
return {
82+
name: NODES_COLUMNS_IDS.Uptime,
83+
header: NODES_COLUMNS_TITLES.Uptime,
84+
sortAccessor: ({StartTime}) => (StartTime ? -StartTime : 0),
85+
render: ({row}) => row.Uptime,
86+
align: DataTable.RIGHT,
87+
width: 110,
88+
};
89+
}
90+
export function getMemoryColumn<
91+
T extends {MemoryUsed?: string; MemoryLimit?: string},
92+
>(): Column<T> {
93+
return {
94+
name: NODES_COLUMNS_IDS.Memory,
95+
header: NODES_COLUMNS_TITLES.Memory,
96+
sortAccessor: ({MemoryUsed = 0}) => Number(MemoryUsed),
97+
defaultOrder: DataTable.DESCENDING,
98+
render: ({row}) => (
99+
<ProgressViewer
100+
value={row.MemoryUsed}
101+
capacity={row.MemoryLimit}
102+
formatValues={formatStorageValuesToGb}
103+
colorizeProgress={true}
104+
/>
105+
),
106+
align: DataTable.LEFT,
107+
width: 140,
108+
resizeMinWidth: 140,
109+
};
110+
}
111+
export function getSharedCacheUsageColumn<
112+
T extends {SharedCacheUsed?: string | number; SharedCacheLimit?: string | number},
113+
>(): Column<T> {
114+
return {
115+
name: NODES_COLUMNS_IDS.SharedCacheUsage,
116+
header: NODES_COLUMNS_TITLES.SharedCacheUsage,
117+
render: ({row}) => (
118+
<ProgressViewer
119+
value={row.SharedCacheUsed}
120+
capacity={row.SharedCacheLimit}
121+
formatValues={formatStorageValuesToGb}
122+
colorizeProgress={true}
123+
/>
124+
),
125+
align: DataTable.LEFT,
126+
width: 140,
127+
resizeMinWidth: 140,
128+
};
129+
}
130+
export function getCpuColumn<T extends {PoolStats?: TPoolStats[]}>(): Column<T> {
131+
return {
132+
name: NODES_COLUMNS_IDS.CPU,
133+
header: NODES_COLUMNS_TITLES.CPU,
134+
sortAccessor: ({PoolStats = []}) => Math.max(...PoolStats.map(({Usage}) => Number(Usage))),
135+
defaultOrder: DataTable.DESCENDING,
136+
render: ({row}) =>
137+
row.PoolStats ? <PoolsGraph pools={row.PoolStats} /> : EMPTY_DATA_PLACEHOLDER,
138+
align: DataTable.LEFT,
139+
width: 80,
140+
resizeMinWidth: 60,
141+
};
142+
}
143+
export function getLoadAverageColumn<T extends {LoadAveragePercents?: number[]}>(): Column<T> {
144+
return {
145+
name: NODES_COLUMNS_IDS.LoadAverage,
146+
header: NODES_COLUMNS_TITLES.LoadAverage,
147+
sortAccessor: ({LoadAveragePercents = []}) => LoadAveragePercents[0],
148+
defaultOrder: DataTable.DESCENDING,
149+
render: ({row}) => (
150+
<ProgressViewer
151+
value={
152+
row.LoadAveragePercents && row.LoadAveragePercents.length > 0
153+
? row.LoadAveragePercents[0]
154+
: undefined
155+
}
156+
percents={true}
157+
colorizeProgress={true}
158+
capacity={100}
159+
/>
160+
),
161+
align: DataTable.LEFT,
162+
width: 140,
163+
resizeMinWidth: 140,
164+
};
165+
}
166+
// The same as loadAverage, but more compact
167+
export function getLoadColumn<T extends {LoadAveragePercents?: number[]}>(): Column<T> {
168+
return {
169+
name: NODES_COLUMNS_IDS.Load,
170+
header: NODES_COLUMNS_TITLES.Load,
171+
sortAccessor: ({LoadAveragePercents = []}) => LoadAveragePercents[0],
172+
defaultOrder: DataTable.DESCENDING,
173+
render: ({row}) =>
174+
row.LoadAveragePercents && row.LoadAveragePercents.length > 0 ? (
175+
<UsageLabel
176+
value={row.LoadAveragePercents[0].toFixed()}
177+
theme={getLoadSeverityForNode(row.LoadAveragePercents[0])}
178+
/>
179+
) : (
180+
EMPTY_DATA_PLACEHOLDER
181+
),
182+
align: DataTable.LEFT,
183+
width: 80,
184+
resizeMinWidth: 70,
185+
};
186+
}
187+
export function getSessionsColumn<T extends {TotalSessions?: number}>(): Column<T> {
188+
return {
189+
name: NODES_COLUMNS_IDS.TotalSessions,
190+
header: NODES_COLUMNS_TITLES.TotalSessions,
191+
render: ({row}) => row.TotalSessions ?? EMPTY_DATA_PLACEHOLDER,
192+
align: DataTable.RIGHT,
193+
width: 100,
194+
};
195+
}
196+
export function getTabletsColumn<
197+
T extends {
198+
TenantName?: string;
199+
NodeId: string | number;
200+
Tablets?: TTabletStateInfo[];
201+
},
202+
>({database}: GetNodesColumnsParams): Column<T> {
203+
return {
204+
name: NODES_COLUMNS_IDS.Tablets,
205+
header: NODES_COLUMNS_TITLES.Tablets,
206+
width: 500,
207+
resizeMinWidth: 500,
208+
render: ({row}) => {
209+
return row.Tablets ? (
210+
<TabletsStatistic
211+
tenantName={database ?? row.TenantName}
212+
nodeId={row.NodeId}
213+
tablets={row.Tablets}
214+
/>
215+
) : (
216+
EMPTY_DATA_PLACEHOLDER
217+
);
218+
},
219+
align: DataTable.LEFT,
220+
sortable: false,
221+
};
222+
}
223+
export function getMissingDisksColumn<T extends {Missing?: number}>(): Column<T> {
224+
return {
225+
name: NODES_COLUMNS_IDS.Missing,
226+
header: NODES_COLUMNS_TITLES.Missing,
227+
render: ({row}) => row.Missing,
228+
align: DataTable.CENTER,
229+
defaultOrder: DataTable.DESCENDING,
230+
};
231+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import type {ValueOf} from '../../types/common';
2+
3+
import i18n from './i18n';
4+
5+
export const NODES_COLUMNS_WIDTH_LS_KEY = 'nodesTableColumnsWidth';
6+
7+
export const NODES_COLUMNS_IDS = {
8+
NodeId: 'NodeId',
9+
Host: 'Host',
10+
NodeName: 'NodeName',
11+
DC: 'DC',
12+
Rack: 'Rack',
13+
Version: 'Version',
14+
Uptime: 'Uptime',
15+
Memory: 'Memory',
16+
CPU: 'CPU',
17+
LoadAverage: 'LoadAverage',
18+
Load: 'Load',
19+
SharedCacheUsage: 'SharedCacheUsage',
20+
TotalSessions: 'TotalSessions',
21+
Missing: 'Missing',
22+
Tablets: 'Tablets',
23+
} as const;
24+
25+
export type NodesColumnId = ValueOf<typeof NODES_COLUMNS_IDS>;
26+
27+
// This code is running when module is initialized and correct language may not be set yet
28+
// get functions guarantee that i18n fields will be inited on render with current render language
29+
export const NODES_COLUMNS_TITLES = {
30+
get NodeId() {
31+
return i18n('node-id');
32+
},
33+
get Host() {
34+
return i18n('host');
35+
},
36+
get NodeName() {
37+
return i18n('node-name');
38+
},
39+
get DC() {
40+
return i18n('dc');
41+
},
42+
get Rack() {
43+
return i18n('rack');
44+
},
45+
get Version() {
46+
return i18n('version');
47+
},
48+
get Uptime() {
49+
return i18n('uptime');
50+
},
51+
get Memory() {
52+
return i18n('memory');
53+
},
54+
get CPU() {
55+
return i18n('cpu');
56+
},
57+
get LoadAverage() {
58+
return i18n('load-average');
59+
},
60+
get Load() {
61+
return i18n('load');
62+
},
63+
get SharedCacheUsage() {
64+
return i18n('caches');
65+
},
66+
get TotalSessions() {
67+
return i18n('sessions');
68+
},
69+
get Missing() {
70+
return i18n('missing');
71+
},
72+
get Tablets() {
73+
return i18n('tablets');
74+
},
75+
} as const satisfies Record<NodesColumnId, string>;

src/containers/Nodes/columns/i18n/en.json renamed to src/components/nodesColumns/i18n/en.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"node-id": "Node Id",
2+
"node-id": "Node ID",
33
"host": "Host",
44
"node-name": "Node Name",
55
"dc": "DC",
@@ -11,7 +11,7 @@
1111
"tablets": "Tablets",
1212
"load-average": "Load Average",
1313
"load": "Load",
14-
"process": "Process",
1514
"caches": "Caches",
16-
"sessions": "Sessions"
15+
"sessions": "Sessions",
16+
"missing": "Missing"
1717
}

src/containers/Nodes/columns/i18n/index.ts renamed to src/components/nodesColumns/i18n/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {registerKeysets} from '../../../../utils/i18n';
1+
import {registerKeysets} from '../../../utils/i18n';
22

33
import en from './en.json';
44

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import type {Column as DataTableColumn} from '@gravity-ui/react-data-table';
2+
3+
import type {GetNodeRefFunc} from '../../types/additionalProps';
4+
import type {Column as PaginatedTableColumn} from '../PaginatedTable';
5+
6+
export type Column<T> = PaginatedTableColumn<T> & DataTableColumn<T>;
7+
8+
export interface GetNodesColumnsParams {
9+
getNodeRef?: GetNodeRefFunc;
10+
database?: string;
11+
}

0 commit comments

Comments
 (0)