Skip to content

Commit

Permalink
dashboard: Allow multi-line charts in UtilizationItem
Browse files Browse the repository at this point in the history
  • Loading branch information
mareklibra authored and rawagner committed Jan 23, 2020
1 parent 3607a4d commit bbdbc64
Show file tree
Hide file tree
Showing 16 changed files with 536 additions and 131 deletions.
2 changes: 1 addition & 1 deletion frontend/__tests__/components/graphs/area.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
PrometheusGraphLink,
} from '@console/internal/components/graphs/prometheus-graph';

const MOCK_DATA = [{ x: 1, y: 100 }];
const MOCK_DATA = [[{ x: 1, y: 100 }]];

describe('<AreaChart />', () => {
it('should render an area chart', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as _ from 'lodash';
import * as React from 'react';
import { Link } from 'react-router-dom';
import { connect, Dispatch } from 'react-redux';
import { DataPoint, PrometheusResponse } from '@console/internal/components/graphs';
import { DataPoint } from '@console/internal/components/graphs';
import { Humanize, resourcePathFromModel } from '@console/internal/components/utils';
import { Dropdown } from '@console/internal/components/utils/dropdown';
import { K8sKind, referenceForModel, K8sResourceKind } from '@console/internal/module/k8s';
Expand All @@ -15,6 +15,7 @@ import { featureReducerName } from '@console/internal/reducers/features';
import { RootState } from '@console/internal/redux';
import * as UIActions from '@console/internal/actions/ui';
import { getActivePerspective } from '@console/internal/reducers/ui';
import { getPrometheusQueryResponse } from '@console/internal/actions/dashboards';
import { PopoverPosition } from '@patternfly/react-core';
import { FLAGS } from '../../../constants';
import { getName, getNamespace } from '../../..';
Expand Down Expand Up @@ -100,8 +101,7 @@ const PopoverBodyInternal: React.FC<DashboardItemProps &
const consumerLoaded = _.get(resources, ['k8sResources', 'loaded']);
const consumersLoadError = _.get(resources, ['k8sResources', 'loadError']);

const error = prometheusResults.getIn([query, 'loadError']);
const data = prometheusResults.getIn([query, 'data']) as PrometheusResponse;
const [data, error] = getPrometheusQueryResponse(prometheusResults, query);
const bodyData = getInstantVectorStats(data, metric);

if (consumerLoaded && !consumersLoadError) {
Expand Down Expand Up @@ -153,6 +153,35 @@ const PopoverBodyInternal: React.FC<DashboardItemProps &
}
}, [canAccessMonitoring, setActivePerspective, activePerspective]);

let body: React.ReactNode;
if (error || consumersLoadError) {
body = <div className="text-secondary">Not available</div>;
} else if (!consumerLoaded || !data) {
body = (
<ul className="co-utilization-card-popover__consumer-list">
<li className="skeleton-consumer" />
<li className="skeleton-consumer" />
<li className="skeleton-consumer" />
<li className="skeleton-consumer" />
<li className="skeleton-consumer" />
</ul>
);
} else {
body = (
<>
<ul
className="co-utilization-card-popover__consumer-list"
aria-label={`Top consumer by ${model.labelPlural}`}
>
<ConsumerItems items={top5Data} model={model} />
</ul>
<Link to={monitoringURL} onClick={viewMoreAction}>
View more
</Link>
</>
);
}

return (
<div className="co-utilization-card-popover__body">
<h4 className="co-utilization-card-popover__title">
Expand All @@ -171,27 +200,7 @@ const PopoverBodyInternal: React.FC<DashboardItemProps &
selectedKey={referenceForModel(model)}
/>
)}
{consumerLoaded && data && !error ? (
<>
<ul
className="co-utilization-card-popover__consumer-list"
aria-label={`Top consumer by ${model.labelPlural}`}
>
<ConsumerItems items={top5Data} model={model} />
</ul>
<Link to={monitoringURL} onClick={viewMoreAction}>
View more
</Link>
</>
) : (
<ul className="co-utilization-card-popover__consumer-list">
<li className="skeleton-consumer" />
<li className="skeleton-consumer" />
<li className="skeleton-consumer" />
<li className="skeleton-consumer" />
<li className="skeleton-consumer" />
</ul>
)}
{body}
</div>
);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,76 @@ import { AreaChart, AreaChartStatus } from '@console/internal/components/graphs/
import { DataPoint } from '@console/internal/components/graphs';
import { ByteDataTypes } from 'packages/console-shared/src/graph-helper/data-utils';

