Skip to content
This repository was archived by the owner on Apr 28, 2020. It is now read-only.

Commit 691f862

Browse files
committed
Added top consumer card for storage dashboard
1 parent 59589e3 commit 691f862

File tree

7 files changed

+230
-2
lines changed

7 files changed

+230
-2
lines changed

src/components/StorageOverview/StorageOverview.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import OCSHealthConnected from './OCSHealth/Health';
1212
import { CapacityConnected } from './Capacity/Capacity';
1313
import EventsConnected from './Events/Events';
1414
import { UtilizationConnected } from './Utilization/Utilization';
15+
import TopConsumersConnected from './TopConsumers/TopConsumers';
1516

1617
const MainCards = () => (
1718
<GridItem lg={6} md={12} sm={12}>
@@ -22,6 +23,9 @@ const MainCards = () => (
2223
<GridItem span={6}>
2324
<CapacityConnected />
2425
</GridItem>
26+
<GridItem span={12}>
27+
<TopConsumersConnected />
28+
</GridItem>
2529
</Grid>
2630
</GridItem>
2731
);
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
4+
import { LineChart, Row, Col } from 'patternfly-react';
5+
6+
import { InlineLoading } from '../../Loading';
7+
8+
import {
9+
DashboardCard,
10+
DashboardCardBody,
11+
DashboardCardHeader,
12+
DashboardCardTitle,
13+
} from '../../Dashboard/DashboardCard';
14+
import { StorageOverviewContext } from '../StorageOverviewContext/StorageOverviewContext';
15+
import { getTopConsumerVectorStats } from '../../../selectors';
16+
17+
const TopConsumersBody = ({ stats }) => {
18+
let results = 'Not data available';
19+
20+
if (stats.length) {
21+
const columnsConf = getTopConsumerVectorStats(stats);
22+
const { columns, unit } = columnsConf;
23+
const formatTime = x => {
24+
const dt = new Date(x);
25+
26+
let hrs = dt.getHours();
27+
hrs = hrs > 9 ? `${hrs}` : `0${hrs}`;
28+
29+
let mins = dt.getMinutes();
30+
mins = mins > 9 ? `${mins}` : `0${mins}`;
31+
32+
return `${hrs}:${mins}`;
33+
};
34+
35+
results = (
36+
<Row>
37+
<Col lg={12} md={12} sm={12} xs={12}>
38+
<LineChart
39+
className="kubevirt-top-consumers__body"
40+
id="line-chart"
41+
data={{
42+
x: 'x',
43+
columns,
44+
type: 'line',
45+
}}
46+
axis={{
47+
y: {
48+
label: {
49+
text: `Used Capacity(${unit})`,
50+
position: 'outer-top',
51+
},
52+
min: 0,
53+
padding: {
54+
bottom: 3,
55+
},
56+
},
57+
x: {
58+
tick: {
59+
format: formatTime,
60+
fit: true,
61+
values: [...columns[0].slice(1)],
62+
},
63+
},
64+
}}
65+
/>
66+
</Col>
67+
</Row>
68+
);
69+
}
70+
71+
return <div>{results}</div>;
72+
};
73+
74+
export const TopConsumers = ({ stats, LoadingComponent, loaded }) => (
75+
<DashboardCard>
76+
<DashboardCardHeader>
77+
<DashboardCardTitle>Top Projects by Used Capacity</DashboardCardTitle>
78+
</DashboardCardHeader>
79+
<DashboardCardBody isLoading={!loaded} LoadingComponent={LoadingComponent}>
80+
<TopConsumersBody stats={stats} />
81+
</DashboardCardBody>
82+
</DashboardCard>
83+
);
84+
85+
TopConsumersBody.propTypes = {
86+
stats: PropTypes.array.isRequired,
87+
};
88+
89+
TopConsumers.defaultProps = {
90+
stats: [],
91+
loaded: false,
92+
LoadingComponent: InlineLoading,
93+
};
94+
95+
TopConsumers.propTypes = {
96+
stats: PropTypes.array,
97+
loaded: PropTypes.bool,
98+
LoadingComponent: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
99+
};
100+
101+
const TopConsumersConnected = () => (
102+
<StorageOverviewContext.Consumer>{props => <TopConsumers {...props} />}</StorageOverviewContext.Consumer>
103+
);
104+
105+
export default TopConsumersConnected;
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { TopConsumers } from '../TopConsumers';
2+
3+
export const TopConsumerStats = {
4+
stats: [
5+
{
6+
metric: {
7+
namespace: 'openshift-namespace',
8+
},
9+
values: [
10+
[1554797100, '46161920'],
11+
[1554797400, '46161920'],
12+
[1554797700, '46161920'],
13+
[1554798000, '46161920'],
14+
[1554798300, '46161920'],
15+
[1554798600, '46161920'],
16+
],
17+
},
18+
],
19+
loaded: true,
20+
};
21+
22+
export default [
23+
{
24+
component: TopConsumers,
25+
props: { ...TopConsumerStats },
26+
},
27+
{
28+
component: TopConsumers,
29+
name: 'Loading top consumers',
30+
},
31+
];
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import React from 'react';
2+
import { render } from 'enzyme';
3+
4+
import { TopConsumers } from '../TopConsumers';
5+
import { TopConsumerStats } from '../fixtures/TopConsumers.fixture';
6+
7+
const testTopConsumersOverview = () => <TopConsumers {...TopConsumerStats} />;
8+
9+
describe('<TopConsumers />', () => {
10+
it('renders correctly', () => {
11+
const component = render(testTopConsumersOverview());
12+
expect(component).toMatchSnapshot();
13+
});
14+
});
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`<TopConsumers /> renders correctly 1`] = `
4+
<div
5+
class="card-pf kubevirt-dashboard__card"
6+
>
7+
<div
8+
class="card-pf-heading kubevirt-dashboard__card-heading"
9+
>
10+
<h2
11+
class="card-pf-title"
12+
>
13+
Top Projects by Used Capacity
14+
</h2>
15+
</div>
16+
<div
17+
class="card-pf-body"
18+
>
19+
<div>
20+
<div
21+
class="row"
22+
>
23+
<div
24+
class="col-lg-12 col-md-12 col-sm-12 col-xs-12"
25+
>
26+
<div
27+
class=" kubevirt-top-consumers__body"
28+
/>
29+
</div>
30+
</div>
31+
</div>
32+
</div>
33+
</div>
34+
`;

