Fixed dashboards capacity Widgets (#2055)
- Added new calculation for prometheus capacity - Added indicator colors to capacity widgets - Adjusted capacity in drives for common dashboard Signed-off-by: Benjamin Perez <benjamin@bexsoft.net>
This commit is contained in:
@@ -456,3 +456,8 @@ export interface IRetentionConfig {
|
||||
unit: string;
|
||||
validity: number;
|
||||
}
|
||||
|
||||
export interface IBytesCalc {
|
||||
total: number;
|
||||
unit: string;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
import storage from "local-storage-fallback";
|
||||
import {
|
||||
IBytesCalc,
|
||||
ICapacity,
|
||||
IErasureCodeCalc,
|
||||
IStorageDistribution,
|
||||
@@ -576,7 +577,7 @@ export const calculateBytes = (
|
||||
showDecimals = false,
|
||||
roundFloor = true,
|
||||
k8sUnit = false
|
||||
) => {
|
||||
): IBytesCalc => {
|
||||
let bytes;
|
||||
|
||||
if (typeof x === "string") {
|
||||
@@ -696,3 +697,15 @@ export const getCookieValue = (cookieName: string) => {
|
||||
?.pop() || ""
|
||||
);
|
||||
};
|
||||
|
||||
export const capacityColors = (usedSpace: number, maxSpace: number) => {
|
||||
const percCalculate = (usedSpace * 100) / maxSpace;
|
||||
|
||||
if (percCalculate >= 90) {
|
||||
return "#C83B51";
|
||||
} else if (percCalculate >= 70) {
|
||||
return "#FFAB0F";
|
||||
}
|
||||
|
||||
return "#07193E";
|
||||
};
|
||||
|
||||
@@ -19,7 +19,11 @@ import { Theme } from "@mui/material/styles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
import withStyles from "@mui/styles/withStyles";
|
||||
import { IDriveInfo } from "../types";
|
||||
import { niceBytes, niceBytesInt } from "../../../../common/utils";
|
||||
import {
|
||||
capacityColors,
|
||||
niceBytes,
|
||||
niceBytesInt,
|
||||
} from "../../../../common/utils";
|
||||
import { Box } from "@mui/material";
|
||||
import { Cell, Pie, PieChart } from "recharts";
|
||||
import { CircleIcon } from "../../../../icons";
|
||||
@@ -48,11 +52,13 @@ const driveStatusColor = (health_status: string) => {
|
||||
};
|
||||
|
||||
const DriveInfoItem = ({ drive }: ICardProps) => {
|
||||
const freeSpace = drive.totalSpace - drive.usedSpace;
|
||||
|
||||
const plotValues = [
|
||||
{ value: drive.totalSpace, color: "#D6D6D6", label: "Free Space" },
|
||||
{ value: freeSpace, color: "#D6D6D6", label: "Free Space" },
|
||||
{
|
||||
value: drive.usedSpace,
|
||||
color: "#073052",
|
||||
color: capacityColors(drive.usedSpace, drive.totalSpace),
|
||||
label: "Used Space",
|
||||
},
|
||||
];
|
||||
|
||||
@@ -22,7 +22,11 @@ import { widgetDetailsToPanel } from "../utils";
|
||||
import { ErrorResponseHandler } from "../../../../../common/types";
|
||||
import { useDispatch } from "react-redux";
|
||||
|
||||
import { niceBytes } from "../../../../../common/utils";
|
||||
import {
|
||||
calculateBytes,
|
||||
capacityColors,
|
||||
niceBytesInt,
|
||||
} from "../../../../../common/utils";
|
||||
import { Cell, Pie, PieChart } from "recharts";
|
||||
import { ReportedUsageIcon } from "../../../../../icons";
|
||||
import Loader from "../../../Common/Loader/Loader";
|
||||
@@ -43,8 +47,10 @@ const CapacityItem = ({
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [dataInner, setDataInner] = useState<Record<string, any>>([]);
|
||||
const [result, setResult] = useState<IDashboardPanel | null>(null);
|
||||
|
||||
const [totalUsableFree, setTotalUsableFree] = useState<number>(0);
|
||||
const [totalUsed, setTotalUsed] = useState<number>(0);
|
||||
const [totalUsable, setTotalUsable] = useState<number>(0);
|
||||
|
||||
useEffect(() => {
|
||||
if (propLoading) {
|
||||
@@ -73,8 +79,30 @@ const CapacityItem = ({
|
||||
)
|
||||
.then((res: any) => {
|
||||
const widgetsWithValue = widgetDetailsToPanel(res, value);
|
||||
setDataInner(widgetsWithValue.data);
|
||||
setResult(widgetsWithValue);
|
||||
|
||||
let tUsable = 0;
|
||||
let tUsed = 0;
|
||||
let tFree = 0;
|
||||
|
||||
widgetsWithValue.data.forEach((eachArray: any[]) => {
|
||||
eachArray.forEach((itemSum) => {
|
||||
switch (itemSum.legend) {
|
||||
case "Total Usable":
|
||||
tUsable += itemSum.value;
|
||||
break;
|
||||
case "Used Space":
|
||||
tUsed += itemSum.value;
|
||||
break;
|
||||
case "Usable Free":
|
||||
tFree += itemSum.value;
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
setTotalUsableFree(tFree);
|
||||
setTotalUsed(tUsed);
|
||||
setTotalUsable(tUsable);
|
||||
|
||||
setLoading(false);
|
||||
})
|
||||
@@ -85,21 +113,18 @@ const CapacityItem = ({
|
||||
}
|
||||
}, [loading, value, timeEnd, timeStart, dispatch, apiPrefix]);
|
||||
|
||||
const [middleLabel, unitValue] = (result?.innerLabel || "").split(" ");
|
||||
|
||||
const usableValueObj = dataInner[0];
|
||||
const { value: usableValue = 0 } = usableValueObj || { value: 0 };
|
||||
const usedConvert = calculateBytes(totalUsed, true, false);
|
||||
|
||||
const plotValues = [
|
||||
{
|
||||
value: parseInt(usableValue),
|
||||
value: totalUsableFree,
|
||||
color: "#D6D6D6",
|
||||
label: "Usable Space",
|
||||
label: "Usable Available Space",
|
||||
},
|
||||
{
|
||||
value: parseInt(usableValue),
|
||||
color: "#073052",
|
||||
label: "Usable Space",
|
||||
value: totalUsed,
|
||||
color: capacityColors(totalUsed, totalUsable),
|
||||
label: "Used Space",
|
||||
},
|
||||
];
|
||||
return (
|
||||
@@ -150,7 +175,7 @@ const CapacityItem = ({
|
||||
fontSize: 12,
|
||||
}}
|
||||
>
|
||||
{niceBytes(usableValue)}
|
||||
{niceBytesInt(totalUsableFree)}
|
||||
<br />
|
||||
<Box
|
||||
sx={{
|
||||
@@ -193,25 +218,51 @@ const CapacityItem = ({
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
"& .value": {
|
||||
fontSize: "50px",
|
||||
fontFamily: "Lato",
|
||||
fontWeight: 600,
|
||||
},
|
||||
"& .unit": {
|
||||
<Box>
|
||||
<Box
|
||||
sx={{
|
||||
color: "#5E5E5E",
|
||||
fontSize: "18px",
|
||||
marginLeft: "12px",
|
||||
marginTop: "10px",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<div className="value">{middleLabel}</div>{" "}
|
||||
<div className="unit">{unitValue}</div>
|
||||
fontWeight: "bold",
|
||||
fontSize: "14px",
|
||||
}}
|
||||
>
|
||||
Used:
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
"& .value": {
|
||||
fontSize: "50px",
|
||||
fontFamily: "Lato",
|
||||
fontWeight: 600,
|
||||
alignSelf: "flex-end",
|
||||
lineHeight: 1,
|
||||
},
|
||||
"& .unit": {
|
||||
color: "#5E5E5E",
|
||||
fontWeight: "bold",
|
||||
fontSize: "14px",
|
||||
marginLeft: "12px",
|
||||
alignSelf: "flex-end",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<div className="value">{usedConvert.total}</div>
|
||||
<div className="unit">{usedConvert.unit}</div>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
marginTop: "5px",
|
||||
"& .value": {
|
||||
color: "#5E5E5E",
|
||||
fontWeight: "bold",
|
||||
fontSize: "14px",
|
||||
textAlign: "right",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<div className="value">Of: {niceBytesInt(totalUsable)}</div>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
|
||||
@@ -83,6 +83,31 @@ export const panelsConfiguration: IDashboardPanel[] = [
|
||||
innerLabel: "N/A",
|
||||
labelDisplayFunction: niceBytes,
|
||||
},
|
||||
{
|
||||
id: 51,
|
||||
title: "Usable Capacity",
|
||||
data: [],
|
||||
dataOuter: [{ name: "outer", value: 100 }],
|
||||
widgetConfiguration: {
|
||||
outerChart: {
|
||||
colorList: ["#9c9c9c"],
|
||||
innerRadius: 0,
|
||||
outerRadius: 0,
|
||||
startAngle: 0,
|
||||
endAngle: 0,
|
||||
},
|
||||
innerChart: {
|
||||
colorList: colorsMain,
|
||||
innerRadius: 20,
|
||||
outerRadius: 50,
|
||||
startAngle: 90,
|
||||
endAngle: -200,
|
||||
},
|
||||
},
|
||||
type: widgetType.pieChart,
|
||||
innerLabel: "N/A",
|
||||
labelDisplayFunction: niceBytes,
|
||||
},
|
||||
{
|
||||
id: 68,
|
||||
title: "Data Usage Growth",
|
||||
@@ -560,35 +585,49 @@ export const widgetDetailsToPanel = (
|
||||
break;
|
||||
case widgetType.pieChart:
|
||||
if (typeOfPayload === "gauge") {
|
||||
let chartSeries = get(payloadData, "targets[0].result", []);
|
||||
|
||||
if (chartSeries === null) {
|
||||
chartSeries = [];
|
||||
}
|
||||
|
||||
const metricCalc = get(
|
||||
payloadData,
|
||||
"options.reduceOptions.calcs[0]",
|
||||
"lastNotNull"
|
||||
);
|
||||
|
||||
const valuesArray = chartSeries.length > 0 ? chartSeries[0].values : [];
|
||||
let chartSeries = get(payloadData, "targets", []).filter(
|
||||
(seriesItem: any) => seriesItem !== null
|
||||
);
|
||||
|
||||
const totalValues = calculateMainValue(valuesArray, metricCalc);
|
||||
const values = chartSeries.map((chartTarget: any) => {
|
||||
const resultMap =
|
||||
chartTarget.result && Array.isArray(chartTarget.result)
|
||||
? chartTarget.result
|
||||
: [];
|
||||
|
||||
const values = chartSeries.map((elementValue: any) => {
|
||||
const values = get(elementValue, "values", []);
|
||||
const metricKeyItem = Object.keys(elementValue.metric);
|
||||
const sortResult = values.sort(
|
||||
(value1: any[], value2: any[]) =>
|
||||
parseInt(value1[0][1]) - parseInt(value2[0][1])
|
||||
);
|
||||
const values = resultMap.map((elementValue: any) => {
|
||||
const values = get(elementValue, "values", []);
|
||||
const metricKeyItem = Object.keys(elementValue.metric);
|
||||
const sortResult = values.sort(
|
||||
(value1: any[], value2: any[]) =>
|
||||
parseInt(value1[0][1]) - parseInt(value2[0][1])
|
||||
);
|
||||
|
||||
const metricName = elementValue.metric[metricKeyItem[0]];
|
||||
const value = sortResult[sortResult.length - 1];
|
||||
return { name: metricName, value: parseInt(value[1]) };
|
||||
const metricName = elementValue.metric[metricKeyItem[0]];
|
||||
const value = sortResult[sortResult.length - 1];
|
||||
return {
|
||||
name: metricName,
|
||||
value: parseInt(value[1]),
|
||||
legend: chartTarget.legendFormat,
|
||||
};
|
||||
});
|
||||
|
||||
return values;
|
||||
});
|
||||
|
||||
const firstTarget =
|
||||
chartSeries[0].result && chartSeries[0].result.length > 0
|
||||
? chartSeries[0].result[0].values
|
||||
: [];
|
||||
|
||||
const totalValues = calculateMainValue(firstTarget, metricCalc);
|
||||
|
||||
const innerLabel = panelItem.labelDisplayFunction
|
||||
? panelItem.labelDisplayFunction(totalValues[1])
|
||||
: totalValues[1];
|
||||
|
||||
@@ -27,14 +27,12 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/minio/madmin-go"
|
||||
|
||||
"github.com/go-openapi/swag"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/restapi/operations"
|
||||
systemApi "github.com/minio/console/restapi/operations/system"
|
||||
"github.com/minio/madmin-go"
|
||||
)
|
||||
|
||||
func registerAdminInfoHandlers(api *operations.ConsoleAPI) {
|
||||
@@ -222,7 +220,7 @@ var widgets = []Metric{
|
||||
},
|
||||
{
|
||||
ID: 50,
|
||||
Title: "Current Usable Capacity",
|
||||
Title: "Current Usable Free Capacity",
|
||||
Type: "gauge",
|
||||
MaxDataPoints: 100,
|
||||
GridPos: GridPos{
|
||||
@@ -239,8 +237,44 @@ var widgets = []Metric{
|
||||
},
|
||||
},
|
||||
Targets: []Target{
|
||||
{
|
||||
Expr: `topk(1, sum(minio_cluster_capacity_usable_total_bytes{$__query}) by (instance))`,
|
||||
LegendFormat: "Total Usable",
|
||||
Step: 300,
|
||||
},
|
||||
{
|
||||
Expr: `topk(1, sum(minio_cluster_capacity_usable_free_bytes{$__query}) by (instance))`,
|
||||
LegendFormat: "Usable Free",
|
||||
Step: 300,
|
||||
},
|
||||
{
|
||||
Expr: `topk(1, sum(minio_cluster_capacity_usable_total_bytes{$__query}) by (instance)) - topk(1, sum(minio_cluster_capacity_usable_free_bytes{$__query}) by (instance))`,
|
||||
LegendFormat: "Used Space",
|
||||
Step: 300,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: 51,
|
||||
Title: "Current Usable Total Bytes",
|
||||
Type: "gauge",
|
||||
MaxDataPoints: 100,
|
||||
GridPos: GridPos{
|
||||
H: 6,
|
||||
W: 3,
|
||||
X: 6,
|
||||
Y: 0,
|
||||
},
|
||||
Options: MetricOptions{
|
||||
ReduceOptions: ReduceOptions{
|
||||
Calcs: []string{
|
||||
"lastNotNull",
|
||||
},
|
||||
},
|
||||
},
|
||||
Targets: []Target{
|
||||
{
|
||||
Expr: `topk(1, sum(minio_cluster_capacity_usable_total_bytes{$__query}) by (instance))`,
|
||||
LegendFormat: "",
|
||||
Step: 300,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user