const getCurrentData = (
humanizeValue: Humanize,
description: string,
data?: DataPoint[],
dataUnits?: string,
): string => {
let current: string;
if (data && data.length > 0) {
const latestData = data[data.length - 1];
current = humanizeValue(latestData.y).string;
if (dataUnits) {
current += ` ${dataUnits}`;
}
current += ` ${description.toLowerCase()}`;
}

return current;
};

export const MultilineUtilizationItem: React.FC<MultilineUtilizationItemProps> = React.memo(
({
title,
data,
dataUnits,
humanizeValue,
isLoading = false,
queries,
error,
TopConsumerPopovers,
byteDataType,
}) => {
const current = data.map((datum, index) =>
getCurrentData(humanizeValue, queries[index].desc, datum, dataUnits && dataUnits[index]),
);
const chart = (
<AreaChart
data={error ? [[]] : data}
loading={!error && isLoading}
query={queries[0].query}
xAxis={false}
humanize={humanizeValue}
padding={{ top: 13, left: 70, bottom: 0, right: 0 }}
height={70}
byteDataType={byteDataType}
/>
);

const currentValue = current.map((curr, index) => {
const TopConsumerPopover = TopConsumerPopovers && TopConsumerPopovers[index];
return TopConsumerPopover ? <TopConsumerPopover current={curr} /> : <div>{curr}</div>;
});

return (
<div className="co-utilization-card__item">
<div className="co-utilization-card__item-description">
<div className="co-utilization-card__item-section-multiline">
<h4 className="pf-c-title pf-m-md">{title}</h4>
{error || (!isLoading && !data.length) ? (
<div className="text-secondary">Not available</div>
) : (
<div className="co-utilization-card__item-description">{currentValue}</div>
)}
</div>
</div>
<div className="co-utilization-card__item-chart">{chart}</div>
</div>
);
},
);

