Skip to content

Commit 74909ea

Browse files
committed
fix: review fixes
1 parent e3559bc commit 74909ea

File tree

12 files changed

+300
-21
lines changed

12 files changed

+300
-21
lines changed

src/containers/Storage/Disks/Disks.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
}
2727

2828
&__pdisk-item {
29-
width: 80px;
29+
min-width: 80px;
3030
}
3131
&__pdisk-progress-bar {
3232
--progress-bar-full-height: 20px;

src/containers/Storage/PDisk/PDisk.scss

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
11
.pdisk-storage {
2+
--pdisk-vdisk-width: 3px;
3+
--pdisk-gap-width: 2px;
4+
25
position: relative;
36

47
display: flex;
58
flex-direction: column;
9+
justify-content: flex-end;
610

711
min-width: 120px;
12+
width: calc(
13+
var(--pdisk-max-slots, 1) * var(--pdisk-vdisk-width) + (var(--pdisk-max-slots, 1) - 1) *
14+
var(--pdisk-gap-width)
15+
);
816

917
&__content {
1018
position: relative;
@@ -18,17 +26,17 @@
1826
&__vdisks {
1927
display: flex;
2028
flex: 0 0 auto;
21-
gap: 2px;
29+
gap: var(--pdisk-gap-width);
2230

2331
margin-bottom: 4px;
2432

2533
white-space: nowrap;
2634
}
2735

2836
&__vdisks-item {
29-
flex: 0 0 3px;
37+
flex: 0 0 var(--pdisk-vdisk-width);
3038

31-
min-width: 3px;
39+
min-width: var(--pdisk-vdisk-width);
3240

3341
.stack__layer {
3442
.data-table__row:hover & {

src/containers/Storage/PDisk/PDisk.tsx

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import './PDisk.scss';
1616

1717
const b = cn('pdisk-storage');
1818

19+
const PDISK_MAX_SLOTS_CSS_VAR = '--pdisk-max-slots';
20+
1921
interface PDiskProps {
2022
data?: PreparedPDisk;
2123
vDisks?: PreparedVDisk[];
@@ -25,6 +27,7 @@ interface PDiskProps {
2527
className?: string;
2628
progressBarClassName?: string;
2729
viewContext?: StorageViewContext;
30+
maximumSlotsPerDisk?: string;
2831
}
2932

3033
export const PDisk = ({
@@ -36,6 +39,7 @@ export const PDisk = ({
3639
className,
3740
progressBarClassName,
3841
viewContext,
42+
maximumSlotsPerDisk,
3943
}: PDiskProps) => {
4044
const {NodeId, PDiskId} = data;
4145
const pDiskIdsDefined = valueIsDefined(NodeId) && valueIsDefined(PDiskId);
@@ -73,7 +77,17 @@ export const PDisk = ({
7377
}
7478

7579
return (
76-
<div className={b(null, className)} ref={anchorRef}>
80+
<div
81+
className={b(null, className)}
82+
ref={anchorRef}
83+
style={
84+
maximumSlotsPerDisk
85+
? ({
86+
[PDISK_MAX_SLOTS_CSS_VAR]: maximumSlotsPerDisk,
87+
} as React.CSSProperties)
88+
: undefined
89+
}
90+
>
7791
{renderVDisks()}
7892
<HoverPopup
7993
showPopup={showPopup}

src/containers/Storage/StorageNodes/columns/StorageNodesColumns.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
gap: 10px;
99

1010
width: 100%;
11+
height: 40px;
1112
}
1213

1314
&__pdisks-item {

src/containers/Storage/StorageNodes/columns/columns.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,12 @@ const getPDisksColumn = ({viewContext}: GetStorageNodesColumnsParams): StorageNo
4545

4646
return (
4747
<div className={b('pdisks-item')} key={pDisk.PDiskId}>
48-
<PDisk data={pDisk} vDisks={vDisks} viewContext={viewContext} />
48+
<PDisk
49+
data={pDisk}
50+
vDisks={vDisks}
51+
viewContext={viewContext}
52+
maximumSlotsPerDisk={row.MaximumSlotsPerDisk}
53+
/>
4954
</div>
5055
);
5156
})}

src/containers/Storage/StorageNodes/getNodes.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export const getStorageNodes: FetchData<
5151
// Get mock configuration from URL parameters or use defaults
5252
const pdisks = parseInt(urlParams.get('pdisks') || '10', 10);
5353
const vdisksPerPDisk = parseInt(urlParams.get('vdisksPerPDisk') || '2', 10);
54-
response = generateNodes(5, {vdisksCount: pdisks * vdisksPerPDisk, pdisksCount: pdisks});
54+
response = generateNodes(5, {maxVdisksPerPDisk: vdisksPerPDisk, maxPdisks: pdisks});
5555
} else {
5656
response = await window.api.viewer.getNodes({
5757
type,

src/mocks/storage/nodes.ts

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -118,41 +118,72 @@ const generateVDisk = (nodeId: number, vdiskId: number, pdiskId: number) => ({
118118
});
119119

120120
interface NodeGeneratorOptions {
121-
vdisksCount?: number;
122-
pdisksCount?: number;
121+
maxVdisksPerPDisk?: number;
122+
maxPdisks?: number;
123123
}
124124

125125
const DEFAULT_OPTIONS: NodeGeneratorOptions = {
126-
vdisksCount: 12,
127-
pdisksCount: 4,
126+
maxVdisksPerPDisk: 3,
127+
maxPdisks: 4,
128128
};
129129

130130
const generateNode = (nodeId: number, options: NodeGeneratorOptions = {}): TNodeInfo => {
131-
const pdisksCount = options.pdisksCount ?? DEFAULT_OPTIONS.pdisksCount;
132-
const vdisksCount = options.vdisksCount ?? DEFAULT_OPTIONS.vdisksCount;
131+
const maxPdisks = options.maxPdisks ?? DEFAULT_OPTIONS.maxPdisks!;
132+
const maxVdisksPerPDisk = options.maxVdisksPerPDisk ?? DEFAULT_OPTIONS.maxVdisksPerPDisk!;
133+
134+
// Generate a random number of pdisks up to maxPdisks
135+
const pdisksCount = Math.floor(Math.random() * maxPdisks) + 1;
136+
137+
// For each pdisk, generate a random number of vdisks up to maxVdisksPerPDisk
138+
const pdiskVdisksCounts = Array.from({length: pdisksCount}, () =>
139+
Math.floor(Math.random() * maxVdisksPerPDisk),
140+
);
141+
const totalVdisks = pdiskVdisksCounts.reduce((sum: number, count: number) => sum + count, 0);
133142

134143
return {
135144
NodeId: nodeId,
136145
UptimeSeconds: 284,
137146
CpuUsage: 0.00947996,
138147
DiskSpaceUsage: 0.234985,
139148
SystemState: generateSystemState(nodeId),
140-
PDisks: Array.from({length: pdisksCount!}, (_, i) =>
149+
PDisks: Array.from({length: pdisksCount}, (_, i) =>
141150
generatePDisk(nodeId, i + 1, getRandomDiskSize()),
142151
),
143-
VDisks: Array.from({length: vdisksCount!}, (_, i) => {
144-
// Distribute VDisks evenly across PDisks
145-
const pdiskId = (i % pdisksCount!) + 1;
146-
return generateVDisk(nodeId, i, pdiskId);
152+
VDisks: Array.from({length: totalVdisks}, (_, i) => {
153+
// Find which pdisk this vdisk belongs to based on the distribution
154+
let pdiskIndex = 0;
155+
let vdiskCount = pdiskVdisksCounts[0];
156+
while (i >= vdiskCount && pdiskIndex < pdisksCount - 1) {
157+
pdiskIndex++;
158+
vdiskCount += pdiskVdisksCounts[pdiskIndex];
159+
}
160+
return generateVDisk(nodeId, i, pdiskIndex + 1);
147161
}),
148162
};
149163
};
150164

151165
export const generateNodes = (count = 1, options?: NodeGeneratorOptions): TNodesInfo => {
152166
const nodes = Array.from({length: count}, (_, i) => generateNode(i + 1, options));
167+
168+
// Calculate MaximumSlotsPerDisk as the maximum number of vdisks on any pdisk
169+
let maxSlotsPerDisk = 0;
170+
nodes.forEach((node) => {
171+
if (node.VDisks) {
172+
const pdiskVdiskCounts = new Map<number, number>();
173+
node.VDisks.forEach((vdisk) => {
174+
if (typeof vdisk.PDiskId === 'number') {
175+
const count = (pdiskVdiskCounts.get(vdisk.PDiskId) || 0) + 1;
176+
pdiskVdiskCounts.set(vdisk.PDiskId, count);
177+
maxSlotsPerDisk = Math.max(maxSlotsPerDisk, count);
178+
}
179+
});
180+
}
181+
});
182+
153183
return {
154184
TotalNodes: count.toString(),
155185
FoundNodes: count.toString(),
156186
Nodes: nodes,
187+
MaximumSlotsPerDisk: maxSlotsPerDisk.toString(),
157188
};
158189
};
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
import type {TNodeInfo} from '../../../../types/api/nodes';
2+
import {TPDiskState} from '../../../../types/api/pdisk';
3+
import {EVDiskState} from '../../../../types/api/vdisk';
4+
import type {TVDiskID} from '../../../../types/api/vdisk';
5+
import {calculateMaximumSlotsPerDisk} from '../utils';
6+
7+
const createVDiskId = (id: number): TVDiskID => ({
8+
GroupID: id,
9+
GroupGeneration: 1,
10+
Ring: 1,
11+
Domain: 1,
12+
VDisk: id,
13+
});
14+
15+
describe('calculateMaximumSlotsPerDisk', () => {
16+
it('should return providedMaximumSlotsPerDisk when it is provided', () => {
17+
const nodes: TNodeInfo[] = [];
18+
const providedMaximumSlotsPerDisk = '5';
19+
20+
expect(calculateMaximumSlotsPerDisk(nodes, providedMaximumSlotsPerDisk)).toBe('5');
21+
});
22+
23+
it('should return "1" for empty nodes array', () => {
24+
const nodes: TNodeInfo[] = [];
25+
26+
expect(calculateMaximumSlotsPerDisk(nodes)).toBe('1');
27+
});
28+
29+
it('should return "1" for undefined nodes', () => {
30+
expect(calculateMaximumSlotsPerDisk(undefined)).toBe('1');
31+
});
32+
33+
it('should return "1" for nodes without PDisks or VDisks', () => {
34+
const nodes: TNodeInfo[] = [
35+
{
36+
NodeId: 1,
37+
SystemState: {},
38+
},
39+
];
40+
41+
expect(calculateMaximumSlotsPerDisk(nodes)).toBe('1');
42+
});
43+
44+
it('should calculate maximum slots correctly for single node with one PDisk and multiple VDisks', () => {
45+
const nodes: TNodeInfo[] = [
46+
{
47+
NodeId: 1,
48+
SystemState: {},
49+
PDisks: [
50+
{
51+
PDiskId: 1,
52+
State: TPDiskState.Normal,
53+
},
54+
],
55+
VDisks: [
56+
{
57+
VDiskId: createVDiskId(1),
58+
PDiskId: 1,
59+
VDiskState: EVDiskState.OK,
60+
},
61+
{
62+
VDiskId: createVDiskId(2),
63+
PDiskId: 1,
64+
VDiskState: EVDiskState.OK,
65+
},
66+
],
67+
},
68+
];
69+
70+
expect(calculateMaximumSlotsPerDisk(nodes)).toBe('2');
71+
});
72+
73+
it('should calculate maximum slots across multiple nodes', () => {
74+
const nodes: TNodeInfo[] = [
75+
{
76+
NodeId: 1,
77+
SystemState: {},
78+
PDisks: [
79+
{
80+
PDiskId: 1,
81+
State: TPDiskState.Normal,
82+
},
83+
],
84+
VDisks: [
85+
{
86+
VDiskId: createVDiskId(1),
87+
PDiskId: 1,
88+
VDiskState: EVDiskState.OK,
89+
},
90+
],
91+
},
92+
{
93+
NodeId: 2,
94+
SystemState: {},
95+
PDisks: [
96+
{
97+
PDiskId: 2,
98+
State: TPDiskState.Normal,
99+
},
100+
],
101+
VDisks: [
102+
{
103+
VDiskId: createVDiskId(2),
104+
PDiskId: 2,
105+
VDiskState: EVDiskState.OK,
106+
},
107+
{
108+
VDiskId: createVDiskId(3),
109+
PDiskId: 2,
110+
VDiskState: EVDiskState.OK,
111+
},
112+
{
113+
VDiskId: createVDiskId(4),
114+
PDiskId: 2,
115+
VDiskState: EVDiskState.OK,
116+
},
117+
],
118+
},
119+
];
120+
121+
expect(calculateMaximumSlotsPerDisk(nodes)).toBe('3');
122+
});
123+
124+
it('should handle nodes with multiple PDisks', () => {
125+
const nodes: TNodeInfo[] = [
126+
{
127+
NodeId: 1,
128+
SystemState: {},
129+
PDisks: [
130+
{
131+
PDiskId: 1,
132+
State: TPDiskState.Normal,
133+
},
134+
{
135+
PDiskId: 2,
136+
State: TPDiskState.Normal,
137+
},
138+
],
139+
VDisks: [
140+
{
141+
VDiskId: createVDiskId(1),
142+
PDiskId: 1,
143+
VDiskState: EVDiskState.OK,
144+
},
145+
{
146+
VDiskId: createVDiskId(2),
147+
PDiskId: 1,
148+
VDiskState: EVDiskState.OK,
149+
},
150+
{
151+
VDiskId: createVDiskId(3),
152+
PDiskId: 2,
153+
VDiskState: EVDiskState.OK,
154+
},
155+
],
156+
},
157+
];
158+
159+
expect(calculateMaximumSlotsPerDisk(nodes)).toBe('2');
160+
});
161+
162+
it('should ignore VDisks with non-matching PDiskId', () => {
163+
const nodes: TNodeInfo[] = [
164+
{
165+
NodeId: 1,
166+
SystemState: {},
167+
PDisks: [
168+
{
169+
PDiskId: 1,
170+
State: TPDiskState.Normal,
171+
},
172+
],
173+
VDisks: [
174+
{
175+
VDiskId: createVDiskId(1),
176+
PDiskId: 1,
177+
VDiskState: EVDiskState.OK,
178+
},
179+
{
180+
VDiskId: createVDiskId(2),
181+
PDiskId: 2, // Non-matching PDiskId
182+
VDiskState: EVDiskState.OK,
183+
},
184+
],
185+
},
186+
];
187+
188+
expect(calculateMaximumSlotsPerDisk(nodes)).toBe('1');
189+
});
190+
});

0 commit comments

Comments
 (0)