src/components/StorageOverview/fixtures/StorageOverview.fixture.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import { persistentVolumeClaims } from '../../../tests/mocks/persistentVolumeCla
1414
import { persistentVolumes } from '../../../tests/mocks/persistentVolume';
1515
import { osdDisksCount } from '../../../tests/mocks/disks';
1616

17+
import { TopConsumerStats } from '../TopConsumers/fixtures/TopConsumers.fixture';
18+
1719
export const nodes = [localhostNode];
1820
export const pvcs = persistentVolumeClaims;
1921
export const pvs = persistentVolumes;
@@ -38,6 +40,7 @@ export default [
3840
diskStats,
3941
eventsData,
4042
...utilizationStats,
43+
...TopConsumerStats,
4144
},
4245
},
4346
{

src/selectors/prometheus/selectors.js

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { get } from 'lodash';
1+
import { get, forEach } from 'lodash';
22

3-
import { parseNumber } from '../../utils';
3+
import { parseNumber, formatBytes } from '../../utils';
44

55
export const getConsumers = (results, nameLabel, formatLabel) => {
66
const result = get(results, 'data.result');
@@ -28,3 +28,40 @@ export const getLastUtilizationStat = response => {
2828
const history = getUtilizationVectorStats(response);
2929
return history ? history[history.length - 1] : null;
3030
};
31+
32+
export const getTopConsumerVectorStats = result => {
33+
let maxVal = 0;
34+
35+
forEach(result, namespace => {
36+
forEach(namespace.values, value => {
37+
maxVal = Math.max(maxVal, parseInt(value[1], 10));
38+
});
39+
});
40+
41+
const maxCapacityConveretd = { ...formatBytes(maxVal) };
42+
43+
const yAxisData = result.map(r => [
44+
r.metric.namespace,
45+
...r.values.map(array => formatBytes(array[1], maxCapacityConveretd.unit, 2).value),
46+
]);
47+
48+
let largestLengthArray = 0;
49+
let largestLengthArrayIndex = 0;
50+
51+
// timestamps count and values are not same for all the namespaces. Hence, finding the largest length array and taking its timestamps value as x-axis point
52+
forEach(result, (namespace, index) => {
53+
const len = namespace.values.length;
54+
if (len > largestLengthArray) {
55+
largestLengthArray = len;
56+
largestLengthArrayIndex = index;
57+
}
58+
});
59+
const xAxisData = ['x', ...result[largestLengthArrayIndex].values.map(array => new Date(array[0] * 1000))];
60+
61+
const stats = {
62+
columns: [xAxisData, ...yAxisData],
63+
unit: maxCapacityConveretd.unit,
64+
};
65+
66+
return stats;
67+
};

0 commit comments

Comments
 (0)