ydb-embedded-ui 3.2.2 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -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/Consumers/Consumers.tsx +2 -1
- 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/Tenants/Tenants.js +2 -1
- 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 -213
- 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
|
}}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {useCallback, useEffect, useState} from 'react';
|
|
2
2
|
import {useDispatch} from 'react-redux';
|
|
3
3
|
import block from 'bem-cn-lite';
|
|
4
|
+
import { escapeRegExp } from 'lodash/fp';
|
|
4
5
|
|
|
5
6
|
import DataTable, {Column} from '@yandex-cloud/react-data-table';
|
|
6
7
|
|
|
@@ -73,7 +74,7 @@ export const Consumers = ({path, type}: ConsumersProps) => {
|
|
|
73
74
|
const filterConsumersByName = (search: string) => {
|
|
74
75
|
const filteredConsumers = search
|
|
75
76
|
? consumers.filter((consumer) => {
|
|
76
|
-
const re = new RegExp(search, 'i');
|
|
77
|
+
const re = new RegExp(escapeRegExp(search), 'i');
|
|
77
78
|
return re.test(consumer.name);
|
|
78
79
|
})
|
|
79
80
|
: consumers;
|