export const UtilizationItem: React.FC<UtilizationItemProps> = React.memo(
({
title,
Expand Down Expand Up @@ -41,7 +111,7 @@ export const UtilizationItem: React.FC<UtilizationItemProps> = React.memo(

const chart = (
<AreaChart
data={error ? [] : data}
data={error ? [[]] : [data]}
loading={!error && isLoading}
query={query}
xAxis={false}
Expand Down Expand Up @@ -97,6 +167,24 @@ type UtilizationItemProps = {
TopConsumerPopover?: React.ComponentType<TopConsumerPopoverProp>;
};

type MultilineUtilizationItemProps = {
title: string;
data?: DataPoint[][];
dataUnits?: string[];
dataDescription?: string[];
isLoading: boolean;
humanizeValue: Humanize;
queries: QueryWithDescription[];
error: boolean;
byteDataType?: ByteDataTypes;
TopConsumerPopovers?: React.ComponentType<TopConsumerPopoverProp>[];
};

export type TopConsumerPopoverProp = {
current: string;
};

export type QueryWithDescription = {
query: string;
desc: string;
};
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-end;
}

.co-utilization-card__item-section {
Expand All @@ -46,6 +47,13 @@
width: 100%;
}

.co-utilization-card__item-section-multiline {
display: flex;
align-items: center;
width: 100%;
justify-content: space-between;
}

.co-utilization-card__item-text {
color: var(--pf-global--Color--200);
font-size: var(--pf-global--FontSize--sm);
Expand Down
20 changes: 12 additions & 8 deletions frontend/packages/console-shared/src/graph-helper/data-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ const log = (x: number, y: number) => {
};

// Get the larget unit seen in the dataframe within the supported range
const bestUnit = (dataPoints: DataPoint[], type) => {
const bestLevel = dataPoints.reduce((maxUnit, point) => {
const bestUnit = (dataPoints: DataPoint[][], type) => {
const flattenDataPoints = dataPoints.reduce((acc, arr) => acc.concat(arr), []);

const bestLevel = flattenDataPoints.reduce((maxUnit, point) => {
const index = Math.floor(log(_.get(type, 'divisor', 1024), point.y));
const unitIndex = index >= type.units.length ? type.units.length - 1 : index;
return maxUnit < unitIndex ? unitIndex : maxUnit;
Expand All @@ -17,22 +19,24 @@ const bestUnit = (dataPoints: DataPoint[], type) => {
};

// Array based procssor
export const processFrame = (dataPoints: DataPoint[], typeName: string): ProcessFrameResult => {
export const processFrame = (dataPoints: DataPoint[][], typeName: string): ProcessFrameResult => {
const type = getType(typeName);
let unit = null;
if (dataPoints) {
if (dataPoints && dataPoints[0]) {
// Get the appropriate unit and convert the dataset to that level
unit = bestUnit(dataPoints, type);
const frameLevel = type.units.indexOf(unit);
dataPoints.forEach((point) => {
point.y /= type.divisor ** frameLevel;
});
dataPoints.forEach((arr) =>
arr.forEach((point) => {
point.y /= type.divisor ** frameLevel;
}),
);
}
return { processedData: dataPoints, unit };
};

export type ProcessFrameResult = {
processedData: DataPoint[];
processedData: DataPoint[][];
unit: string;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ export enum VMQueries {
CPU_USAGE = 'CPU_USAGE',
MEMORY_USAGE = 'MEMORY_USAGE',
FILESYSTEM_USAGE = 'FILESYSTEM_USAGE',
NETWORK_INOUT_USAGE = 'NETWORK_INOUT_USAGE',
// NETWORK_IN_USAGE = 'NETWORK_IN_USAGE',
// NETWORK_OUT_USAGE = 'NETWORK_OUT_USAGE',
NETWORK_USAGE = 'NETWORK_USAGE',
NETWORK_IN_USAGE = 'NETWORK_IN_USAGE',
NETWORK_OUT_USAGE = 'NETWORK_OUT_USAGE',
}

const queries = {
Expand All @@ -21,17 +21,12 @@ const queries = {
[VMQueries.FILESYSTEM_USAGE]: _.template(
`sum(kubevirt_vmi_storage_traffic_bytes_total{exported_namespace='<%= namespace %>',name='<%= vmName %>'})`,
),
[VMQueries.NETWORK_INOUT_USAGE]: _.template(
`sum(kubevirt_vmi_network_traffic_bytes_total{exported_namespace='<%= namespace %>',name='<%= vmName %>'})`,
),
/* TODO: use when multi-line chart is ready
[VMQueries.NETWORK_IN_USAGE]: _.template(
`sum(kubevirt_vmi_network_traffic_bytes_total{type='rx',exported_namespace='<%= namespace %>',name='<%= vmName %>'})`,
),
[VMQueries.NETWORK_OUT_USAGE]: _.template(
`sum(kubevirt_vmi_network_traffic_bytes_total{type='tx',exported_namespace='<%= namespace %>',name='<%= vmName %>'})`,
),
*/
};

export const getUtilizationQueries = (props: {
Expand All @@ -42,6 +37,21 @@ export const getUtilizationQueries = (props: {
[VMQueries.CPU_USAGE]: queries[VMQueries.CPU_USAGE](props),
[VMQueries.MEMORY_USAGE]: queries[VMQueries.MEMORY_USAGE](props),
[VMQueries.FILESYSTEM_USAGE]: queries[VMQueries.FILESYSTEM_USAGE](props),
[VMQueries.NETWORK_INOUT_USAGE]: queries[VMQueries.NETWORK_INOUT_USAGE](props),
// [VMQueries.NETWORK_OUT_USAGE]: queries[VMQueries.NETWORK_OUT_USAGE](props),
});

export const getMultilineUtilizationQueries = (props: {
vmName: string;
namespace: string;
launcherPodName?: string;
}) => ({
[VMQueries.NETWORK_USAGE]: [
{
query: queries[VMQueries.NETWORK_IN_USAGE](props),
desc: 'In',
},
{
query: queries[VMQueries.NETWORK_OUT_USAGE](props),
desc: 'Out',
},
],
});

0 comments on commit bbdbc64

Please sign in to comment.