Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/containers/Storage/Disks/Disks.scss
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
}

&__pdisk-item {
width: 80px;
min-width: 80px;
}
&__pdisk-progress-bar {
--progress-bar-full-height: 20px;
Expand Down
28 changes: 21 additions & 7 deletions src/containers/Storage/PDisk/PDisk.scss
Original file line number Diff line number Diff line change
@@ -1,28 +1,42 @@
.pdisk-storage {
--pdisk-vdisk-width: 3px;
--pdisk-gap-width: 2px;

position: relative;

width: 120px;
display: flex;
flex-direction: column;
justify-content: flex-end;

width: calc(
var(--pdisk-max-slots, 1) * var(--pdisk-vdisk-width) + (var(--pdisk-max-slots, 1) - 1) *
var(--pdisk-gap-width)
);
min-width: 120px;

&__content {
position: relative;

display: block;
flex: 1;

border-radius: 4px; // to match interactive area with disk shape
border-radius: 4px;
}

&__vdisks {
display: flex;
// this breaks disks relative sizes, but disks rarely exceed one line
flex-wrap: wrap;
gap: 2px;
flex: 0 0 auto;
gap: var(--pdisk-gap-width);

margin-bottom: 4px;

white-space: nowrap;
}

&__vdisks-item {
flex-basis: 3px;
flex-shrink: 0;
flex: 0 0 var(--pdisk-vdisk-width);

min-width: var(--pdisk-vdisk-width);

.stack__layer {
.data-table__row:hover & {
Expand Down
16 changes: 15 additions & 1 deletion src/containers/Storage/PDisk/PDisk.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import './PDisk.scss';

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

const PDISK_MAX_SLOTS_CSS_VAR = '--pdisk-max-slots';

interface PDiskProps {
data?: PreparedPDisk;
vDisks?: PreparedVDisk[];
Expand All @@ -25,6 +27,7 @@ interface PDiskProps {
className?: string;
progressBarClassName?: string;
viewContext?: StorageViewContext;
maximumSlotsPerDisk?: string;
}

export const PDisk = ({
Expand All @@ -36,6 +39,7 @@ export const PDisk = ({
className,
progressBarClassName,
viewContext,
maximumSlotsPerDisk,
}: PDiskProps) => {
const {NodeId, PDiskId} = data;
const pDiskIdsDefined = valueIsDefined(NodeId) && valueIsDefined(PDiskId);
Expand Down Expand Up @@ -73,7 +77,17 @@ export const PDisk = ({
}

return (
<div className={b(null, className)} ref={anchorRef}>
<div
className={b(null, className)}
ref={anchorRef}
style={
maximumSlotsPerDisk
? ({
[PDISK_MAX_SLOTS_CSS_VAR]: maximumSlotsPerDisk,
} as React.CSSProperties)
: undefined
}
>
{renderVDisks()}
<HoverPopup
showPopup={showPopup}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,14 @@

&__pdisks-wrapper {
display: flex;
justify-content: left;
align-items: flex-end;
gap: 10px;

width: max-content;
width: 100%;
height: 40px;
}
&__pdisks-item {
flex-grow: 1;

max-width: 200px;
margin-right: 10px;

&:last-child {
margin-right: 0px;
}
&__pdisks-item {
display: flex;
flex-shrink: 0;
}
}
7 changes: 6 additions & 1 deletion src/containers/Storage/StorageNodes/columns/columns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,12 @@ const getPDisksColumn = ({viewContext}: GetStorageNodesColumnsParams): StorageNo

return (
<div className={b('pdisks-item')} key={pDisk.PDiskId}>
<PDisk data={pDisk} vDisks={vDisks} viewContext={viewContext} />
<PDisk
data={pDisk}
vDisks={vDisks}
viewContext={viewContext}
maximumSlotsPerDisk={row.MaximumSlotsPerDisk}
/>
</div>
);
})}
Expand Down
42 changes: 26 additions & 16 deletions src/containers/Storage/StorageNodes/getNodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
NODES_COLUMNS_TO_DATA_FIELDS,
getNodesColumnSortField,
} from '../../../components/nodesColumns/constants';
import {generateNodes} from '../../../mocks/storage/nodes';
import type {
PreparedStorageNode,
PreparedStorageNodeFilters,
Expand Down Expand Up @@ -44,22 +45,31 @@ export const getStorageNodes: FetchData<

const dataFieldsRequired = getRequiredDataFields(columnsIds, NODES_COLUMNS_TO_DATA_FIELDS);

const response = await window.api.viewer.getNodes({
type,
storage,
limit,
offset,
sort,
filter: searchValue,
uptime: getUptimeParamValue(nodesUptimeFilter),
with: visibleEntities,
database,
node_id: nodeId,
group_id: groupId,
filter_group: filterGroup,
filter_group_by: filterGroupBy,
fieldsRequired: dataFieldsRequired,
});
let response;
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.get('mocks')) {
// Get mock configuration from URL parameters or use defaults
const pdisks = parseInt(urlParams.get('pdisks') || '10', 10);
const vdisksPerPDisk = parseInt(urlParams.get('vdisksPerPDisk') || '2', 10);
response = generateNodes(5, {maxVdisksPerPDisk: vdisksPerPDisk, maxPdisks: pdisks});
} else {
response = await window.api.viewer.getNodes({
type,
storage,
limit,
offset,
sort,
filter: searchValue,
uptime: getUptimeParamValue(nodesUptimeFilter),
with: visibleEntities,
database,
node_id: nodeId,
group_id: groupId,
filter_group: filterGroup,
filter_group_by: filterGroupBy,
fieldsRequired: dataFieldsRequired,
});
}
const preparedResponse = prepareStorageNodesResponse(response);
return {
data: preparedResponse.nodes || [],
Expand Down
189 changes: 189 additions & 0 deletions src/mocks/storage/nodes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
import {EFlag} from '../../types/api/enums';
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for test only (will delete before merge)

import type {
TEndpoint,
TNodeInfo,
TNodesInfo,
TPoolStats,
TSystemStateInfo,
} from '../../types/api/nodes';
import {TPDiskState} from '../../types/api/pdisk';
import {EVDiskState} from '../../types/api/vdisk';

// Different disk sizes to simulate variety (in bytes)
const DISK_SIZES = [
'68719476736', // 64 GB
'137438953472', // 128 GB
'274877906944', // 256 GB
'549755813888', // 512 GB
'1099511627776', // 1 TB
];

const getRandomDiskSize = () => DISK_SIZES[Math.floor(Math.random() * DISK_SIZES.length)];

const generatePoolStats = (count = 5): TPoolStats[] => {
const poolNames = ['System', 'User', 'Batch', 'IO', 'IC'] as const;
return poolNames.slice(0, count).map((Name) => ({
Name,
Usage: Math.random() * 0.02,
Threads: Math.floor(Math.random() * 3) + 1,
}));
};

const generateEndpoints = (): TEndpoint[] => [
{Name: 'ic', Address: ':19001'},
{Name: 'http-mon', Address: ':8765'},
{Name: 'grpcs', Address: ':2135'},
{Name: 'grpc', Address: ':2136'},
];

const generateSystemState = (nodeId: number): TSystemStateInfo => ({
StartTime: '1734358137851',
ChangeTime: '1734358421375',
LoadAverage: [3.381347656, 2.489257813, 1.279296875],
NumberOfCpus: 8,
SystemState: EFlag.Green,
NodeId: nodeId,
Host: `localhost-${nodeId}`,
Version: 'main.95ce0df',
PoolStats: generatePoolStats(),
Endpoints: generateEndpoints(),
Roles: ['Bootstrapper', 'StateStorage', 'StateStorageBoard', 'SchemeBoard', 'Storage'],
MemoryLimit: '2147483648',
MaxDiskUsage: 0.002349853516,
Location: {
DataCenter: '1',
Rack: '1',
Unit: '1',
},
TotalSessions: 0,
CoresUsed: 0.07583969556,
CoresTotal: 8,
});

const generatePDisk = (nodeId: number, pdiskId: number, totalSize = '68719476736') => ({
PDiskId: pdiskId,
ChangeTime: '1734358142074',
Path: `/ydb_data/pdisk${pdiskId}l3ki78no.data`,
Guid: pdiskId.toString(),
Category: '0',
TotalSize: totalSize,
AvailableSize: (Number(totalSize) * 0.9).toString(), // 90% available by default
State: TPDiskState.Normal,
NodeId: nodeId,
Device: EFlag.Green,
Realtime: EFlag.Green,
SerialNumber: '',
SystemSize: '213909504',
LogUsedSize: '35651584',
LogTotalSize: '68486692864',
EnforcedDynamicSlotSize: '22817013760',
});

const generateVDisk = (nodeId: number, vdiskId: number, pdiskId: number) => ({
VDiskId: {
GroupID: vdiskId,
GroupGeneration: 1,
Ring: 0,
Domain: 0,
VDisk: 0,
},
ChangeTime: '1734358420919',
PDiskId: pdiskId,
VDiskSlotId: vdiskId,
Guid: '1',
Kind: '0',
NodeId: nodeId,
VDiskState: EVDiskState.OK,
DiskSpace: EFlag.Green,
SatisfactionRank: {
FreshRank: {
Flag: EFlag.Green,
},
LevelRank: {
Flag: EFlag.Green,
},
},
Replicated: true,
ReplicationProgress: 1,
ReplicationSecondsRemaining: 0,
AllocatedSize: '0',
AvailableSize: '22817013760',
HasUnreadableBlobs: false,
IncarnationGuid: '11528832187803248876',
InstanceGuid: '14836434871903384493',
FrontQueues: EFlag.Green,
StoragePoolName: 'static',
ReadThroughput: '0',
WriteThroughput: '420',
});

interface NodeGeneratorOptions {
maxVdisksPerPDisk?: number;
maxPdisks?: number;
}

const DEFAULT_OPTIONS: NodeGeneratorOptions = {
maxVdisksPerPDisk: 3,
maxPdisks: 4,
};

const generateNode = (nodeId: number, options: NodeGeneratorOptions = {}): TNodeInfo => {
const maxPdisks = options.maxPdisks ?? DEFAULT_OPTIONS.maxPdisks!;
const maxVdisksPerPDisk = options.maxVdisksPerPDisk ?? DEFAULT_OPTIONS.maxVdisksPerPDisk!;

// Generate a random number of pdisks up to maxPdisks
const pdisksCount = Math.floor(Math.random() * maxPdisks) + 1;

// For each pdisk, generate a random number of vdisks up to maxVdisksPerPDisk
const pdiskVdisksCounts = Array.from({length: pdisksCount}, () =>
Math.floor(Math.random() * maxVdisksPerPDisk),
);
const totalVdisks = pdiskVdisksCounts.reduce((sum: number, count: number) => sum + count, 0);

return {
NodeId: nodeId,
UptimeSeconds: 284,
CpuUsage: 0.00947996,
DiskSpaceUsage: 0.234985,
SystemState: generateSystemState(nodeId),
PDisks: Array.from({length: pdisksCount}, (_, i) =>
generatePDisk(nodeId, i + 1, getRandomDiskSize()),
),
VDisks: Array.from({length: totalVdisks}, (_, i) => {
// Find which pdisk this vdisk belongs to based on the distribution
let pdiskIndex = 0;
let vdiskCount = pdiskVdisksCounts[0];
while (i >= vdiskCount && pdiskIndex < pdisksCount - 1) {
pdiskIndex++;
vdiskCount += pdiskVdisksCounts[pdiskIndex];
}
return generateVDisk(nodeId, i, pdiskIndex + 1);
}),
};
};

export const generateNodes = (count = 1, options?: NodeGeneratorOptions): TNodesInfo => {
const nodes = Array.from({length: count}, (_, i) => generateNode(i + 1, options));

// Calculate MaximumSlotsPerDisk as the maximum number of vdisks on any pdisk
let maxSlotsPerDisk = 0;
nodes.forEach((node) => {
if (node.VDisks) {
const pdiskVdiskCounts = new Map<number, number>();
node.VDisks.forEach((vdisk) => {
if (typeof vdisk.PDiskId === 'number') {
const count = (pdiskVdiskCounts.get(vdisk.PDiskId) || 0) + 1;
pdiskVdiskCounts.set(vdisk.PDiskId, count);
maxSlotsPerDisk = Math.max(maxSlotsPerDisk, count);
}
});
}
});

return {
TotalNodes: count.toString(),
FoundNodes: count.toString(),
Nodes: nodes,
MaximumSlotsPerDisk: maxSlotsPerDisk.toString(),
};
};
Loading
Loading