ydb-embedded-ui 3.2.3 → 3.3.0
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +20 -0
- package/dist/components/InfoViewer/InfoViewer.scss +10 -0
- package/dist/components/InfoViewer/InfoViewer.tsx +12 -2
- package/dist/components/InfoViewer/formatters/cdcStream.ts +10 -0
- package/dist/components/InfoViewer/formatters/index.ts +3 -0
- package/dist/components/InfoViewer/formatters/pqGroup.ts +51 -0
- package/dist/components/InfoViewer/formatters/schema.ts +1 -29
- package/dist/components/InfoViewer/formatters/topicStats.tsx +50 -0
- package/dist/components/InfoViewer/schemaInfo/index.ts +0 -2
- package/dist/components/InfoViewer/utils.ts +15 -0
- package/dist/components/InternalLink/InternalLink.tsx +17 -0
- package/dist/components/InternalLink/index.ts +1 -0
- package/dist/components/Tablet/Tablet.js +1 -1
- package/dist/components/TabletsStatistic/TabletsStatistic.tsx +1 -1
- package/dist/components/VerticalBars/VerticalBars.scss +15 -0
- package/dist/components/VerticalBars/VerticalBars.tsx +38 -0
- package/dist/components/VerticalBars/index.ts +1 -0
- package/dist/containers/App/App.js +0 -11
- package/dist/containers/App/App.scss +0 -1
- package/dist/containers/Cluster/Cluster.tsx +2 -2
- package/dist/containers/Nodes/Nodes.scss +5 -1
- package/dist/containers/Nodes/Nodes.tsx +196 -0
- package/dist/containers/{App → Nodes}/NodesTable.scss +2 -2
- package/dist/{utils/getNodesColumns.js → containers/Nodes/getNodesColumns.tsx} +60 -35
- package/dist/containers/Nodes/i18n/en.json +3 -0
- package/dist/containers/Nodes/i18n/index.ts +11 -0
- package/dist/containers/Nodes/i18n/ru.json +3 -0
- package/dist/containers/Nodes/index.ts +1 -0
- package/dist/containers/Storage/DiskStateProgressBar/DiskStateProgressBar.scss +14 -20
- package/dist/containers/Storage/DiskStateProgressBar/DiskStateProgressBar.tsx +32 -16
- package/dist/containers/Storage/DiskStateProgressBar/index.ts +1 -0
- package/dist/containers/Storage/{Pdisk/Pdisk.scss → PDisk/PDisk.scss} +15 -4
- package/dist/containers/Storage/PDisk/PDisk.tsx +145 -0
- package/dist/containers/Storage/PDisk/__tests__/colors.tsx +37 -0
- package/dist/containers/Storage/PDisk/index.ts +1 -0
- package/dist/containers/Storage/PDiskPopup/PDiskPopup.scss +3 -0
- package/dist/containers/Storage/PDiskPopup/PDiskPopup.tsx +82 -0
- package/dist/containers/Storage/PDiskPopup/index.ts +1 -0
- package/dist/containers/Storage/Storage.js +1 -1
- package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +10 -10
- package/dist/containers/Storage/StorageNodes/StorageNodes.scss +1 -0
- package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +7 -4
- package/dist/containers/Storage/VDisk/VDisk.scss +7 -0
- package/dist/containers/Storage/VDisk/VDisk.tsx +148 -0
- package/dist/containers/Storage/VDisk/__tests__/colors.tsx +209 -0
- package/dist/containers/Storage/VDisk/index.ts +1 -0
- package/dist/containers/Storage/VDiskPopup/VDiskPopup.scss +14 -0
- package/dist/containers/Storage/VDiskPopup/VDiskPopup.tsx +134 -0
- package/dist/containers/Storage/VDiskPopup/index.ts +1 -0
- package/dist/containers/Storage/utils/constants.ts +2 -9
- package/dist/containers/TabletsFilters/TabletsFilters.js +10 -6
- package/dist/containers/Tenant/Diagnostics/Diagnostics.scss +2 -2
- package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +3 -4
- package/dist/containers/Tenant/Diagnostics/OverloadedShards/OverloadedShards.tsx +1 -1
- package/dist/containers/Tenant/Diagnostics/Overview/ChangefeedInfo/ChangefeedInfo.tsx +69 -0
- package/dist/containers/Tenant/Diagnostics/Overview/ChangefeedInfo/index.ts +1 -0
- package/dist/containers/Tenant/Diagnostics/Overview/Overview.tsx +18 -16
- package/dist/containers/Tenant/Diagnostics/Overview/TopicInfo/TopicInfo.tsx +37 -0
- package/dist/containers/Tenant/Diagnostics/Overview/TopicInfo/index.ts +1 -0
- package/dist/containers/Tenant/Diagnostics/Overview/TopicStats/TopicStats.scss +30 -0
- package/dist/containers/Tenant/Diagnostics/Overview/TopicStats/TopicStats.tsx +94 -0
- package/dist/containers/Tenant/Diagnostics/Overview/TopicStats/i18n/en.json +3 -0
- package/dist/containers/Tenant/Diagnostics/Overview/TopicStats/i18n/index.ts +11 -0
- package/dist/containers/Tenant/Diagnostics/Overview/TopicStats/i18n/ru.json +3 -0
- package/dist/containers/Tenant/Diagnostics/Overview/TopicStats/index.ts +1 -0
- package/dist/containers/Tenant/Diagnostics/Overview/utils/index.ts +1 -0
- package/dist/containers/Tenant/Diagnostics/Overview/utils/prepareTopicSchemaInfo.ts +42 -0
- package/dist/containers/Tenant/utils/schema.ts +19 -0
- package/dist/containers/UserSettings/UserSettings.tsx +18 -10
- package/dist/services/api.d.ts +8 -1
- package/dist/services/api.js +27 -8
- package/dist/store/reducers/index.ts +3 -1
- package/dist/store/reducers/nodes.ts +148 -14
- package/dist/store/reducers/{clusterNodes.js → nodesList.js} +0 -41
- package/dist/store/reducers/settings.js +10 -4
- package/dist/store/reducers/storage.js +24 -13
- package/dist/store/reducers/tenant.js +5 -4
- package/dist/store/reducers/tooltip.ts +1 -1
- package/dist/store/reducers/topic.ts +52 -0
- package/dist/styles/mixins.scss +19 -11
- package/dist/types/api/common.ts +5 -0
- package/dist/types/api/compute.ts +1 -1
- package/dist/types/api/consumer.ts +12 -10
- package/dist/types/api/nodes.ts +2 -0
- package/dist/types/api/pdisk.ts +1 -0
- package/dist/types/api/schema.ts +3 -3
- package/dist/types/api/topic.ts +5 -4
- package/dist/types/api/vdisk.ts +2 -1
- package/dist/types/store/nodes.ts +56 -6
- package/dist/types/store/tooltip.ts +1 -1
- package/dist/types/store/topic.ts +21 -0
- package/dist/utils/constants.ts +5 -1
- package/dist/utils/i18n/i18n.ts +10 -2
- package/dist/utils/index.js +1 -1
- package/dist/utils/timeParsers/__test__/formatDuration.test.ts +50 -0
- package/dist/utils/timeParsers/__test__/protobuf.test.ts +74 -0
- package/dist/utils/timeParsers/formatDuration.ts +46 -0
- package/dist/utils/timeParsers/i18n/en.json +7 -0
- package/dist/utils/timeParsers/i18n/index.ts +11 -0
- package/dist/utils/timeParsers/i18n/ru.json +7 -0
- package/dist/utils/timeParsers/index.ts +2 -0
- package/dist/utils/timeParsers/protobuf.ts +36 -0
- package/package.json +1 -1
- package/dist/components/InfoViewer/schemaInfo/CDCStreamInfo.tsx +0 -48
- package/dist/components/InfoViewer/schemaInfo/PersQueueGroupInfo.tsx +0 -30
- package/dist/components/InternalLink/InternalLink.js +0 -23
- package/dist/containers/Nodes/Nodes.js +0 -214
- package/dist/containers/NodesViewer/NodesViewer.js +0 -163
- package/dist/containers/NodesViewer/NodesViewer.scss +0 -66
- package/dist/containers/Storage/Pdisk/Pdisk.tsx +0 -153
- package/dist/containers/Storage/Pdisk/__tests__/colors.tsx +0 -41
- package/dist/containers/Storage/Vdisk/Vdisk.js +0 -275
- package/dist/containers/Storage/Vdisk/Vdisk.scss +0 -22
- package/dist/containers/Storage/Vdisk/__tests__/colors.tsx +0 -163
- package/dist/containers/Tenant/Diagnostics/Compute/Compute.js +0 -139
- package/dist/containers/Tenant/Diagnostics/Compute/Compute.scss +0 -14
@@ -19,7 +19,7 @@ import {stringifyVdiskId} from '../../../utils';
|
|
19
19
|
import {getUsage, isFullDonorData} from '../../../utils/storage';
|
20
20
|
|
21
21
|
import {EmptyFilter} from '../EmptyFilter/EmptyFilter';
|
22
|
-
import
|
22
|
+
import {VDisk} from '../VDisk';
|
23
23
|
import {getDegradedSeverity, getUsageSeverity} from '../utils';
|
24
24
|
|
25
25
|
import i18n from './i18n';
|
@@ -254,16 +254,16 @@ function StorageGroups({
|
|
254
254
|
|
255
255
|
return donors.length > 0 ? (
|
256
256
|
<Stack className={b('vdisks-item')} key={stringifyVdiskId(el.VDiskId)}>
|
257
|
-
<
|
258
|
-
{
|
259
|
-
|
257
|
+
<VDisk
|
258
|
+
data={el}
|
259
|
+
poolName={row[TableColumnsIds.PoolName]}
|
260
260
|
nodes={nodes}
|
261
261
|
/>
|
262
262
|
{donors.map((donor) => (
|
263
|
-
<
|
264
|
-
{
|
263
|
+
<VDisk
|
264
|
+
data={donor}
|
265
265
|
// donor and acceptor are always in the same group
|
266
|
-
|
266
|
+
poolName={row[TableColumnsIds.PoolName]}
|
267
267
|
nodes={nodes}
|
268
268
|
key={stringifyVdiskId(donor.VDiskId)}
|
269
269
|
/>
|
@@ -271,9 +271,9 @@ function StorageGroups({
|
|
271
271
|
</Stack>
|
272
272
|
) : (
|
273
273
|
<div className={b('vdisks-item')} key={stringifyVdiskId(el.VDiskId)}>
|
274
|
-
<
|
275
|
-
{
|
276
|
-
|
274
|
+
<VDisk
|
275
|
+
data={el}
|
276
|
+
poolName={row[TableColumnsIds.PoolName]}
|
277
277
|
nodes={nodes}
|
278
278
|
/>
|
279
279
|
</div>
|
@@ -8,7 +8,7 @@ import {VisibleEntities} from '../../../store/reducers/storage';
|
|
8
8
|
import {NodesUptimeFilterValues} from '../../../utils/nodes';
|
9
9
|
|
10
10
|
import {EmptyFilter} from '../EmptyFilter/EmptyFilter';
|
11
|
-
import
|
11
|
+
import {PDisk} from '../PDisk';
|
12
12
|
|
13
13
|
import i18n from './i18n';
|
14
14
|
import './StorageNodes.scss';
|
@@ -132,8 +132,8 @@ function StorageNodes({
|
|
132
132
|
render: ({value, row}) => (
|
133
133
|
<div className={b('pdisks-wrapper')}>
|
134
134
|
{_.map(value as any, (el) => (
|
135
|
-
<div className={b('pdisks-item')}>
|
136
|
-
<
|
135
|
+
<div className={b('pdisks-item')} key={el.PDiskId}>
|
136
|
+
<PDisk data={el} nodeId={row.NodeId} />
|
137
137
|
</div>
|
138
138
|
))}
|
139
139
|
</div>
|
@@ -183,7 +183,10 @@ function StorageNodes({
|
|
183
183
|
theme="yandex-cloud"
|
184
184
|
data={data}
|
185
185
|
columns={columns}
|
186
|
-
settings={
|
186
|
+
settings={{
|
187
|
+
...tableSettings,
|
188
|
+
dynamicRenderType: 'variable',
|
189
|
+
}}
|
187
190
|
initialSortOrder={setSortOrder(visibleEntities)}
|
188
191
|
emptyDataMessage={i18n('empty.default')}
|
189
192
|
/>
|
@@ -0,0 +1,148 @@
|
|
1
|
+
import React, {useEffect, useState, useRef, useMemo} from 'react';
|
2
|
+
import cn from 'bem-cn-lite';
|
3
|
+
|
4
|
+
import {InternalLink} from '../../../components/InternalLink';
|
5
|
+
|
6
|
+
import routes, {createHref} from '../../../routes';
|
7
|
+
import {EFlag} from '../../../types/api/enums';
|
8
|
+
import {EVDiskState, TVDiskStateInfo} from '../../../types/api/vdisk';
|
9
|
+
import {stringifyVdiskId} from '../../../utils';
|
10
|
+
|
11
|
+
import {STRUCTURE} from '../../Node/NodePages';
|
12
|
+
|
13
|
+
import {DiskStateProgressBar, EDiskStateSeverity} from '../DiskStateProgressBar';
|
14
|
+
import type {NodesHosts} from '../PDiskPopup';
|
15
|
+
import {VDiskPopup} from '../VDiskPopup';
|
16
|
+
|
17
|
+
import {NOT_AVAILABLE_SEVERITY} from '../utils';
|
18
|
+
|
19
|
+
import './VDisk.scss';
|
20
|
+
|
21
|
+
const b = cn('vdisk-storage');
|
22
|
+
|
23
|
+
const stateSeverity: Record<EVDiskState, EDiskStateSeverity> = {
|
24
|
+
Initial: EDiskStateSeverity.Yellow,
|
25
|
+
LocalRecoveryError: EDiskStateSeverity.Red,
|
26
|
+
SyncGuidRecoveryError: EDiskStateSeverity.Red,
|
27
|
+
SyncGuidRecovery: EDiskStateSeverity.Yellow,
|
28
|
+
PDiskError: EDiskStateSeverity.Red,
|
29
|
+
OK: EDiskStateSeverity.Green,
|
30
|
+
};
|
31
|
+
|
32
|
+
const getStateSeverity = (vDiskState?: EVDiskState) => {
|
33
|
+
if (!vDiskState) {
|
34
|
+
return NOT_AVAILABLE_SEVERITY;
|
35
|
+
}
|
36
|
+
|
37
|
+
return stateSeverity[vDiskState] ?? NOT_AVAILABLE_SEVERITY;
|
38
|
+
};
|
39
|
+
|
40
|
+
const getColorSeverity = (color?: EFlag) => {
|
41
|
+
if (!color) {
|
42
|
+
return EDiskStateSeverity.Grey;
|
43
|
+
}
|
44
|
+
|
45
|
+
return EDiskStateSeverity[color] ?? EDiskStateSeverity.Grey;
|
46
|
+
};
|
47
|
+
|
48
|
+
interface VDiskProps {
|
49
|
+
data?: TVDiskStateInfo;
|
50
|
+
poolName?: string;
|
51
|
+
nodes?: NodesHosts;
|
52
|
+
compact?: boolean;
|
53
|
+
}
|
54
|
+
|
55
|
+
export const VDisk = ({data = {}, poolName, nodes, compact}: VDiskProps) => {
|
56
|
+
const [severity, setSeverity] = useState(getStateSeverity(data.VDiskState));
|
57
|
+
const [isPopupVisible, setIsPopupVisible] = useState(false);
|
58
|
+
|
59
|
+
const anchor = useRef(null);
|
60
|
+
|
61
|
+
// determine disk status severity
|
62
|
+
useEffect(() => {
|
63
|
+
const {DiskSpace, VDiskState, FrontQueues, Replicated, DonorMode} = data;
|
64
|
+
|
65
|
+
// if the disk is not available, this determines its status severity regardless of other features
|
66
|
+
if (!VDiskState) {
|
67
|
+
setSeverity(NOT_AVAILABLE_SEVERITY);
|
68
|
+
return;
|
69
|
+
}
|
70
|
+
|
71
|
+
const DiskSpaceSeverity = getColorSeverity(DiskSpace);
|
72
|
+
const VDiskSpaceSeverity = getStateSeverity(VDiskState);
|
73
|
+
const FrontQueuesSeverity = Math.min(
|
74
|
+
EDiskStateSeverity.Orange,
|
75
|
+
getColorSeverity(FrontQueues),
|
76
|
+
);
|
77
|
+
|
78
|
+
let newSeverity = Math.max(DiskSpaceSeverity, VDiskSpaceSeverity, FrontQueuesSeverity);
|
79
|
+
|
80
|
+
// donors are always in the not replicated state since they are leftovers
|
81
|
+
// painting them blue is useless
|
82
|
+
if (!Replicated && !DonorMode && newSeverity === EDiskStateSeverity.Green) {
|
83
|
+
newSeverity = EDiskStateSeverity.Blue;
|
84
|
+
}
|
85
|
+
|
86
|
+
setSeverity(newSeverity);
|
87
|
+
}, [data]);
|
88
|
+
|
89
|
+
const showPopup = () => {
|
90
|
+
setIsPopupVisible(true);
|
91
|
+
};
|
92
|
+
|
93
|
+
const hidePopup = () => {
|
94
|
+
setIsPopupVisible(false);
|
95
|
+
};
|
96
|
+
|
97
|
+
const vdiskAllocatedPercent = useMemo(() => {
|
98
|
+
const {AvailableSize, AllocatedSize, PDisk} = data;
|
99
|
+
const available = AvailableSize ? AvailableSize : PDisk?.AvailableSize;
|
100
|
+
|
101
|
+
if (!available) {
|
102
|
+
return undefined;
|
103
|
+
}
|
104
|
+
|
105
|
+
return isNaN(Number(AllocatedSize))
|
106
|
+
? undefined
|
107
|
+
: (Number(AllocatedSize) * 100) / (Number(available) + Number(AllocatedSize));
|
108
|
+
}, [data]);
|
109
|
+
|
110
|
+
return (
|
111
|
+
<React.Fragment>
|
112
|
+
<VDiskPopup
|
113
|
+
data={data}
|
114
|
+
poolName={poolName}
|
115
|
+
nodes={nodes}
|
116
|
+
anchorRef={anchor}
|
117
|
+
open={isPopupVisible}
|
118
|
+
/>
|
119
|
+
<div className={b()} ref={anchor} onMouseEnter={showPopup} onMouseLeave={hidePopup}>
|
120
|
+
{data.NodeId ? (
|
121
|
+
<InternalLink
|
122
|
+
to={createHref(
|
123
|
+
routes.node,
|
124
|
+
{id: data.NodeId, activeTab: STRUCTURE},
|
125
|
+
{
|
126
|
+
pdiskId: data.PDisk?.PDiskId,
|
127
|
+
vdiskId: stringifyVdiskId(data.VDiskId),
|
128
|
+
},
|
129
|
+
)}
|
130
|
+
className={b('content')}
|
131
|
+
>
|
132
|
+
<DiskStateProgressBar
|
133
|
+
diskAllocatedPercent={vdiskAllocatedPercent}
|
134
|
+
severity={severity}
|
135
|
+
compact={compact}
|
136
|
+
/>
|
137
|
+
</InternalLink>
|
138
|
+
) : (
|
139
|
+
<DiskStateProgressBar
|
140
|
+
diskAllocatedPercent={vdiskAllocatedPercent}
|
141
|
+
severity={severity}
|
142
|
+
compact={compact}
|
143
|
+
/>
|
144
|
+
)}
|
145
|
+
</div>
|
146
|
+
</React.Fragment>
|
147
|
+
);
|
148
|
+
};
|
@@ -0,0 +1,209 @@
|
|
1
|
+
import {renderWithStore} from '../../../../utils/tests/providers';
|
2
|
+
|
3
|
+
import {EVDiskState} from '../../../../types/api/vdisk';
|
4
|
+
import {EFlag} from '../../../../types/api/enums';
|
5
|
+
|
6
|
+
import {VDisk} from '../VDisk';
|
7
|
+
|
8
|
+
describe('VDisk state', () => {
|
9
|
+
it('Should determine severity based on the highest value among VDiskState, DiskSpace and FrontQueues', () => {
|
10
|
+
const {getAllByRole} = renderWithStore(
|
11
|
+
<>
|
12
|
+
<VDisk
|
13
|
+
data={{
|
14
|
+
VDiskId: {Domain: 1},
|
15
|
+
VDiskState: EVDiskState.OK, // severity 1, green
|
16
|
+
DiskSpace: EFlag.Yellow, // severity 3, yellow
|
17
|
+
FrontQueues: EFlag.Green, // severity 1, green
|
18
|
+
}}
|
19
|
+
/>
|
20
|
+
<VDisk
|
21
|
+
data={{
|
22
|
+
VDiskId: {Domain: 2},
|
23
|
+
VDiskState: EVDiskState.PDiskError, // severity 5, red
|
24
|
+
DiskSpace: EFlag.Yellow, // severity 3, yellow
|
25
|
+
FrontQueues: EFlag.Green, // severity 1, green
|
26
|
+
}}
|
27
|
+
/>
|
28
|
+
<VDisk
|
29
|
+
data={{
|
30
|
+
VDiskId: {Domain: 3},
|
31
|
+
VDiskState: EVDiskState.OK, // severity 1, green
|
32
|
+
DiskSpace: EFlag.Yellow, // severity 3, yellow
|
33
|
+
FrontQueues: EFlag.Orange, // severity 4, orange
|
34
|
+
}}
|
35
|
+
/>
|
36
|
+
</>,
|
37
|
+
);
|
38
|
+
|
39
|
+
const [disk1, disk2, disk3] = getAllByRole('meter');
|
40
|
+
|
41
|
+
expect(disk1.className).toMatch(/_yellow\b/i);
|
42
|
+
expect(disk2.className).toMatch(/_red\b/i);
|
43
|
+
expect(disk3.className).toMatch(/_orange\b/i);
|
44
|
+
});
|
45
|
+
|
46
|
+
it('Should not pick the highest severity based on FrontQueues value', () => {
|
47
|
+
const {getAllByRole} = renderWithStore(
|
48
|
+
<>
|
49
|
+
<VDisk
|
50
|
+
data={{
|
51
|
+
VDiskId: {Domain: 1},
|
52
|
+
VDiskState: EVDiskState.OK, // severity 1, green
|
53
|
+
DiskSpace: EFlag.Green, // severity 1, green
|
54
|
+
FrontQueues: EFlag.Red, // severity 5, red
|
55
|
+
}}
|
56
|
+
/>
|
57
|
+
<VDisk
|
58
|
+
data={{
|
59
|
+
VDiskId: {Domain: 2},
|
60
|
+
VDiskState: EVDiskState.OK, // severity 1, green
|
61
|
+
DiskSpace: EFlag.Red, // severity 5, red
|
62
|
+
FrontQueues: EFlag.Red, // severity 5, red
|
63
|
+
}}
|
64
|
+
/>
|
65
|
+
</>,
|
66
|
+
);
|
67
|
+
|
68
|
+
const [disk1, disk2] = getAllByRole('meter');
|
69
|
+
|
70
|
+
expect(disk1.className).not.toMatch(/_red\b/i);
|
71
|
+
expect(disk2.className).toMatch(/_red\b/i);
|
72
|
+
});
|
73
|
+
|
74
|
+
// prettier-ignore
|
75
|
+
it('Should display as unavailable when no VDiskState is provided', () => {
|
76
|
+
const {getAllByRole} = renderWithStore(
|
77
|
+
<>
|
78
|
+
<VDisk data={{VDiskId: {Domain: 1}}} />
|
79
|
+
<VDisk data={{VDiskId: {Domain: 2}, VDiskState: EVDiskState.OK}} />
|
80
|
+
<VDisk data={{VDiskId: {Domain: 3}, DiskSpace: EFlag.Green}} />
|
81
|
+
<VDisk data={{VDiskId: {Domain: 4}, FrontQueues: EFlag.Green}} />
|
82
|
+
<VDisk data={{VDiskId: {Domain: 5}, VDiskState: EVDiskState.OK, DiskSpace: EFlag.Green}} />
|
83
|
+
<VDisk data={{VDiskId: {Domain: 6}, VDiskState: EVDiskState.OK, FrontQueues: EFlag.Green}} />
|
84
|
+
<VDisk data={{VDiskId: {Domain: 7}, DiskSpace: EFlag.Green, FrontQueues: EFlag.Green}} />
|
85
|
+
<VDisk data={{VDiskId: {Domain: 8}, VDiskState: EVDiskState.OK, DiskSpace: EFlag.Green, FrontQueues: EFlag.Green}} />
|
86
|
+
</>
|
87
|
+
);
|
88
|
+
|
89
|
+
const [disk1, disk2, disk3, disk4, disk5, disk6, disk7, disk8] =
|
90
|
+
getAllByRole('meter');
|
91
|
+
|
92
|
+
// unavailable disks display with the grey color
|
93
|
+
expect(disk1.className).toMatch(/_grey\b/i);
|
94
|
+
expect(disk2.className).not.toMatch(/_grey\b/i);
|
95
|
+
expect(disk3.className).toMatch(/_grey\b/i);
|
96
|
+
expect(disk4.className).toMatch(/_grey\b/i);
|
97
|
+
expect(disk5.className).not.toMatch(/_grey\b/i);
|
98
|
+
expect(disk6.className).not.toMatch(/_grey\b/i);
|
99
|
+
expect(disk7.className).toMatch(/_grey\b/i);
|
100
|
+
expect(disk8.className).not.toMatch(/_grey\b/i);
|
101
|
+
});
|
102
|
+
|
103
|
+
it('Should display as unavailable when no VDiskState is provided even if DiskSpace or FrontQueues flags are not green', () => {
|
104
|
+
const {getByRole} = renderWithStore(
|
105
|
+
<VDisk
|
106
|
+
data={{
|
107
|
+
VDiskId: {Domain: 1},
|
108
|
+
DiskSpace: EFlag.Red,
|
109
|
+
FrontQueues: EFlag.Yellow,
|
110
|
+
}}
|
111
|
+
/>,
|
112
|
+
);
|
113
|
+
|
114
|
+
const disk = getByRole('meter');
|
115
|
+
|
116
|
+
// unavailable disks display with the grey color
|
117
|
+
expect(disk.className).toMatch(/_grey\b/i);
|
118
|
+
});
|
119
|
+
|
120
|
+
it('Should display replicating VDisks in OK state with a distinct color', () => {
|
121
|
+
const {getAllByRole} = renderWithStore(
|
122
|
+
<>
|
123
|
+
<VDisk
|
124
|
+
data={{
|
125
|
+
VDiskId: {Domain: 1},
|
126
|
+
VDiskState: EVDiskState.OK, // severity 1, green
|
127
|
+
Replicated: false,
|
128
|
+
}}
|
129
|
+
/>
|
130
|
+
<VDisk
|
131
|
+
data={{
|
132
|
+
VDiskId: {Domain: 2},
|
133
|
+
VDiskState: EVDiskState.OK, // severity 1, green
|
134
|
+
Replicated: true,
|
135
|
+
}}
|
136
|
+
/>
|
137
|
+
</>,
|
138
|
+
);
|
139
|
+
|
140
|
+
const [disk1, disk2] = getAllByRole('meter');
|
141
|
+
|
142
|
+
expect(disk1.className).toMatch(/_blue\b/i);
|
143
|
+
expect(disk2.className).not.toMatch(/_blue\b/i);
|
144
|
+
});
|
145
|
+
|
146
|
+
it('Should display replicating VDisks in a not-OK state with a regular color', () => {
|
147
|
+
const {getAllByRole} = renderWithStore(
|
148
|
+
<>
|
149
|
+
<VDisk
|
150
|
+
data={{
|
151
|
+
VDiskId: {Domain: 1},
|
152
|
+
VDiskState: EVDiskState.Initial, // severity 3, yellow
|
153
|
+
Replicated: false,
|
154
|
+
}}
|
155
|
+
/>
|
156
|
+
<VDisk
|
157
|
+
data={{
|
158
|
+
VDiskId: {Domain: 2},
|
159
|
+
VDiskState: EVDiskState.PDiskError, // severity 5, red
|
160
|
+
Replicated: false,
|
161
|
+
}}
|
162
|
+
/>
|
163
|
+
</>,
|
164
|
+
);
|
165
|
+
|
166
|
+
const [disk1, disk2] = getAllByRole('meter');
|
167
|
+
|
168
|
+
expect(disk1.className).toMatch(/_yellow\b/i);
|
169
|
+
expect(disk2.className).toMatch(/_red\b/i);
|
170
|
+
});
|
171
|
+
|
172
|
+
it('Should always display donor VDisks with a regular color', () => {
|
173
|
+
const {getAllByRole} = renderWithStore(
|
174
|
+
<>
|
175
|
+
<VDisk
|
176
|
+
data={{
|
177
|
+
VDiskId: {Domain: 1},
|
178
|
+
VDiskState: EVDiskState.OK, // severity 1, green
|
179
|
+
Replicated: false, // donors are always in the not replicated state since they are leftovers
|
180
|
+
DonorMode: true,
|
181
|
+
}}
|
182
|
+
/>
|
183
|
+
<VDisk
|
184
|
+
data={{
|
185
|
+
VDiskId: {Domain: 2},
|
186
|
+
VDiskState: EVDiskState.Initial, // severity 3, yellow
|
187
|
+
Replicated: false,
|
188
|
+
DonorMode: true,
|
189
|
+
}}
|
190
|
+
/>
|
191
|
+
<VDisk
|
192
|
+
data={{
|
193
|
+
VDiskId: {Domain: 3},
|
194
|
+
VDiskState: EVDiskState.PDiskError, // severity 5, red
|
195
|
+
Replicated: false,
|
196
|
+
DonorMode: true,
|
197
|
+
}}
|
198
|
+
/>
|
199
|
+
</>,
|
200
|
+
);
|
201
|
+
|
202
|
+
const [disk1, disk2, disk3] = getAllByRole('meter');
|
203
|
+
|
204
|
+
expect(disk1.className).not.toMatch(/_blue\b/i);
|
205
|
+
expect(disk1.className).toMatch(/_green\b/i);
|
206
|
+
expect(disk2.className).toMatch(/_yellow\b/i);
|
207
|
+
expect(disk3.className).toMatch(/_red\b/i);
|
208
|
+
});
|
209
|
+
});
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './VDisk';
|
@@ -0,0 +1,134 @@
|
|
1
|
+
import {useMemo} from 'react';
|
2
|
+
import cn from 'bem-cn-lite';
|
3
|
+
|
4
|
+
import {Label, Popup, PopupProps} from '@gravity-ui/uikit';
|
5
|
+
|
6
|
+
import {InfoViewer, InfoViewerItem} from '../../../components/InfoViewer';
|
7
|
+
|
8
|
+
import {EFlag} from '../../../types/api/enums';
|
9
|
+
import {TVDiskStateInfo} from '../../../types/api/vdisk';
|
10
|
+
import {stringifyVdiskId} from '../../../utils';
|
11
|
+
import {bytesToGB, bytesToSpeed} from '../../../utils/utils';
|
12
|
+
|
13
|
+
import {NodesHosts, preparePDiskData} from '../PDiskPopup';
|
14
|
+
|
15
|
+
import './VDiskPopup.scss';
|
16
|
+
|
17
|
+
const b = cn('vdisk-storage-popup');
|
18
|
+
|
19
|
+
const prepareVDiskData = (data: TVDiskStateInfo, poolName?: string) => {
|
20
|
+
const {
|
21
|
+
VDiskId,
|
22
|
+
VDiskState,
|
23
|
+
SatisfactionRank,
|
24
|
+
DiskSpace,
|
25
|
+
FrontQueues,
|
26
|
+
Replicated,
|
27
|
+
UnsyncedVDisks,
|
28
|
+
AllocatedSize,
|
29
|
+
ReadThroughput,
|
30
|
+
WriteThroughput,
|
31
|
+
} = data;
|
32
|
+
|
33
|
+
const vdiskData: InfoViewerItem[] = [
|
34
|
+
{label: 'VDisk', value: stringifyVdiskId(VDiskId)},
|
35
|
+
{label: 'State', value: VDiskState ?? 'not available'},
|
36
|
+
];
|
37
|
+
|
38
|
+
if (poolName) {
|
39
|
+
vdiskData.push({label: 'StoragePool', value: poolName});
|
40
|
+
}
|
41
|
+
|
42
|
+
if (SatisfactionRank && SatisfactionRank.FreshRank?.Flag !== EFlag.Green) {
|
43
|
+
vdiskData.push({
|
44
|
+
label: 'Fresh',
|
45
|
+
value: SatisfactionRank.FreshRank?.Flag,
|
46
|
+
});
|
47
|
+
}
|
48
|
+
|
49
|
+
if (SatisfactionRank && SatisfactionRank.LevelRank?.Flag !== EFlag.Green) {
|
50
|
+
vdiskData.push({
|
51
|
+
label: 'Level',
|
52
|
+
value: SatisfactionRank.LevelRank?.Flag,
|
53
|
+
});
|
54
|
+
}
|
55
|
+
|
56
|
+
if (SatisfactionRank && SatisfactionRank.FreshRank?.RankPercent) {
|
57
|
+
vdiskData.push({
|
58
|
+
label: 'Fresh',
|
59
|
+
value: SatisfactionRank.FreshRank.RankPercent,
|
60
|
+
});
|
61
|
+
}
|
62
|
+
|
63
|
+
if (SatisfactionRank && SatisfactionRank.LevelRank?.RankPercent) {
|
64
|
+
vdiskData.push({
|
65
|
+
label: 'Level',
|
66
|
+
value: SatisfactionRank.LevelRank.RankPercent,
|
67
|
+
});
|
68
|
+
}
|
69
|
+
|
70
|
+
if (DiskSpace && DiskSpace !== EFlag.Green) {
|
71
|
+
vdiskData.push({label: 'Space', value: DiskSpace});
|
72
|
+
}
|
73
|
+
|
74
|
+
if (FrontQueues && FrontQueues !== EFlag.Green) {
|
75
|
+
vdiskData.push({label: 'FrontQueues', value: FrontQueues});
|
76
|
+
}
|
77
|
+
|
78
|
+
if (!Replicated) {
|
79
|
+
vdiskData.push({label: 'Replicated', value: 'NO'});
|
80
|
+
}
|
81
|
+
|
82
|
+
if (UnsyncedVDisks) {
|
83
|
+
vdiskData.push({label: 'UnsyncVDisks', value: UnsyncedVDisks});
|
84
|
+
}
|
85
|
+
|
86
|
+
if (Number(AllocatedSize)) {
|
87
|
+
vdiskData.push({
|
88
|
+
label: 'Allocated',
|
89
|
+
value: bytesToGB(AllocatedSize),
|
90
|
+
});
|
91
|
+
}
|
92
|
+
|
93
|
+
if (Number(ReadThroughput)) {
|
94
|
+
vdiskData.push({label: 'Read', value: bytesToSpeed(ReadThroughput)});
|
95
|
+
}
|
96
|
+
|
97
|
+
if (Number(WriteThroughput)) {
|
98
|
+
vdiskData.push({
|
99
|
+
label: 'Write',
|
100
|
+
value: bytesToSpeed(WriteThroughput),
|
101
|
+
});
|
102
|
+
}
|
103
|
+
|
104
|
+
return vdiskData;
|
105
|
+
};
|
106
|
+
|
107
|
+
interface VDiskPopupProps extends PopupProps {
|
108
|
+
data: TVDiskStateInfo;
|
109
|
+
poolName?: string;
|
110
|
+
nodes?: NodesHosts;
|
111
|
+
}
|
112
|
+
|
113
|
+
export const VDiskPopup = ({data, poolName, nodes, ...props}: VDiskPopupProps) => {
|
114
|
+
const vdiskInfo = useMemo(() => prepareVDiskData(data, poolName), [data, poolName]);
|
115
|
+
const pdiskInfo = useMemo(
|
116
|
+
() => data.PDisk && preparePDiskData(data.PDisk, nodes),
|
117
|
+
[data.PDisk, nodes],
|
118
|
+
);
|
119
|
+
|
120
|
+
return (
|
121
|
+
<Popup
|
122
|
+
className={b()}
|
123
|
+
placement={['top', 'bottom']}
|
124
|
+
// bigger offset for easier switching to neighbour nodes
|
125
|
+
// matches the default offset for popup with arrow out of a sense of beauty
|
126
|
+
offset={[0, 12]}
|
127
|
+
{...props}
|
128
|
+
>
|
129
|
+
{data.DonorMode && <Label className={b('donor-label')}>Donor</Label>}
|
130
|
+
<InfoViewer title="VDisk" info={vdiskInfo} size="s" />
|
131
|
+
{pdiskInfo && <InfoViewer title="PDisk" info={pdiskInfo} size="s" />}
|
132
|
+
</Popup>
|
133
|
+
);
|
134
|
+
};
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './VDiskPopup';
|
@@ -1,10 +1,3 @@
|
|
1
|
-
|
2
|
-
Grey: 0,
|
3
|
-
Green: 1,
|
4
|
-
Blue: 2,
|
5
|
-
Yellow: 3,
|
6
|
-
Orange: 4,
|
7
|
-
Red: 5,
|
8
|
-
};
|
1
|
+
import {EDiskStateSeverity} from '../DiskStateProgressBar';
|
9
2
|
|
10
|
-
export const NOT_AVAILABLE_SEVERITY =
|
3
|
+
export const NOT_AVAILABLE_SEVERITY = EDiskStateSeverity.Grey;
|
@@ -173,11 +173,13 @@ class TabletsFilters extends React.Component {
|
|
173
173
|
|
174
174
|
return (
|
175
175
|
<div className={b()}>
|
176
|
-
{
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
176
|
+
{tenantPath ? (
|
177
|
+
<div className={b('tenant')}>
|
178
|
+
<>
|
179
|
+
<span className={b('label')}>Database: </span> {tenantPath}
|
180
|
+
</>
|
181
|
+
</div>
|
182
|
+
) : null}
|
181
183
|
<MemoizedFilters
|
182
184
|
nodesForSelect={nodesForSelect}
|
183
185
|
nodeFilter={nodeFilter}
|
@@ -253,7 +255,9 @@ const Filters = ({
|
|
253
255
|
return (
|
254
256
|
<div className={b('node')}>
|
255
257
|
<div>{option.content}</div>
|
256
|
-
<div className={b('node-meta')} title={option.meta}>
|
258
|
+
<div className={b('node-meta')} title={option.meta}>
|
259
|
+
{option.meta}
|
260
|
+
</div>
|
257
261
|
</div>
|
258
262
|
);
|
259
263
|
}}
|