ydb-embedded-ui 1.11.0 → 1.12.1
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 +24 -0
- package/dist/containers/Storage/StorageGroups/StorageGroups.scss +6 -0
- package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +67 -49
- package/dist/containers/Storage/utils/index.ts +50 -0
- package/dist/services/api.d.ts +9 -0
- package/dist/types/api/schema.ts +6 -14
- package/dist/types/api/storage.ts +64 -21
- package/dist/types/store/storage.ts +11 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
@@ -1,5 +1,29 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [1.12.1](https://github.com/ydb-platform/ydb-embedded-ui/compare/v1.12.0...v1.12.1) (2022-08-26)
|
4
|
+
|
5
|
+
|
6
|
+
### Bug Fixes
|
7
|
+
|
8
|
+
* **Storage:** properly display usage for 0 storage ([aee67f9](https://github.com/ydb-platform/ydb-embedded-ui/commit/aee67f9314341c995e2c9468f5eedc48fa0a3d35))
|
9
|
+
|
10
|
+
## [1.12.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v1.11.1...v1.12.0) (2022-08-26)
|
11
|
+
|
12
|
+
|
13
|
+
### Features
|
14
|
+
|
15
|
+
* **Storage:** show usage column ([73aed5f](https://github.com/ydb-platform/ydb-embedded-ui/commit/73aed5f9ed60b6d2bd77fd315ae514ee7443c489))
|
16
|
+
* **Storage:** vividly show degraded disks count ([7315a9c](https://github.com/ydb-platform/ydb-embedded-ui/commit/7315a9cfd98002a7fab85d721712aa82c6dbb552))
|
17
|
+
|
18
|
+
## [1.11.1](https://github.com/ydb-platform/ydb-embedded-ui/compare/v1.11.0...v1.11.1) (2022-08-26)
|
19
|
+
|
20
|
+
|
21
|
+
### Bug Fixes
|
22
|
+
|
23
|
+
* number type instead of string for uint32 ([e60799e](https://github.com/ydb-platform/ydb-embedded-ui/commit/e60799edec4ef831e8c0d51f4384cde83520541d))
|
24
|
+
* **Storage:** expect arbitrary donors data ([09f8e08](https://github.com/ydb-platform/ydb-embedded-ui/commit/09f8e085c94faacd9da502643355e932346502ac))
|
25
|
+
* vdisk data contains pdisk data, not id ([bd1ea7f](https://github.com/ydb-platform/ydb-embedded-ui/commit/bd1ea7f59e0461256bb12f146b50470d21ac1ace))
|
26
|
+
|
3
27
|
## [1.11.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v1.10.3...v1.11.0) (2022-08-23)
|
4
28
|
|
5
29
|
|
@@ -1,9 +1,8 @@
|
|
1
1
|
import _ from 'lodash';
|
2
2
|
import cn from 'bem-cn-lite';
|
3
3
|
import DataTable, {Column, Settings, SortOrder} from '@yandex-cloud/react-data-table';
|
4
|
-
import {Popover, PopoverBehavior} from '@yandex-cloud/uikit';
|
4
|
+
import {Label, Popover, PopoverBehavior} from '@yandex-cloud/uikit';
|
5
5
|
|
6
|
-
import Vdisk from '../Vdisk/Vdisk';
|
7
6
|
import {Stack} from '../../../components/Stack/Stack';
|
8
7
|
//@ts-ignore
|
9
8
|
import EntityStatus from '../../../components/EntityStatus/EntityStatus';
|
@@ -16,6 +15,9 @@ import {bytesToGB, bytesToSpeed} from '../../../utils/utils';
|
|
16
15
|
//@ts-ignore
|
17
16
|
import {stringifyVdiskId} from '../../../utils';
|
18
17
|
|
18
|
+
import Vdisk from '../Vdisk/Vdisk';
|
19
|
+
import {isFullDonorData, getDegradedSeverity, getUsageSeverity, getUsage} from '../utils';
|
20
|
+
|
19
21
|
import './StorageGroups.scss';
|
20
22
|
|
21
23
|
enum TableColumnsIds {
|
@@ -47,11 +49,11 @@ const tableColumnsNames: Record<TableColumnsIdsValues, string> = {
|
|
47
49
|
Used: 'Used',
|
48
50
|
Limit: 'Limit',
|
49
51
|
UsedSpaceFlag: 'Space',
|
50
|
-
UsedPercents: '
|
52
|
+
UsedPercents: 'Usage',
|
51
53
|
Read: 'Read',
|
52
54
|
Write: 'Write',
|
53
55
|
VDisks: 'VDisks',
|
54
|
-
Missing: '
|
56
|
+
Missing: 'Degraded',
|
55
57
|
};
|
56
58
|
|
57
59
|
const b = cn('global-storage-groups');
|
@@ -60,8 +62,8 @@ function setSortOrder(visibleEntities: keyof typeof VisibleEntities): SortOrder
|
|
60
62
|
switch (visibleEntities) {
|
61
63
|
case VisibleEntities.All: {
|
62
64
|
return {
|
63
|
-
columnId: TableColumnsIds.
|
64
|
-
order: DataTable.
|
65
|
+
columnId: TableColumnsIds.Missing,
|
66
|
+
order: DataTable.DESCENDING,
|
65
67
|
};
|
66
68
|
}
|
67
69
|
case VisibleEntities.Missing: {
|
@@ -108,6 +110,37 @@ function StorageGroups({data, tableSettings, visibleEntities, nodes}: StorageGro
|
|
108
110
|
},
|
109
111
|
align: DataTable.LEFT,
|
110
112
|
},
|
113
|
+
{
|
114
|
+
name: TableColumnsIds.Missing,
|
115
|
+
header: tableColumnsNames[TableColumnsIds.Missing],
|
116
|
+
width: 100,
|
117
|
+
render: ({value, row}) => value ? (
|
118
|
+
<Label theme={getDegradedSeverity(row)}>Degraded: {value}</Label>
|
119
|
+
) : '-',
|
120
|
+
align: DataTable.LEFT,
|
121
|
+
defaultOrder: DataTable.DESCENDING,
|
122
|
+
},
|
123
|
+
{
|
124
|
+
name: TableColumnsIds.UsedPercents,
|
125
|
+
header: tableColumnsNames[TableColumnsIds.UsedPercents],
|
126
|
+
width: 100,
|
127
|
+
render: ({row}) => {
|
128
|
+
const usage = getUsage(row, 5);
|
129
|
+
// without a limit the usage can be evaluated as 0,
|
130
|
+
// but the absence of a value is more clear
|
131
|
+
return row.Limit ? (
|
132
|
+
<Label
|
133
|
+
theme={getUsageSeverity(usage)}
|
134
|
+
className={b('usage-label', {overload: usage >= 100})}
|
135
|
+
>
|
136
|
+
≥ {usage}%
|
137
|
+
</Label>
|
138
|
+
) : '-';
|
139
|
+
},
|
140
|
+
// without a limit exclude usage from sort to display at the bottom
|
141
|
+
sortAccessor: (row) => row.Limit ? getUsage(row) : null,
|
142
|
+
align: DataTable.LEFT,
|
143
|
+
},
|
111
144
|
{
|
112
145
|
name: TableColumnsIds.GroupID,
|
113
146
|
header: tableColumnsNames[TableColumnsIds.GroupID],
|
@@ -135,18 +168,6 @@ function StorageGroups({data, tableSettings, visibleEntities, nodes}: StorageGro
|
|
135
168
|
},
|
136
169
|
align: DataTable.RIGHT,
|
137
170
|
},
|
138
|
-
// {
|
139
|
-
// name: tableColumnsIds.UsedPercents,
|
140
|
-
// header: tableColumnsNames[tableColumnsIds.UsedPercents],
|
141
|
-
// width: '100px',
|
142
|
-
// render: ({row}) => {
|
143
|
-
// return (
|
144
|
-
// Math.round((row[tableColumnsIds.Used] * 100) / row[tableColumnsIds.Limit]) +
|
145
|
-
// '%'
|
146
|
-
// );
|
147
|
-
// },
|
148
|
-
// align: DataTable.RIGHT,
|
149
|
-
// },
|
150
171
|
{
|
151
172
|
name: TableColumnsIds.UsedSpaceFlag,
|
152
173
|
header: tableColumnsNames[TableColumnsIds.UsedSpaceFlag],
|
@@ -184,47 +205,44 @@ function StorageGroups({data, tableSettings, visibleEntities, nodes}: StorageGro
|
|
184
205
|
},
|
185
206
|
align: DataTable.RIGHT,
|
186
207
|
},
|
187
|
-
{
|
188
|
-
name: TableColumnsIds.Missing,
|
189
|
-
header: tableColumnsNames[TableColumnsIds.Missing],
|
190
|
-
width: 100,
|
191
|
-
align: DataTable.CENTER,
|
192
|
-
defaultOrder: DataTable.DESCENDING,
|
193
|
-
},
|
194
208
|
{
|
195
209
|
name: TableColumnsIds.VDisks,
|
196
210
|
className: b('vdisks-column'),
|
197
211
|
header: tableColumnsNames[TableColumnsIds.VDisks],
|
198
212
|
render: ({value, row}) => (
|
199
213
|
<div className={b('vdisks-wrapper')}>
|
200
|
-
{_.map(value as TVDiskStateInfo[], (el) =>
|
201
|
-
Array.isArray(el.Donors)
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
214
|
+
{_.map(value as TVDiskStateInfo[], (el) => {
|
215
|
+
const donors = Array.isArray(el.Donors) ? el.Donors.filter(isFullDonorData) : [];
|
216
|
+
|
217
|
+
return (
|
218
|
+
donors.length > 0 ? (
|
219
|
+
<Stack className={b('vdisks-item')} key={stringifyVdiskId(el.VDiskId)}>
|
220
|
+
<Vdisk
|
221
|
+
{...el}
|
222
|
+
PoolName={row[TableColumnsIds.PoolName]}
|
223
|
+
nodes={nodes}
|
224
|
+
/>
|
225
|
+
{donors.map((donor) => (
|
226
|
+
<Vdisk
|
227
|
+
{...donor}
|
228
|
+
// donor and acceptor are always in the same group
|
229
|
+
PoolName={row[TableColumnsIds.PoolName]}
|
230
|
+
nodes={nodes}
|
231
|
+
key={stringifyVdiskId(donor.VDiskId)}
|
232
|
+
/>
|
233
|
+
))}
|
234
|
+
</Stack>
|
235
|
+
) : (
|
236
|
+
<div className={b('vdisks-item')} key={stringifyVdiskId(el.VDiskId)}>
|
209
237
|
<Vdisk
|
210
|
-
{...
|
211
|
-
// donor and acceptor are always in the same group
|
238
|
+
{...el}
|
212
239
|
PoolName={row[TableColumnsIds.PoolName]}
|
213
240
|
nodes={nodes}
|
214
|
-
key={stringifyVdiskId(donor.VDiskId)}
|
215
241
|
/>
|
216
|
-
|
217
|
-
|
218
|
-
)
|
219
|
-
|
220
|
-
<Vdisk
|
221
|
-
{...el}
|
222
|
-
PoolName={row[TableColumnsIds.PoolName]}
|
223
|
-
nodes={nodes}
|
224
|
-
/>
|
225
|
-
</div>
|
226
|
-
)
|
227
|
-
))}
|
242
|
+
</div>
|
243
|
+
)
|
244
|
+
);
|
245
|
+
})}
|
228
246
|
</div>
|
229
247
|
),
|
230
248
|
align: DataTable.CENTER,
|
@@ -1 +1,51 @@
|
|
1
|
+
import type {TVDiskStateInfo, TVSlotId} from '../../../types/api/storage';
|
2
|
+
import type {IStoragePoolGroup} from '../../../types/store/storage';
|
3
|
+
|
1
4
|
export * from './constants';
|
5
|
+
|
6
|
+
export const isFullDonorData = (donor: TVDiskStateInfo | TVSlotId): donor is TVDiskStateInfo =>
|
7
|
+
'VDiskId' in donor;
|
8
|
+
|
9
|
+
const generateEvaluator = (warn: number, crit: number) =>
|
10
|
+
(value: number) => {
|
11
|
+
if (0 <= value && value < warn) {
|
12
|
+
return 'success';
|
13
|
+
}
|
14
|
+
|
15
|
+
if (warn <= value && value < crit) {
|
16
|
+
return 'warning';
|
17
|
+
}
|
18
|
+
|
19
|
+
if (crit <= value) {
|
20
|
+
return 'danger';
|
21
|
+
}
|
22
|
+
|
23
|
+
return undefined;
|
24
|
+
};
|
25
|
+
|
26
|
+
const defaultDegradationEvaluator = generateEvaluator(1, 2);
|
27
|
+
|
28
|
+
const degradationEvaluators = {
|
29
|
+
'block-4-2': generateEvaluator(1, 2),
|
30
|
+
'mirror-3-dc': generateEvaluator(1, 3),
|
31
|
+
};
|
32
|
+
|
33
|
+
const canEvaluateErasureSpecies = (value?: string): value is keyof typeof degradationEvaluators =>
|
34
|
+
value !== undefined && value in degradationEvaluators;
|
35
|
+
|
36
|
+
export const getDegradedSeverity = (group: IStoragePoolGroup) => {
|
37
|
+
const evaluate = canEvaluateErasureSpecies(group.ErasureSpecies) ?
|
38
|
+
degradationEvaluators[group.ErasureSpecies] :
|
39
|
+
defaultDegradationEvaluator;
|
40
|
+
|
41
|
+
return evaluate(group.Missing);
|
42
|
+
};
|
43
|
+
|
44
|
+
export const getUsageSeverity = generateEvaluator(80, 85);
|
45
|
+
|
46
|
+
export const getUsage = (data: IStoragePoolGroup, step = 1) => {
|
47
|
+
// if limit is 0, display 0
|
48
|
+
const usage = Math.round((data.Used * 100) / data.Limit) || 0;
|
49
|
+
|
50
|
+
return Math.floor(usage / step) * step;
|
51
|
+
};
|
package/dist/services/api.d.ts
CHANGED
@@ -4,6 +4,15 @@ interface Window {
|
|
4
4
|
params: {path: string},
|
5
5
|
axiosOptions?: {concurrentId?: string},
|
6
6
|
) => Promise<import('../types/api/schema').TEvDescribeSchemeResult>;
|
7
|
+
getStorageInfo: (
|
8
|
+
params: {
|
9
|
+
tenant: string,
|
10
|
+
filter: string,
|
11
|
+
nodeId: string,
|
12
|
+
type: 'Groups' | 'Nodes',
|
13
|
+
},
|
14
|
+
axiosOptions?: {concurrentId?: string},
|
15
|
+
) => Promise<import('../types/api/storage').TStorageInfo>;
|
7
16
|
[method: string]: Function;
|
8
17
|
};
|
9
18
|
}
|
package/dist/types/api/schema.ts
CHANGED
@@ -91,27 +91,19 @@ export interface TTableDescription {
|
|
91
91
|
export interface TPartitionConfig {
|
92
92
|
/** uint64 */
|
93
93
|
FollowerCount?: string;
|
94
|
-
/**
|
95
|
-
|
96
|
-
* @deprecated use FollowerGroups
|
97
|
-
*/
|
98
|
-
CrossDataCenterFollowerCount?: string;
|
94
|
+
/** @deprecated use FollowerGroups */
|
95
|
+
CrossDataCenterFollowerCount?: number;
|
99
96
|
/** 0 or 1 items */
|
100
97
|
FollowerGroups?: TFollowerGroup[];
|
101
98
|
}
|
102
99
|
|
103
100
|
export interface TFollowerGroup {
|
104
|
-
|
105
|
-
FollowerCount?: string;
|
101
|
+
FollowerCount?: number;
|
106
102
|
AllowLeaderPromotion?: boolean;
|
107
103
|
AllowClientRead?: boolean;
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
* uint32[]
|
112
|
-
* @deprecated use AllowedDataCenters
|
113
|
-
*/
|
114
|
-
AllowedDataCenterNumIDs?: string[];
|
104
|
+
AllowedNodeIDs?: number[];
|
105
|
+
/** @deprecated use AllowedDataCenters */
|
106
|
+
AllowedDataCenterNumIDs?: number[];
|
115
107
|
RequireAllDataCenters?: boolean;
|
116
108
|
LocalNodeOnly?: boolean;
|
117
109
|
RequireDifferentNodes?: boolean;
|
@@ -64,12 +64,11 @@ export enum EVDiskState {
|
|
64
64
|
|
65
65
|
interface TRank {
|
66
66
|
/**
|
67
|
-
* uint32
|
68
67
|
* Rank in percents; 0-100% is good; >100% is bad.
|
69
68
|
* Formula for rank calculation is the following:
|
70
69
|
* Rank = actual_value / max_allowed_value * 100
|
71
70
|
*/
|
72
|
-
RankPercent?:
|
71
|
+
RankPercent?: number;
|
73
72
|
|
74
73
|
/**
|
75
74
|
* Flag is the Rank transformed to something simple
|
@@ -84,16 +83,17 @@ interface TVDiskSatisfactionRank {
|
|
84
83
|
}
|
85
84
|
|
86
85
|
interface TVDiskID {
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
86
|
+
GroupID?: number;
|
87
|
+
GroupGeneration?: number;
|
88
|
+
Ring?: number;
|
89
|
+
Domain?: number;
|
90
|
+
VDisk?: number;
|
91
|
+
}
|
92
|
+
|
93
|
+
export interface TVSlotId {
|
94
|
+
NodeId?: number;
|
95
|
+
PDiskId?: number;
|
96
|
+
VSlotId?: number;
|
97
97
|
}
|
98
98
|
|
99
99
|
export interface TVDiskStateInfo {
|
@@ -102,18 +102,14 @@ export interface TVDiskStateInfo {
|
|
102
102
|
CreateTime?: string;
|
103
103
|
/** uint64 */
|
104
104
|
ChangeTime?: string;
|
105
|
-
|
106
|
-
|
107
|
-
/** uint32 */
|
108
|
-
VDiskSlotId?: string;
|
105
|
+
PDisk?: TPDiskStateInfo;
|
106
|
+
VDiskSlotId?: number;
|
109
107
|
/** uint64 */
|
110
108
|
Guid?: string;
|
111
109
|
/** uint64 */
|
112
110
|
Kind?: string;
|
113
|
-
|
114
|
-
|
115
|
-
/** uint32 */
|
116
|
-
Count?: string;
|
111
|
+
NodeId?: number;
|
112
|
+
Count?: number;
|
117
113
|
|
118
114
|
Overall?: EFlag;
|
119
115
|
|
@@ -154,7 +150,8 @@ export interface TVDiskStateInfo {
|
|
154
150
|
* VDisk actor instance guid
|
155
151
|
*/
|
156
152
|
InstanceGuid?: string;
|
157
|
-
Donors
|
153
|
+
// in reality it is `Donors: TVDiskStateInfo[] | TVSlotId[]`, but this way it is more error-proof
|
154
|
+
Donors?: Array<TVDiskStateInfo | TVSlotId>;
|
158
155
|
|
159
156
|
/** VDisk (Skeleton) Front Queue Status */
|
160
157
|
FrontQueues?: EFlag;
|
@@ -173,3 +170,49 @@ export interface TVDiskStateInfo {
|
|
173
170
|
*/
|
174
171
|
WriteThroughput?: string;
|
175
172
|
}
|
173
|
+
|
174
|
+
export interface TBSGroupStateInfo {
|
175
|
+
/** uint32 */
|
176
|
+
GroupID?: string;
|
177
|
+
ErasureSpecies?: string;
|
178
|
+
VDisks?: TVDiskStateInfo[];
|
179
|
+
/** uint64 */
|
180
|
+
ChangeTime?: string;
|
181
|
+
/** uint32 */
|
182
|
+
NodeId?: string; // filled during merge
|
183
|
+
/** uint32 */
|
184
|
+
GroupGeneration?: string;
|
185
|
+
Overall?: EFlag;
|
186
|
+
Latency?: EFlag;
|
187
|
+
/** uint32 */
|
188
|
+
Count?: string; // filled during group count
|
189
|
+
StoragePoolName?: string; // from BS_CONTROLLER
|
190
|
+
}
|
191
|
+
|
192
|
+
export interface TStoragePoolInfo {
|
193
|
+
Overall?: EFlag;
|
194
|
+
Name?: string;
|
195
|
+
Kind?: string;
|
196
|
+
Groups?: TBSGroupStateInfo[];
|
197
|
+
/** uint64 */
|
198
|
+
AcquiredUnits?: string;
|
199
|
+
AcquiredIOPS?: number;
|
200
|
+
/** uint64 */
|
201
|
+
AcquiredThroughput?: string;
|
202
|
+
/** uint64 */
|
203
|
+
AcquiredSize?: string;
|
204
|
+
MaximumIOPS?: number;
|
205
|
+
/** uint64 */
|
206
|
+
MaximumThroughput?: string;
|
207
|
+
/** uint64 */
|
208
|
+
MaximumSize?: string;
|
209
|
+
}
|
210
|
+
|
211
|
+
export interface TStorageInfo {
|
212
|
+
Overall?: EFlag;
|
213
|
+
StoragePools?: TStoragePoolInfo[];
|
214
|
+
/** uint64 */
|
215
|
+
TotalGroups?: string;
|
216
|
+
/** uint64 */
|
217
|
+
FoundGroups?: string;
|
218
|
+
}
|