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 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
 
@@ -42,6 +42,12 @@
42
42
  vertical-align: top;
43
43
  text-overflow: ellipsis;
44
44
  }
45
+ &__usage-label {
46
+ &_overload {
47
+ color: var(--yc-color-text-light-primary);
48
+ background-color: var(--yc-color-base-danger-heavy);
49
+ }
50
+ }
45
51
  &__group-id {
46
52
  font-weight: 500;
47
53
  }
@@ -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: 'Used percents',
52
+ UsedPercents: 'Usage',
51
53
  Read: 'Read',
52
54
  Write: 'Write',
53
55
  VDisks: 'VDisks',
54
- Missing: '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.GroupID,
64
- order: DataTable.ASCENDING,
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) && el.Donors.length > 0 ? (
202
- <Stack className={b('vdisks-item')} key={stringifyVdiskId(el.VDiskId)}>
203
- <Vdisk
204
- {...el}
205
- PoolName={row[TableColumnsIds.PoolName]}
206
- nodes={nodes}
207
- />
208
- {el.Donors.map((donor) => (
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
- {...donor}
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
- </Stack>
218
- ) : (
219
- <div className={b('vdisks-item')} key={stringifyVdiskId(el.VDiskId)}>
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
+ };
@@ -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
  }
@@ -91,27 +91,19 @@ export interface TTableDescription {
91
91
  export interface TPartitionConfig {
92
92
  /** uint64 */
93
93
  FollowerCount?: string;
94
- /**
95
- * uint32
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
- /** uint32 */
105
- FollowerCount?: string;
101
+ FollowerCount?: number;
106
102
  AllowLeaderPromotion?: boolean;
107
103
  AllowClientRead?: boolean;
108
- /** uint32[] */
109
- AllowedNodeIDs?: string[];
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?: string;
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
- /** uint32 */
88
- GroupID?: string;
89
- /** uint32 */
90
- GroupGeneration?: string;
91
- /** uint32 */
92
- Ring?: string;
93
- /** uint32 */
94
- Domain?: string;
95
- /** uint32 */
96
- VDisk?: string;
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
- /** uint32 */
106
- PDiskId?: string;
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
- /** uint32 */
114
- NodeId?: string;
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?: TVDiskStateInfo[];
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
+ }
@@ -0,0 +1,11 @@
1
+ import type {TBSGroupStateInfo} from '../api/storage';
2
+
3
+ export interface IStoragePoolGroup extends TBSGroupStateInfo {
4
+ Read: number;
5
+ Write: number;
6
+ PoolName?: string;
7
+ Used: number;
8
+ Limit: number;
9
+ Missing: number;
10
+ UsedSpaceFlag: number;
11
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ydb-embedded-ui",
3
- "version": "1.11.0",
3
+ "version": "1.12.1",
4
4
  "files": [
5
5
  "dist"
6
6
  ],