Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ClusterInfo): display groups stats #598

Merged
merged 2 commits into from
Dec 1, 2023
Merged
Show file tree
Hide file tree
Changes from all 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/components/ProgressViewer/ProgressViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Props description:
interface ProgressViewerProps {
value?: number | string;
capacity?: number | string;
formatValues?: (value?: number, capacity?: number) => (string | undefined)[];
formatValues?: (value?: number, capacity?: number) => (string | number | undefined)[];
percents?: boolean;
className?: string;
size?: ProgressViewerSize;
Expand Down
2 changes: 2 additions & 0 deletions src/containers/Cluster/Cluster.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ function Cluster({
loading: clusterLoading,
wasLoaded: clusterWasLoaded,
error: clusterError,
groupsStats,
} = useTypedSelector((state) => state.cluster);
const {
nodes,
Expand Down Expand Up @@ -135,6 +136,7 @@ function Cluster({
<div className={b()} ref={container}>
<ClusterInfo
cluster={cluster}
groupsStats={groupsStats}
versionsValues={versionsValues}
loading={infoLoading}
error={clusterError}
Expand Down
19 changes: 14 additions & 5 deletions src/containers/Cluster/ClusterInfo/ClusterInfo.scss
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,6 @@
}
}

&__metric-field {
margin-top: -8px;
}

&__system-tablets {
display: flex;
flex-wrap: wrap;
Expand Down Expand Up @@ -83,14 +79,27 @@
margin-left: 15px;
padding: 0 !important;
}

&__links {
display: flex;
flex-flow: row wrap;

gap: 12px;
}

&__storage-groups-stats {
display: flex;
flex-direction: column;
gap: 11px;
}

&__groups-stats-bar {
cursor: pointer;
}

&__groups-stats-popup-content {
padding: 12px;
}

&__clipboard-button {
display: flex;
align-items: center;
Expand Down
128 changes: 104 additions & 24 deletions src/containers/Cluster/ClusterInfo/ClusterInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {Tablet} from '../../../components/Tablet';
import {ResponseError} from '../../../components/Errors/ResponseError';
import {ExternalLinkWithIcon} from '../../../components/ExternalLinkWithIcon/ExternalLinkWithIcon';
import {IconWrapper as Icon} from '../../../components/Icon/Icon';
import {ContentWithPopup} from '../../../components/ContentWithPopup/ContentWithPopup';

import type {IResponseError} from '../../../types/api/error';
import type {AdditionalClusterProps, ClusterLink} from '../../../types/additionalProps';
Expand All @@ -18,39 +19,122 @@ import type {TClusterInfo} from '../../../types/api/cluster';
import {backend, customBackend} from '../../../store';
import {formatStorageValues} from '../../../utils/dataFormatters/dataFormatters';
import {useSetting, useTypedSelector} from '../../../utils/hooks';
import {formatBytes, getSizeWithSignificantDigits} from '../../../utils/bytesParsers';
import {
CLUSTER_DEFAULT_TITLE,
CLUSTER_INFO_HIDDEN_KEY,
DEVELOPER_UI_TITLE,
} from '../../../utils/constants';
import type {
ClusterGroupsStats,
DiskErasureGroupsStats,
DiskGroupsStats,
} from '../../../store/reducers/cluster/types';

import {VersionsBar} from '../VersionsBar/VersionsBar';
import {ClusterInfoSkeleton} from '../ClusterInfoSkeleton/ClusterInfoSkeleton';
import i18n from '../i18n';

import {compareTablets} from './utils';

import './ClusterInfo.scss';

const b = block('cluster-info');

interface GroupsStatsPopupContentProps {
stats: DiskErasureGroupsStats;
}

const GroupsStatsPopupContent = ({stats}: GroupsStatsPopupContentProps) => {
const {diskType, erasure, allocatedSize, availableSize} = stats;

const sizeToConvert = getSizeWithSignificantDigits(Math.max(allocatedSize, availableSize), 2);

const convertedAllocatedSize = formatBytes({value: allocatedSize, size: sizeToConvert});
const convertedAvailableSize = formatBytes({value: availableSize, size: sizeToConvert});

const usage = Math.round((allocatedSize / (allocatedSize + availableSize)) * 100);

const info = [
{
label: i18n('disk-type'),
value: diskType,
},
{
label: i18n('erasure'),
value: erasure,
},
{
label: i18n('allocated'),
value: convertedAllocatedSize,
},
{
label: i18n('available'),
value: convertedAvailableSize,
},
{
label: i18n('usage'),
value: usage + '%',
},
];

return (
<InfoViewer dots={true} info={info} className={b('groups-stats-popup-content')} size="s" />
);
};

interface DiskGroupsStatsProps {
stats: DiskGroupsStats;
}

const DiskGroupsStatsBars = ({stats}: DiskGroupsStatsProps) => {
return (
<div className={b('storage-groups-stats')}>
{Object.values(stats).map((erasureStats) => (
<ContentWithPopup
placement={['right']}
key={erasureStats.erasure}
content={<GroupsStatsPopupContent stats={erasureStats} />}
>
<ProgressViewer
className={b('groups-stats-bar')}
value={erasureStats.createdGroups}
capacity={erasureStats.totalGroups}
/>
</ContentWithPopup>
))}
</div>
);
};

const getGroupsStatsFields = (groupsStats: ClusterGroupsStats) => {
return Object.keys(groupsStats).map((diskType) => {
return {
label: i18n('storage-groups', {diskType}),
value: <DiskGroupsStatsBars stats={groupsStats[diskType]} />,
};
});
};

const getInfo = (
cluster: TClusterInfo,
versionsValues: VersionValue[],
groupsStats: ClusterGroupsStats,
additionalInfo: InfoViewerItem[],
links: ClusterLink[],
) => {
const info: InfoViewerItem[] = [];

if (cluster.DataCenters) {
info.push({
label: 'DC',
label: i18n('dc'),
value: <Tags tags={cluster.DataCenters} />,
});
}

if (cluster.SystemTablets) {
info.push({
label: 'Tablets',
label: i18n('tablets'),
value: (
<div className={b('system-tablets')}>
{cluster.SystemTablets.sort(compareTablets).map((tablet, tabletIndex) => (
Expand All @@ -63,46 +147,40 @@ const getInfo = (

if (cluster.Tenants) {
info.push({
label: 'Databases',
label: i18n('databases'),
value: cluster.Tenants,
});
}

info.push(
{
label: 'Nodes',
value: (
<ProgressViewer
className={b('metric-field')}
value={cluster?.NodesAlive}
capacity={cluster?.NodesTotal}
/>
),
label: i18n('nodes'),
value: <ProgressViewer value={cluster?.NodesAlive} capacity={cluster?.NodesTotal} />,
},
{
label: 'Load',
value: (
<ProgressViewer
className={b('metric-field')}
value={cluster?.LoadAverage}
capacity={cluster?.NumberOfCpus}
/>
),
label: i18n('load'),
value: <ProgressViewer value={cluster?.LoadAverage} capacity={cluster?.NumberOfCpus} />,
},
{
label: 'Storage',
label: i18n('storage-size'),
value: (
<ProgressViewer
className={b('metric-field')}
value={cluster?.StorageUsed}
capacity={cluster?.StorageTotal}
formatValues={formatStorageValues}
/>
),
},
);

if (Object.keys(groupsStats).length) {
info.push(...getGroupsStatsFields(groupsStats));
}

info.push(
...additionalInfo,
{
label: 'Links',
label: i18n('links'),
value: (
<div className={b('links')}>
{links.map(({title, url}) => (
Expand All @@ -112,7 +190,7 @@ const getInfo = (
),
},
{
label: 'Versions',
label: i18n('versions'),
value: <VersionsBar versionsValues={versionsValues} />,
},
);
Expand All @@ -123,6 +201,7 @@ const getInfo = (
interface ClusterInfoProps {
cluster?: TClusterInfo;
versionsValues?: VersionValue[];
groupsStats?: ClusterGroupsStats;
loading?: boolean;
error?: IResponseError;
additionalClusterProps?: AdditionalClusterProps;
Expand All @@ -131,6 +210,7 @@ interface ClusterInfoProps {
export const ClusterInfo = ({
cluster = {},
versionsValues = [],
groupsStats = {},
loading,
error,
additionalClusterProps = {},
Expand All @@ -151,7 +231,7 @@ export const ClusterInfo = ({

const {info = [], links = []} = additionalClusterProps;

const clusterInfo = getInfo(cluster, versionsValues, info, [
const clusterInfo = getInfo(cluster, versionsValues, groupsStats, info, [
{title: DEVELOPER_UI_TITLE, url: internalLink},
...links,
]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ interface ClusterInfoSkeletonProps {
rows?: number;
}

export const ClusterInfoSkeleton = ({rows = 7, className}: ClusterInfoSkeletonProps) => (
export const ClusterInfoSkeleton = ({rows = 8, className}: ClusterInfoSkeletonProps) => (
<div className={b(null, className)}>
{[...new Array(rows)].map((_, index) => (
<div className={b('row')} key={`skeleton-row-${index}`}>
Expand Down
16 changes: 16 additions & 0 deletions src/containers/Cluster/i18n/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"disk-type": "Disk Type",
"erasure": "Erasure",
"allocated": "Allocated",
"available": "Available",
"usage": "Usage",
"dc": "DC",
"tablets": "Tablets",
"databases": "Databases",
"nodes": "Nodes",
"load": "Load",
"storage-size": "Storage size",
"storage-groups": "Storage groups, {{diskType}}",
"links": "Links",
"versions": "Versions"
}
11 changes: 11 additions & 0 deletions src/containers/Cluster/i18n/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {i18n, Lang} from '../../../utils/i18n';

import en from './en.json';
import ru from './ru.json';

const COMPONENT = 'ydb-cluster';

i18n.registerKeyset(Lang.En, COMPONENT, en);
i18n.registerKeyset(Lang.Ru, COMPONENT, ru);

export default i18n.keyset(COMPONENT);
16 changes: 16 additions & 0 deletions src/containers/Cluster/i18n/ru.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"disk-type": "Тип диска",
"erasure": "Режим",
"allocated": "Использовано",
"available": "Доступно",
"usage": "Потребление",
"dc": "ДЦ",
"tablets": "Таблетки",
"databases": "Базы данных",
"nodes": "Узлы",
"load": "Нагрузка",
"storage-size": "Размер хранилища",
"storage-groups": "Группы хранения, {{diskType}}",
"links": "Ссылки",
"versions": "Версии"
}
Loading
Loading