Skip to content

Commit

Permalink
feat: Show resoruces per node (#3452)
Browse files Browse the repository at this point in the history
* first impl

* improve tests

* improve details cards

* reorder machine infp

* apply review suggestions

* improve digit display

* fix
  • Loading branch information
dbadura authored Nov 15, 2024
1 parent 3fcec6c commit a0ff994
Show file tree
Hide file tree
Showing 10 changed files with 330 additions and 25 deletions.
9 changes: 7 additions & 2 deletions public/i18n/en.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ cluster-overview:
statistics:
cpu-usage: 'CPU Usage'
memory-usage: 'Memory Usage'
cpu-requests: 'CPU requests'
memory-requests: 'Memory requests'
cpu-limits: 'CPU limits'
memory-limits: 'Memory limits'
pods-overview: 'Pods Overview'
total-pods: 'Total Pods'
healthy-pods: 'Healthy Pods'
Expand Down Expand Up @@ -795,7 +799,7 @@ limit-ranges:
name_singular: LimitRange
title: Limit Ranges
machine-info:
architecture-cpus: Architecture and CPUs
architecture: Architecture
cpus: CPUs
gib: GiB
kube-proxy-version: Kube proxy version
Expand Down Expand Up @@ -876,7 +880,8 @@ node-details:
all: All messages
information: Information
internal-ip: Internal IP
pod-cidr: Pod CIDR
pod-cidr: Pod CIDRs
provider: Provider
warnings: Warnings
nodes:
title: Nodes
Expand Down
23 changes: 14 additions & 9 deletions src/components/Nodes/MachineInfo/MachineInfo.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import './MachineInfo.scss';
import { DynamicPageComponent } from 'shared/components/DynamicPageComponent/DynamicPageComponent';
import ResourceDetailsCard from 'shared/components/ResourceDetails/ResourceDetailsCard';

export function MachineInfo({ nodeInfo, capacity }) {
export function MachineInfo({ nodeInfo, capacity, spec }) {
const formattedMemory =
Math.round((parseInt(capacity.memory) / 1024 / 1024) * 10) / 10;
const { t } = useTranslation();
Expand All @@ -20,19 +20,24 @@ export function MachineInfo({ nodeInfo, capacity }) {
>
{`${nodeInfo.operatingSystem} (${nodeInfo.osImage})`}
</DynamicPageComponent.Column>
<DynamicPageComponent.Column
title={t('machine-info.architecture-cpus')}
>
{`${nodeInfo.architecture}, ${capacity.cpu} ${t(
'machine-info.cpus',
)}`}
<DynamicPageComponent.Column title={t('node-details.provider')}>
{spec.providerID}
</DynamicPageComponent.Column>
<DynamicPageComponent.Column title={t('machine-info.pods-capacity')}>
{capacity.pods}
<DynamicPageComponent.Column title={t('machine-info.architecture')}>
{nodeInfo.architecture}
</DynamicPageComponent.Column>
<DynamicPageComponent.Column title={t('machine-info.cpus')}>
{capacity.cpu}
</DynamicPageComponent.Column>
<DynamicPageComponent.Column title={t('machine-info.memory')}>
{`${formattedMemory} ${t('machine-info.gib')}`}
</DynamicPageComponent.Column>
<DynamicPageComponent.Column title={t('machine-info.pods-capacity')}>
{capacity.pods}
</DynamicPageComponent.Column>
<DynamicPageComponent.Column title={t('node-details.pod-cidr')}>
{spec.podCIDRs.join(',')}
</DynamicPageComponent.Column>
<DynamicPageComponent.Column
title={t('machine-info.kube-proxy-version')}
>
Expand Down
6 changes: 4 additions & 2 deletions src/components/Nodes/NodeDetails/NodeDetails.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useWindowTitle } from 'shared/hooks/useWindowTitle';
import { useTranslation } from 'react-i18next';
import { useNodeQuery } from '../nodeQueries';
import { useNodeQuery, useResourceByNode } from '../nodeQueries';
import { NodeDetailsCard } from '../NodeDetailsCard';
import { MachineInfo } from '../MachineInfo/MachineInfo';
import { NodeResources } from '../NodeResources/NodeResources';
Expand All @@ -17,6 +17,7 @@ export default function NodeDetails({ nodeName }) {
const { data, error, loading } = useNodeQuery(nodeName);
const { t } = useTranslation();
useWindowTitle(t('nodes.title_details', { nodeName }));
const { data: resources } = useResourceByNode(nodeName);

const filterByHost = e => e.source.host === nodeName;
const Events = (
Expand Down Expand Up @@ -53,6 +54,7 @@ export default function NodeDetails({ nodeName }) {
<MachineInfo
nodeInfo={data.node.status.nodeInfo}
capacity={data.node.status.capacity}
spec={data.node.spec}
/>
</div>
<Title
Expand All @@ -69,7 +71,7 @@ export default function NodeDetails({ nodeName }) {
className="flexwrap"
style={spacing.sapUiSmallMarginBeginEnd}
>
<NodeResources {...data} />
<NodeResources metrics={data.metrics} resources={resources} />
</div>
{Events}
</>
Expand Down
3 changes: 0 additions & 3 deletions src/components/Nodes/NodeDetailsCard.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,6 @@ export function NodeDetailsCard({ node, loading, error }) {
timestamp={node.metadata.creationTimestamp}
/>
</DynamicPageComponent.Column>
<DynamicPageComponent.Column title={t('node-details.pod-cidr')}>
{node.spec.podCIDR}
</DynamicPageComponent.Column>
<DynamicPageComponent.Column
title={t('node-details.internal-ip')}
>
Expand Down
68 changes: 67 additions & 1 deletion src/components/Nodes/NodeResources/NodeResources.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Card, CardHeader } from '@ui5/webcomponents-react';
import { roundTwoDecimals } from 'shared/utils/helpers';
import './NodeResources.scss';

export function NodeResources({ metrics }) {
export function NodeResources({ metrics, resources }) {
const { t } = useTranslation();
const { cpu, memory } = metrics || {};

Expand Down Expand Up @@ -42,6 +42,72 @@ export function NodeResources({ metrics }) {
)}GiB / ${roundTwoDecimals(memory.capacity)}GiB`}
/>
</Card>
<Card
className="radial-chart-card"
header={
<CardHeader
titleText={t('cluster-overview.statistics.cpu-requests')}
/>
}
>
<UI5RadialChart
color="var(--sapChart_OrderedColor_5)"
value={resources?.requests?.cpu}
max={cpu.capacity}
additionalInfo={`${roundTwoDecimals(
resources?.requests?.cpu,
)}m / ${roundTwoDecimals(cpu.capacity)}m`}
/>
</Card>
<Card
className="radial-chart-card"
header={
<CardHeader
titleText={t('cluster-overview.statistics.memory-requests')}
/>
}
>
<UI5RadialChart
color="var(--sapChart_OrderedColor_6)"
value={resources?.requests?.memory}
max={memory.capacity}
additionalInfo={`${roundTwoDecimals(
resources.requests?.memory,
)}GiB / ${roundTwoDecimals(memory.capacity)}GiB`}
/>
</Card>
<Card
className="radial-chart-card"
header={
<CardHeader titleText={t('cluster-overview.statistics.cpu-limits')} />
}
>
<UI5RadialChart
color="var(--sapChart_OrderedColor_5)"
value={resources?.limits?.cpu}
max={cpu.capacity}
additionalInfo={`${roundTwoDecimals(
resources?.limits?.cpu,
)}m / ${roundTwoDecimals(cpu.capacity)}m`}
/>
</Card>
<Card
className="radial-chart-card"
header={
<CardHeader
titleText={t('cluster-overview.statistics.memory-limits')}
/>
}
>
<UI5RadialChart
color="var(--sapChart_OrderedColor_6)"
value={resources?.limits?.memory}
max={memory.capacity}
additionalInfo={`${roundTwoDecimals(
resources.limits.memory,
)}GiB / ${roundTwoDecimals(memory.capacity)}GiB`}
/>
</Card>
</>
) : (
t('components.error-panel.error')
Expand Down
92 changes: 91 additions & 1 deletion src/components/Nodes/nodeQueries.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import React from 'react';
import React, { useMemo } from 'react';
import { useGet } from 'shared/hooks/BackendAPI/useGet';
import {
getBytes,
getCpus,
} from '../../resources/Namespaces/ResourcesUsage.js';

const round = (num, places) =>
Math.round(num * Math.pow(10, places)) / Math.pow(10, places);
Expand Down Expand Up @@ -109,3 +113,89 @@ export function useNodeQuery(nodeName) {
loading: metricsLoading || nodeLoading,
};
}

const emptyResources = {
limits: {
cpu: 0,
memory: 0,
},
requests: {
cpu: 0,
memory: 0,
},
};

function addResources(a, b) {
if (!a) {
if (!b) {
return structuredClone(emptyResources);
}
return b;
}
if (!b) {
if (!a) {
return structuredClone(emptyResources);
}
return a;
}
return {
limits: {
cpu: getCpus(a?.limits?.cpu) + getCpus(b?.limits?.cpu),
memory: getBytes(a?.limits?.memory) + getBytes(b?.limits?.memory),
},
requests: {
cpu: getCpus(a?.requests?.cpu) + getCpus(b?.requests?.cpu),
memory: getBytes(a?.requests?.memory) + getBytes(b?.requests?.memory),
},
};
}

function sumContainersResources(containers) {
return containers?.reduce((containerAccu, container) => {
return addResources(containerAccu, container.resources);
}, structuredClone(emptyResources));
}

export function calcNodeResources(pods) {
const nodeResources =
pods?.items?.reduce((accumulator, pod) => {
if (pod?.spec?.containers) {
const containerResources = sumContainersResources(
pod?.spec?.containers,
);
return addResources(accumulator, containerResources);
}
return accumulator;
}, structuredClone(emptyResources)) || structuredClone(emptyResources);

return {
limits: {
cpu: nodeResources.limits.cpu * 1000,
memory: nodeResources.limits.memory / Math.pow(1024, 3),
},
requests: {
cpu: nodeResources.requests.cpu * 1000,
memory: nodeResources.requests.memory / Math.pow(1024, 3),
},
};
}

export function useResourceByNode(nodeName) {
const [data, setData] = React.useState(null);
const { data: pods, error, loading } = useGet(
`/api/v1/pods?fieldSelector=spec.nodeName=${nodeName},status.phase!=Failed,status.phase!=Succeeded&limit=500`,
);

const nodeResources = useMemo(() => calcNodeResources(pods), [pods]);

React.useEffect(() => {
if (nodeResources) {
setData(nodeResources);
}
}, [nodeResources]);
return {
data,
error,
loading,
};
}
Loading

0 comments on commit a0ff994

Please sign in to comment.