Skip to content

Commit 6d7e3a3

Browse files
committed
ClusterOverview: refactor Utilization card and connect (kubevirt#307)
Utilization card is refactored to split concern between ClusterOverview and Dashboard components. Connected to real data. So far CPU only, others will follow.
1 parent 13a2eda commit 6d7e3a3

File tree

21 files changed

+659
-147
lines changed

21 files changed

+659
-147
lines changed

sass/_components.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@
2323
@import './components/Dashboard/inventory';
2424
@import './components/Dashboard/detail';
2525
@import './components/Dashboard/capacity';
26+
@import './components/Dashboard/utilization';
2627
@import './components/ClusterOverview/cluster-overview';
2728
@import './components/ClusterOverview/events';
2829
@import './components/ClusterOverview/consumers';
2930
@import './components/ClusterOverview/health';
3031
@import './components/ClusterOverview/compliance';
31-
@import './components/ClusterOverview/utilization';
3232
@import './components/CreateBaremetalHostDialog/create-baremetal-host-dialog';
3333

3434
/*

sass/components/ClusterOverview/utilization.scss

Lines changed: 0 additions & 3 deletions
This file was deleted.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
.kubevirt-utilization__item {
2+
border-bottom: 1px solid #d1d1d1;
3+
font-size: 1.333em; // aligned with the Inventory card
4+
5+
.row {
6+
display: flex;
7+
align-items: center;
8+
}
9+
}
10+
11+
.kubevirt-utilization__item-actual {
12+
text-align: right;
13+
}
14+
15+
.kubevirt-utilization__item-chart {
16+
border-left-width: 1px;
17+
border-left-style: solid;
18+
border-left-color: rgb(209, 209, 209);
19+
}

src/components/ClusterOverview/ClusterOverview.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import ComplianceConnected from './Compliance/Compliance';
1212
import EventsConnected from './Events/Events';
1313
import { InventoryConnected } from './Inventory/Inventory';
1414
import { CapacityConnected } from './Capacity/Capacity';
15-
import UtilizationConnected from './Utilization/Utilization';
15+
import { UtilizationConnected } from './Utilization/Utilization';
1616
import TopConsumersConnected from './TopConsumers/TopConsumers';
1717

1818
const MainCards = () => (

src/components/ClusterOverview/Utilization/Utilization.js

Lines changed: 61 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import React from 'react';
2-
import { SparklineChart, Row, Col } from 'patternfly-react';
32
import PropTypes from 'prop-types';
43

54
import {
@@ -8,74 +7,77 @@ import {
87
DashboardCardHeader,
98
DashboardCardTitle,
109
} from '../../Dashboard/DashboardCard';
11-
import { ClusterOverviewContextGenericConsumer } from '../ClusterOverviewContext';
10+
import { ClusterOverviewContext } from '../ClusterOverviewContext';
11+
import { InlineLoading } from '../../Loading';
12+
import { UtilizationBody } from '../../Dashboard/Utilization/UtilizationBody';
13+
import { UtilizationItem } from '../../Dashboard/Utilization/UtilizationItem';
14+
import { getCapacityStats, getUtilizationVectorStats } from '../../../selectors';
15+
import { formatBytes } from '../../../utils';
1216

13-
const UtilizationItem = ({ id, title, data, unit }) => (
14-
<div className="kubevirt-utilization__item">
15-
<Row>
16-
<Col lg={2} md={2} sm={2} xs={2}>
17-
<h2>{title}</h2>
18-
</Col>
19-
<Col lg={3} md={3} sm={3} xs={3}>
20-
<h2>{`${data[data.length - 1]} ${unit}`}</h2>
21-
</Col>
22-
<Col lg={7} md={7} sm={7} xs={7}>
23-
<SparklineChart
24-
id={id}
25-
data={{
26-
columns: [[unit, ...data]],
27-
unload: true,
28-
type: 'area',
29-
}}
30-
unloadBeforeLoad
31-
/>
32-
</Col>
33-
</Row>
34-
</div>
35-
);
36-
37-
UtilizationItem.propTypes = {
38-
id: PropTypes.string.isRequired,
39-
title: PropTypes.string.isRequired,
40-
data: PropTypes.array.isRequired,
41-
unit: PropTypes.string.isRequired,
42-
};
17+
export const Utilization = ({ cpuUtilization, memoryUtilization, memoryTotal, LoadingComponent }) => {
18+
const cpuStats = getUtilizationVectorStats(cpuUtilization);
4319

44-
class UtilizationBody extends React.PureComponent {
45-
render() {
46-
const { stats } = this.props;
47-
return Object.keys(stats).map(key => (
48-
<UtilizationItem id={key} key={key} title={stats[key].title} data={stats[key].data} unit={stats[key].unit} />
49-
));
20+
let memoryStats = null;
21+
let memoryTotalConverted;
22+
let memoryMax = 0;
23+
let maxConverted;
24+
const memoryStatsRaw = getUtilizationVectorStats(memoryUtilization);
25+
if (memoryStatsRaw) {
26+
memoryMax = Math.max(0, ...memoryStatsRaw);
27+
maxConverted = formatBytes(memoryMax);
28+
memoryStats = memoryStatsRaw.map(bytes => formatBytes(bytes, maxConverted.unit, 1).value);
29+
memoryTotalConverted = memoryTotal
30+
? formatBytes(getCapacityStats(memoryTotal), maxConverted.unit, 1).value
31+
: undefined;
32+
} else {
33+
maxConverted = formatBytes(memoryMax); // B
5034
}
51-
}
5235

53-
UtilizationBody.propTypes = {
54-
stats: PropTypes.object.isRequired,
36+
return (
37+
<DashboardCard>
38+
<DashboardCardHeader>
39+
<DashboardCardTitle>Cluster Utilization</DashboardCardTitle>
40+
</DashboardCardHeader>
41+
<DashboardCardBody>
42+
<UtilizationBody>
43+
<UtilizationItem
44+
unit="%"
45+
id="cpu"
46+
title="CPU"
47+
data={cpuStats}
48+
maxY={100}
49+
LoadingComponent={LoadingComponent}
50+
isLoading={!cpuUtilization}
51+
/>
52+
<UtilizationItem
53+
unit={maxConverted.unit}
54+
id="memory"
55+
title="Memory"
56+
data={memoryStats}
57+
maxY={memoryTotalConverted}
58+
LoadingComponent={LoadingComponent}
59+
isLoading={!memoryUtilization}
60+
/>
61+
</UtilizationBody>
62+
</DashboardCardBody>
63+
</DashboardCard>
64+
);
5565
};
5666

57-
export const Utilization = ({ stats, loaded }) => (
58-
<DashboardCard>
59-
<DashboardCardHeader>
60-
<DashboardCardTitle>Cluster utilization</DashboardCardTitle>
61-
</DashboardCardHeader>
62-
<DashboardCardBody isLoading={!loaded}>
63-
<UtilizationBody stats={stats} />
64-
</DashboardCardBody>
65-
</DashboardCard>
66-
);
67-
6867
Utilization.defaultProps = {
69-
loaded: false,
68+
cpuUtilization: null,
69+
memoryUtilization: null,
70+
memoryTotal: null,
71+
LoadingComponent: InlineLoading,
7072
};
7173

7274
Utilization.propTypes = {
73-
stats: PropTypes.object.isRequired,
74-
loaded: PropTypes.bool,
75+
cpuUtilization: PropTypes.object,
76+
memoryUtilization: PropTypes.object,
77+
memoryTotal: PropTypes.object,
78+
LoadingComponent: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
7579
};
7680

77-
const UtilizationConnected = () => (
78-
<ClusterOverviewContextGenericConsumer Component={Utilization} dataPath="utilizationStats" />
81+
export const UtilizationConnected = () => (
82+
<ClusterOverviewContext.Consumer>{props => <Utilization {...props} />}</ClusterOverviewContext.Consumer>
7983
);
80-
81-
export default UtilizationConnected;
Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,45 @@
11
import { Utilization } from '../Utilization';
22

3-
import { utilizationStats } from '../..';
3+
const time0 = 1000;
4+
const KiB = 1024;
5+
6+
export const utilizationStats = {
7+
cpuUtilization: {
8+
data: {
9+
result: [
10+
{
11+
values: [[time0, 0], [time0 + 10, 10], [time0 + 20, 15], [time0 + 30, 100], [time0 + 40, 50]],
12+
},
13+
],
14+
},
15+
},
16+
memoryUtilization: {
17+
data: {
18+
result: [
19+
{
20+
values: [
21+
[time0, 2 * KiB],
22+
[time0 + 10, 5 * KiB],
23+
[time0 + 20, 15 * KiB],
24+
[time0 + 30, 10 * KiB],
25+
[time0 + 40, 30 * KiB],
26+
],
27+
},
28+
],
29+
},
30+
},
31+
memoryTotal: {
32+
data: {
33+
result: [
34+
{
35+
value: [0, 100 * KiB],
36+
},
37+
],
38+
},
39+
},
40+
};
441

542
export default {
643
component: Utilization,
7-
props: { ...utilizationStats },
44+
props: utilizationStats,
845
};

src/components/ClusterOverview/Utilization/tests/Utilization.test.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ import { render } from 'enzyme';
44
import { Utilization } from '../Utilization';
55
import { default as UtilizationFixtures } from '../fixtures/Utilization.fixture';
66

7-
const testUtilizationOverview = () => <Utilization {...UtilizationFixtures.props} />;
8-
97
describe('<Utilization />', () => {
108
it('renders correctly', () => {
11-
const component = render(testUtilizationOverview());
9+
const component = render(<Utilization {...UtilizationFixtures.props} />);
10+
expect(component).toMatchSnapshot();
11+
});
12+
it('renders correctly in Loading state', () => {
13+
const component = render(<Utilization />);
1214
expect(component).toMatchSnapshot();
1315
});
1416
});

0 commit comments

Comments
 (0)