Skip to content

Commit c31d048

Browse files
feat(ClusterInfo): display groups stats (#598)
1 parent c99b7e2 commit c31d048

File tree

13 files changed

+452
-37
lines changed

13 files changed

+452
-37
lines changed

src/components/ProgressViewer/ProgressViewer.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ Props description:
4444
interface ProgressViewerProps {
4545
value?: number | string;
4646
capacity?: number | string;
47-
formatValues?: (value?: number, capacity?: number) => (string | undefined)[];
47+
formatValues?: (value?: number, capacity?: number) => (string | number | undefined)[];
4848
percents?: boolean;
4949
className?: string;
5050
size?: ProgressViewerSize;

src/containers/Cluster/Cluster.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ function Cluster({
6868
loading: clusterLoading,
6969
wasLoaded: clusterWasLoaded,
7070
error: clusterError,
71+
groupsStats,
7172
} = useTypedSelector((state) => state.cluster);
7273
const {
7374
nodes,
@@ -135,6 +136,7 @@ function Cluster({
135136
<div className={b()} ref={container}>
136137
<ClusterInfo
137138
cluster={cluster}
139+
groupsStats={groupsStats}
138140
versionsValues={versionsValues}
139141
loading={infoLoading}
140142
error={clusterError}

src/containers/Cluster/ClusterInfo/ClusterInfo.scss

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,6 @@
4949
}
5050
}
5151

52-
&__metric-field {
53-
margin-top: -8px;
54-
}
55-
5652
&__system-tablets {
5753
display: flex;
5854
flex-wrap: wrap;
@@ -83,14 +79,27 @@
8379
margin-left: 15px;
8480
padding: 0 !important;
8581
}
86-
8782
&__links {
8883
display: flex;
8984
flex-flow: row wrap;
9085

9186
gap: 12px;
9287
}
9388

89+
&__storage-groups-stats {
90+
display: flex;
91+
flex-direction: column;
92+
gap: 11px;
93+
}
94+
95+
&__groups-stats-bar {
96+
cursor: pointer;
97+
}
98+
99+
&__groups-stats-popup-content {
100+
padding: 12px;
101+
}
102+
94103
&__clipboard-button {
95104
display: flex;
96105
align-items: center;

src/containers/Cluster/ClusterInfo/ClusterInfo.tsx

Lines changed: 104 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {Tablet} from '../../../components/Tablet';
1010
import {ResponseError} from '../../../components/Errors/ResponseError';
1111
import {ExternalLinkWithIcon} from '../../../components/ExternalLinkWithIcon/ExternalLinkWithIcon';
1212
import {IconWrapper as Icon} from '../../../components/Icon/Icon';
13+
import {ContentWithPopup} from '../../../components/ContentWithPopup/ContentWithPopup';
1314

1415
import type {IResponseError} from '../../../types/api/error';
1516
import type {AdditionalClusterProps, ClusterLink} from '../../../types/additionalProps';
@@ -18,39 +19,122 @@ import type {TClusterInfo} from '../../../types/api/cluster';
1819
import {backend, customBackend} from '../../../store';
1920
import {formatStorageValues} from '../../../utils/dataFormatters/dataFormatters';
2021
import {useSetting, useTypedSelector} from '../../../utils/hooks';
22+
import {formatBytes, getSizeWithSignificantDigits} from '../../../utils/bytesParsers';
2123
import {
2224
CLUSTER_DEFAULT_TITLE,
2325
CLUSTER_INFO_HIDDEN_KEY,
2426
DEVELOPER_UI_TITLE,
2527
} from '../../../utils/constants';
28+
import type {
29+
ClusterGroupsStats,
30+
DiskErasureGroupsStats,
31+
DiskGroupsStats,
32+
} from '../../../store/reducers/cluster/types';
2633

2734
import {VersionsBar} from '../VersionsBar/VersionsBar';
2835
import {ClusterInfoSkeleton} from '../ClusterInfoSkeleton/ClusterInfoSkeleton';
36+
import i18n from '../i18n';
2937

3038
import {compareTablets} from './utils';
3139

3240
import './ClusterInfo.scss';
3341

3442
const b = block('cluster-info');
3543

44+
interface GroupsStatsPopupContentProps {
45+
stats: DiskErasureGroupsStats;
46+
}
47+
48+
const GroupsStatsPopupContent = ({stats}: GroupsStatsPopupContentProps) => {
49+
const {diskType, erasure, allocatedSize, availableSize} = stats;
50+
51+
const sizeToConvert = getSizeWithSignificantDigits(Math.max(allocatedSize, availableSize), 2);
52+
53+
const convertedAllocatedSize = formatBytes({value: allocatedSize, size: sizeToConvert});
54+
const convertedAvailableSize = formatBytes({value: availableSize, size: sizeToConvert});
55+
56+
const usage = Math.round((allocatedSize / (allocatedSize + availableSize)) * 100);
57+
58+
const info = [
59+
{
60+
label: i18n('disk-type'),
61+
value: diskType,
62+
},
63+
{
64+
label: i18n('erasure'),
65+
value: erasure,
66+
},
67+
{
68+
label: i18n('allocated'),
69+
value: convertedAllocatedSize,
70+
},
71+
{
72+
label: i18n('available'),
73+
value: convertedAvailableSize,
74+
},
75+
{
76+
label: i18n('usage'),
77+
value: usage + '%',
78+
},
79+
];
80+
81+
return (
82+
<InfoViewer dots={true} info={info} className={b('groups-stats-popup-content')} size="s" />
83+
);
84+
};
85+
86+
interface DiskGroupsStatsProps {
87+
stats: DiskGroupsStats;
88+
}
89+
90+
const DiskGroupsStatsBars = ({stats}: DiskGroupsStatsProps) => {
91+
return (
92+
<div className={b('storage-groups-stats')}>
93+
{Object.values(stats).map((erasureStats) => (
94+
<ContentWithPopup
95+
placement={['right']}
96+
key={erasureStats.erasure}
97+
content={<GroupsStatsPopupContent stats={erasureStats} />}
98+
>
99+
<ProgressViewer
100+
className={b('groups-stats-bar')}
101+
value={erasureStats.createdGroups}
102+
capacity={erasureStats.totalGroups}
103+
/>
104+
</ContentWithPopup>
105+
))}
106+
</div>
107+
);
108+
};
109+
110+
const getGroupsStatsFields = (groupsStats: ClusterGroupsStats) => {
111+
return Object.keys(groupsStats).map((diskType) => {
112+
return {
113+
label: i18n('storage-groups', {diskType}),
114+
value: <DiskGroupsStatsBars stats={groupsStats[diskType]} />,
115+
};
116+
});
117+
};
118+
36119
const getInfo = (
37120
cluster: TClusterInfo,
38121
versionsValues: VersionValue[],
122+
groupsStats: ClusterGroupsStats,
39123
additionalInfo: InfoViewerItem[],
40124
links: ClusterLink[],
41125
) => {
42126
const info: InfoViewerItem[] = [];
43127

44128
if (cluster.DataCenters) {
45129
info.push({
46-
label: 'DC',
130+
label: i18n('dc'),
47131
value: <Tags tags={cluster.DataCenters} />,
48132
});
49133
}
50134

51135
if (cluster.SystemTablets) {
52136
info.push({
53-
label: 'Tablets',
137+
label: i18n('tablets'),
54138
value: (
55139
<div className={b('system-tablets')}>
56140
{cluster.SystemTablets.sort(compareTablets).map((tablet, tabletIndex) => (
@@ -63,46 +147,40 @@ const getInfo = (
63147

64148
if (cluster.Tenants) {
65149
info.push({
66-
label: 'Databases',
150+
label: i18n('databases'),
67151
value: cluster.Tenants,
68152
});
69153
}
70154

71155
info.push(
72156
{
73-
label: 'Nodes',
74-
value: (
75-
<ProgressViewer
76-
className={b('metric-field')}
77-
value={cluster?.NodesAlive}
78-
capacity={cluster?.NodesTotal}
79-
/>
80-
),
157+
label: i18n('nodes'),
158+
value: <ProgressViewer value={cluster?.NodesAlive} capacity={cluster?.NodesTotal} />,
81159
},
82160
{
83-
label: 'Load',
84-
value: (
85-
<ProgressViewer
86-
className={b('metric-field')}
87-
value={cluster?.LoadAverage}
88-
capacity={cluster?.NumberOfCpus}
89-
/>
90-
),
161+
label: i18n('load'),
162+
value: <ProgressViewer value={cluster?.LoadAverage} capacity={cluster?.NumberOfCpus} />,
91163
},
92164
{
93-
label: 'Storage',
165+
label: i18n('storage-size'),
94166
value: (
95167
<ProgressViewer
96-
className={b('metric-field')}
97168
value={cluster?.StorageUsed}
98169
capacity={cluster?.StorageTotal}
99170
formatValues={formatStorageValues}
100171
/>
101172
),
102173
},
174+
);
175+
176+
if (Object.keys(groupsStats).length) {
177+
info.push(...getGroupsStatsFields(groupsStats));
178+
}
179+
180+
info.push(
103181
...additionalInfo,
104182
{
105-
label: 'Links',
183+
label: i18n('links'),
106184
value: (
107185
<div className={b('links')}>
108186
{links.map(({title, url}) => (
@@ -112,7 +190,7 @@ const getInfo = (
112190
),
113191
},
114192
{
115-
label: 'Versions',
193+
label: i18n('versions'),
116194
value: <VersionsBar versionsValues={versionsValues} />,
117195
},
118196
);
@@ -123,6 +201,7 @@ const getInfo = (
123201
interface ClusterInfoProps {
124202
cluster?: TClusterInfo;
125203
versionsValues?: VersionValue[];
204+
groupsStats?: ClusterGroupsStats;
126205
loading?: boolean;
127206
error?: IResponseError;
128207
additionalClusterProps?: AdditionalClusterProps;
@@ -131,6 +210,7 @@ interface ClusterInfoProps {
131210
export const ClusterInfo = ({
132211
cluster = {},
133212
versionsValues = [],
213+
groupsStats = {},
134214
loading,
135215
error,
136216
additionalClusterProps = {},
@@ -151,7 +231,7 @@ export const ClusterInfo = ({
151231

152232
const {info = [], links = []} = additionalClusterProps;
153233

154-
const clusterInfo = getInfo(cluster, versionsValues, info, [
234+
const clusterInfo = getInfo(cluster, versionsValues, groupsStats, info, [
155235
{title: DEVELOPER_UI_TITLE, url: internalLink},
156236
...links,
157237
]);

src/containers/Cluster/ClusterInfoSkeleton/ClusterInfoSkeleton.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ interface ClusterInfoSkeletonProps {
1818
rows?: number;
1919
}
2020

21-
export const ClusterInfoSkeleton = ({rows = 7, className}: ClusterInfoSkeletonProps) => (
21+
export const ClusterInfoSkeleton = ({rows = 8, className}: ClusterInfoSkeletonProps) => (
2222
<div className={b(null, className)}>
2323
{[...new Array(rows)].map((_, index) => (
2424
<div className={b('row')} key={`skeleton-row-${index}`}>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"disk-type": "Disk Type",
3+
"erasure": "Erasure",
4+
"allocated": "Allocated",
5+
"available": "Available",
6+
"usage": "Usage",
7+
"dc": "DC",
8+
"tablets": "Tablets",
9+
"databases": "Databases",
10+
"nodes": "Nodes",
11+
"load": "Load",
12+
"storage-size": "Storage size",
13+
"storage-groups": "Storage groups, {{diskType}}",
14+
"links": "Links",
15+
"versions": "Versions"
16+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import {i18n, Lang} from '../../../utils/i18n';
2+
3+
import en from './en.json';
4+
import ru from './ru.json';
5+
6+
const COMPONENT = 'ydb-cluster';
7+
8+
i18n.registerKeyset(Lang.En, COMPONENT, en);
9+
i18n.registerKeyset(Lang.Ru, COMPONENT, ru);
10+
11+
export default i18n.keyset(COMPONENT);
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"disk-type": "Тип диска",
3+
"erasure": "Режим",
4+
"allocated": "Использовано",
5+
"available": "Доступно",
6+
"usage": "Потребление",
7+
"dc": "ДЦ",
8+
"tablets": "Таблетки",
9+
"databases": "Базы данных",
10+
"nodes": "Узлы",
11+
"load": "Нагрузка",
12+
"storage-size": "Размер хранилища",
13+
"storage-groups": "Группы хранения, {{diskType}}",
14+
"links": "Ссылки",
15+
"versions": "Версии"
16+
}

0 commit comments

Comments
 (0)