mirror of
https://github.com/skooner-k8s/skooner.git
synced 2026-08-24 04:16:18 +00:00
Added a fancy crosshair and did some refactoring
This commit is contained in:
@@ -64,7 +64,7 @@ export default class Menu extends Base<MenuProps, MenuStates> {
|
||||
)}
|
||||
|
||||
{canView(rules, api.namespace) && (
|
||||
<MenuItem title='PrometheusGraph' path='prometheusgraph' resource='Node' onClick={onClick} />
|
||||
<MenuItem title='PrometheusGraphs' path='prometheusgraphs' resource='Node' onClick={onClick} />
|
||||
)}
|
||||
</Group>
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ import PersistentVolumeClaims from './views/persistentVolumeClaims';
|
||||
import PersistentVolumes from './views/persistentVolumes';
|
||||
import Pod from './views/pod';
|
||||
import Pods from './views/pods';
|
||||
import PrometheusGraph from './views/prometheusgraph';
|
||||
import PrometheusGraphs from './views/prometheusgraphs';
|
||||
import ReplicaSet from './views/replicaSet';
|
||||
import ReplicaSets from './views/replicaSets';
|
||||
import Role from './views/role';
|
||||
@@ -110,7 +110,7 @@ registerRoute('storageclass', () => <StorageClasses />);
|
||||
registerRoute('storageclass/:name', params => <StorageClass {...params} />);
|
||||
registerRoute('workload', () => <Workloads />);
|
||||
|
||||
registerRoute('prometheusgraph', () => <PrometheusGraph />);
|
||||
registerRoute('prometheusgraphs', () => <PrometheusGraphs />);
|
||||
// @ts-ignore
|
||||
registerRoute('workload/cronjob/:namespace/:name', params => <CronJob {...params} />);
|
||||
// @ts-ignore
|
||||
|
||||
@@ -1,96 +1,111 @@
|
||||
import React from 'react';
|
||||
import {XYPlot, XAxis, YAxis, HorizontalGridLines, LineSeries} from 'react-vis';
|
||||
import {XYPlot, XAxis, YAxis, HorizontalGridLines, LineSeries, Crosshair} from 'react-vis';
|
||||
import Base from '../components/base';
|
||||
|
||||
|
||||
const BASE_HTTP_URL = 'http://localhost:4654/prom';
|
||||
const GRAPH_QUERIES = [
|
||||
['instance:node_cpu:ratio', 'Node CPU Usage'],
|
||||
['instance:node_memory_utilisation:ratio', 'Node Memory Usage'],
|
||||
];
|
||||
|
||||
|
||||
type Props = {
|
||||
|
||||
queryString: string;
|
||||
title: string;
|
||||
}
|
||||
|
||||
type State = {
|
||||
metric: string;
|
||||
data: Map<string, Array<Array<any>>>;
|
||||
data: Array<any>;
|
||||
crosshairValues: any[];
|
||||
}
|
||||
|
||||
export default class PrometheusGraph extends Base<Props, State> {
|
||||
/**
|
||||
* Event handler for onMouseLeave.
|
||||
* @private
|
||||
*/
|
||||
onMouseLeave = () => {
|
||||
this.setState({crosshairValues: []});
|
||||
};
|
||||
|
||||
/**
|
||||
* Event handler for onNearestX.
|
||||
* @param {Object} value Selected value.
|
||||
* @param {index} index Index of the value in the data array.
|
||||
* @private
|
||||
*/
|
||||
onNearestX = (value: any, {index} : any) => {
|
||||
this.setState((prevState => ({
|
||||
...prevState,
|
||||
crosshairValues: [value],
|
||||
})));
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
// TODO: use proxy if ready
|
||||
const url = `${BASE_HTTP_URL}/api/v1/query_range`;
|
||||
this.setState((prevState => ({
|
||||
...prevState,
|
||||
data: new Map(),
|
||||
data: new Array<any>(),
|
||||
crosshairValues: [],
|
||||
})));
|
||||
for (let i = 0; i < GRAPH_QUERIES.length; i++) {
|
||||
const query = GRAPH_QUERIES[i][0];
|
||||
const params = {
|
||||
query,
|
||||
start: (Date.now() / 1000 - 60 * 60).toString(),
|
||||
end: (Date.now() / 1000).toString(), // One hour range
|
||||
step: '1',
|
||||
};
|
||||
fetch(`${url}?${new URLSearchParams(params).toString()}`)
|
||||
.then(result => result.json())
|
||||
.then((json) => {
|
||||
const jsonData = json.data;
|
||||
const graphData : Array<Array<any>> = jsonData.result[0]?.values.map((value: any) => ({x: value[0], y: +value[1]}));
|
||||
this.state.data.set(query, graphData);
|
||||
this.setState(prevState => ({
|
||||
...prevState,
|
||||
}));
|
||||
});
|
||||
}
|
||||
const params = {
|
||||
query: this.props.queryString,
|
||||
start: (Date.now() / 1000 - 60 * 60).toString(),
|
||||
end: (Date.now() / 1000).toString(), // One hour range
|
||||
step: '1',
|
||||
};
|
||||
fetch(`${url}?${new URLSearchParams(params).toString()}`)
|
||||
.then(result => result.json())
|
||||
.then((json) => {
|
||||
const jsonData = json.data;
|
||||
const graphData : Array<any> = jsonData.result[0]?.values.map((value: any) => ({x: value[0], y: +value[1]}));
|
||||
this.setState(prevState => ({
|
||||
...prevState,
|
||||
data: graphData,
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div>
|
||||
{this.state?.data && GRAPH_QUERIES.map((value) => {
|
||||
const query = value[0];
|
||||
const title = value[1];
|
||||
if (!this.state.data.get(query)) {
|
||||
return <div>
|
||||
<span style={{fontWeight: 'bold'}}>{title}</span>
|
||||
<div>Pending...</div>
|
||||
</div>;
|
||||
}
|
||||
return this.state.data.get(query)
|
||||
&& <div>
|
||||
<div style={{fontWeight: 'bold'}}>{title}</div>
|
||||
<XYPlot
|
||||
yPadding={60}
|
||||
width={600}
|
||||
height={600}
|
||||
xType="time">
|
||||
<HorizontalGridLines />
|
||||
<LineSeries
|
||||
data={this.state.data.get(query)}
|
||||
color={'#6822aa'}
|
||||
/>
|
||||
<XAxis
|
||||
tickFormat={function tickFormat(d) {
|
||||
const date = new Date(d * 1000);
|
||||
return date.toLocaleTimeString();
|
||||
}}
|
||||
tickLabelAngle={30}
|
||||
tickTotal={5}
|
||||
/>
|
||||
<YAxis
|
||||
tickFormat={function tickFormet(d) {
|
||||
return `${(d * 100).toFixed(2)}%`;
|
||||
}}
|
||||
tickLabelAngle={30}
|
||||
tickPadding={-5}
|
||||
/>
|
||||
</XYPlot>
|
||||
</div>;
|
||||
})}
|
||||
|
||||
</div>;
|
||||
if (!this.state || !this.state.data) {
|
||||
return <div>
|
||||
<span style={{fontWeight: 'bold'}}>{this.props.title}</span>
|
||||
<div>Pending...</div>
|
||||
</div>;
|
||||
}
|
||||
return this.state.data
|
||||
&& <div>
|
||||
<div style={{fontWeight: 'bold'}}>{this.props.title}</div>
|
||||
<XYPlot
|
||||
yPadding={60}
|
||||
width={600}
|
||||
height={600}
|
||||
xType="time"
|
||||
onMouseLeave={this.onMouseLeave}>
|
||||
<HorizontalGridLines />
|
||||
<LineSeries
|
||||
data={this.state.data}
|
||||
color={'#6822aa'}
|
||||
onNearestX={this.onNearestX}
|
||||
/>
|
||||
<Crosshair
|
||||
values={this.state.crosshairValues}
|
||||
className={'test-class-name'}
|
||||
/>
|
||||
<XAxis
|
||||
tickFormat={function tickFormat(d) {
|
||||
const date = new Date(d * 1000);
|
||||
return date.toLocaleTimeString();
|
||||
}}
|
||||
tickLabelAngle={30}
|
||||
tickTotal={5}
|
||||
/>
|
||||
<YAxis
|
||||
tickFormat={function tickFormet(d) {
|
||||
return `${(d * 100).toFixed(2)}%`;
|
||||
}}
|
||||
tickLabelAngle={30}
|
||||
tickPadding={-5}
|
||||
/>
|
||||
</XYPlot>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import React from 'react';
|
||||
import PrometheusGraph from './prometheusgraph';
|
||||
import Base from '../components/base';
|
||||
|
||||
|
||||
type Props = {
|
||||
|
||||
}
|
||||
|
||||
type State = {
|
||||
metric: string;
|
||||
data: Map<string, Array<Array<any>>>;
|
||||
crosshairValues: [];
|
||||
}
|
||||
|
||||
export default class PrometheusGraphs extends Base<Props, State> {
|
||||
// GRAPH_QUERIES = [
|
||||
// ['instance:node_cpu:ratio', 'Node CPU Usage'],
|
||||
// ['instance:node_memory_utilisation:ratio', 'Node Memory Usage'],
|
||||
// ];
|
||||
GRAPH_QUERIES = [
|
||||
{
|
||||
queryString: 'instance:node_cpu:ratio',
|
||||
title: 'Node CPU Usage',
|
||||
},
|
||||
{
|
||||
queryString: 'instance:node_memory_utilisation:ratio',
|
||||
title: 'Node Memory Usage',
|
||||
},
|
||||
]
|
||||
|
||||
render() {
|
||||
return <div>
|
||||
{/* eslint-disable-next-line react/jsx-key */}
|
||||
{this.GRAPH_QUERIES.map(query => <PrometheusGraph
|
||||
queryString={query.queryString}
|
||||
title={query.title}
|
||||
/>)}
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user