Add Server Metrics Info Tab (#2340)
This commit is contained in:
@@ -24,11 +24,13 @@ package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/go-openapi/validate"
|
||||
)
|
||||
|
||||
// AdminInfoResponse admin info response
|
||||
@@ -36,15 +38,16 @@ import (
|
||||
// swagger:model adminInfoResponse
|
||||
type AdminInfoResponse struct {
|
||||
|
||||
// advanced metrics status
|
||||
// Enum: [not configured available unavailable]
|
||||
AdvancedMetricsStatus string `json:"advancedMetricsStatus,omitempty"`
|
||||
|
||||
// buckets
|
||||
Buckets int64 `json:"buckets,omitempty"`
|
||||
|
||||
// objects
|
||||
Objects int64 `json:"objects,omitempty"`
|
||||
|
||||
// prometheus not ready
|
||||
PrometheusNotReady bool `json:"prometheusNotReady,omitempty"`
|
||||
|
||||
// servers
|
||||
Servers []*ServerProperties `json:"servers"`
|
||||
|
||||
@@ -59,6 +62,10 @@ type AdminInfoResponse struct {
|
||||
func (m *AdminInfoResponse) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.validateAdvancedMetricsStatus(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateServers(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
@@ -73,6 +80,51 @@ func (m *AdminInfoResponse) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var adminInfoResponseTypeAdvancedMetricsStatusPropEnum []interface{}
|
||||
|
||||
func init() {
|
||||
var res []string
|
||||
if err := json.Unmarshal([]byte(`["not configured","available","unavailable"]`), &res); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, v := range res {
|
||||
adminInfoResponseTypeAdvancedMetricsStatusPropEnum = append(adminInfoResponseTypeAdvancedMetricsStatusPropEnum, v)
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
// AdminInfoResponseAdvancedMetricsStatusNotConfigured captures enum value "not configured"
|
||||
AdminInfoResponseAdvancedMetricsStatusNotConfigured string = "not configured"
|
||||
|
||||
// AdminInfoResponseAdvancedMetricsStatusAvailable captures enum value "available"
|
||||
AdminInfoResponseAdvancedMetricsStatusAvailable string = "available"
|
||||
|
||||
// AdminInfoResponseAdvancedMetricsStatusUnavailable captures enum value "unavailable"
|
||||
AdminInfoResponseAdvancedMetricsStatusUnavailable string = "unavailable"
|
||||
)
|
||||
|
||||
// prop value enum
|
||||
func (m *AdminInfoResponse) validateAdvancedMetricsStatusEnum(path, location string, value string) error {
|
||||
if err := validate.EnumCase(path, location, value, adminInfoResponseTypeAdvancedMetricsStatusPropEnum, true); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *AdminInfoResponse) validateAdvancedMetricsStatus(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.AdvancedMetricsStatus) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
// value enum
|
||||
if err := m.validateAdvancedMetricsStatusEnum("advancedMetricsStatus", "body", m.AdvancedMetricsStatus); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *AdminInfoResponse) validateServers(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.Servers) { // not required
|
||||
return nil
|
||||
|
||||
@@ -264,7 +264,6 @@ const DateRangeSelector = ({
|
||||
alignItems: "flex-end",
|
||||
display: "flex",
|
||||
justifyContent: "flex-end",
|
||||
marginRight: "35px",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Fragment } from "react";
|
||||
import React from "react";
|
||||
import { Box } from "@mui/material";
|
||||
import {
|
||||
ArrowRightIcon,
|
||||
@@ -67,7 +67,7 @@ interface IDashboardProps {
|
||||
|
||||
const getServersList = (usage: Usage | null) => {
|
||||
if (usage !== null) {
|
||||
return usage.servers.sort(function (a, b) {
|
||||
return [...usage.servers].sort(function (a, b) {
|
||||
const nameA = a.endpoint.toLowerCase();
|
||||
const nameB = b.endpoint.toLowerCase();
|
||||
if (nameA < nameB) {
|
||||
@@ -114,14 +114,8 @@ const BasicDashboard = ({ usage }: IDashboardProps) => {
|
||||
serversGroup;
|
||||
const drivesGroup = groupBy(allDrivesArray, "state");
|
||||
const { offline: offlineDrives = [], ok: onlineDrives = [] } = drivesGroup;
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
maxWidth: "1536px",
|
||||
margin: "auto",
|
||||
}}
|
||||
>
|
||||
<Box>
|
||||
<Box
|
||||
sx={{
|
||||
display: "grid",
|
||||
@@ -129,69 +123,8 @@ const BasicDashboard = ({ usage }: IDashboardProps) => {
|
||||
gridTemplateColumns: "1fr",
|
||||
gap: "27px",
|
||||
marginBottom: "40px",
|
||||
marginTop: "40px",
|
||||
marginLeft: "40px",
|
||||
marginRight: "40px",
|
||||
}}
|
||||
>
|
||||
<Box>
|
||||
{usage?.prometheusNotReady && (
|
||||
<HelpBox
|
||||
iconComponent={<PrometheusErrorIcon />}
|
||||
title={"We can't retrieve advanced metrics at this time"}
|
||||
help={
|
||||
<Fragment>
|
||||
MinIO Dashboard will display basic metrics as we couldn't
|
||||
connect to Prometheus successfully.
|
||||
<br /> <br />
|
||||
Please try again in a few minutes. If the problem persists,
|
||||
you can review your configuration and confirm that Prometheus
|
||||
server is up and running.
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
{!usage?.prometheusNotReady && (
|
||||
<HelpBox
|
||||
iconComponent={<PrometheusErrorIcon />}
|
||||
title={"We can’t retrieve advanced metrics at this time."}
|
||||
help={
|
||||
<Box>
|
||||
<Box
|
||||
sx={{
|
||||
fontSize: "14px",
|
||||
}}
|
||||
>
|
||||
MinIO Dashboard will display basic metrics as we couldn’t
|
||||
connect to Prometheus successfully. Please try again in a
|
||||
few minutes. If the problem persists, you can review your
|
||||
configuration and confirm that Prometheus server is up and
|
||||
running.
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
paddingTop: "20px",
|
||||
fontSize: "14px",
|
||||
"& a": {
|
||||
color: (theme) => theme.colors.link,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<a
|
||||
href="https://min.io/docs/minio/linux/operations/monitoring/collect-minio-metrics-using-prometheus.html"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Read more about Prometheus on our Docs site.
|
||||
</a>
|
||||
</Box>
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: "grid",
|
||||
@@ -355,6 +288,46 @@ const BasicDashboard = ({ usage }: IDashboardProps) => {
|
||||
<ServersList data={serverList} />
|
||||
</Box>
|
||||
</Box>
|
||||
{usage?.advancedMetricsStatus === "not configured" && (
|
||||
<Box>
|
||||
<HelpBox
|
||||
iconComponent={<PrometheusErrorIcon />}
|
||||
title={"We can’t retrieve advanced metrics at this time."}
|
||||
help={
|
||||
<Box>
|
||||
<Box
|
||||
sx={{
|
||||
fontSize: "14px",
|
||||
}}
|
||||
>
|
||||
MinIO Dashboard will display basic metrics as we couldn’t
|
||||
connect to Prometheus successfully. Please try again in a
|
||||
few minutes. If the problem persists, you can review your
|
||||
configuration and confirm that Prometheus server is up and
|
||||
running.
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
paddingTop: "20px",
|
||||
fontSize: "14px",
|
||||
"& a": {
|
||||
color: (theme) => theme.colors.link,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<a
|
||||
href="https://min.io/docs/minio/linux/operations/monitoring/collect-minio-metrics-using-prometheus.html"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Read more about Prometheus on our Docs site.
|
||||
</a>
|
||||
</Box>
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -70,7 +70,10 @@ const ServersList = ({ data }: { data: ServerInfo[] }) => {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
border: "1px solid #f1f1f1",
|
||||
borderTop: index === 0 ? "1px solid #f1f1f1" : "",
|
||||
borderBottom: "1px solid #f1f1f1",
|
||||
borderLeft: "1px solid #f1f1f1",
|
||||
borderRight: "1px solid #f1f1f1",
|
||||
padding: "3px 10px 3px 10px",
|
||||
|
||||
"&:hover": {
|
||||
|
||||
@@ -14,9 +14,7 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Fragment, useCallback, useEffect, useState } from "react";
|
||||
|
||||
import get from "lodash/get";
|
||||
import React, { Fragment, useEffect, useState } from "react";
|
||||
import PrDashboard from "./Prometheus/PrDashboard";
|
||||
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||
import Grid from "@mui/material/Grid";
|
||||
@@ -25,13 +23,9 @@ import { Theme } from "@mui/material/styles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
import withStyles from "@mui/styles/withStyles";
|
||||
import { LinearProgress } from "@mui/material";
|
||||
import api from "../../../common/api";
|
||||
import { Usage } from "./types";
|
||||
|
||||
import { ErrorResponseHandler } from "../../../common/types";
|
||||
import BasicDashboard from "./BasicDashboard/BasicDashboard";
|
||||
import { setErrorSnackMessage } from "../../../systemSlice";
|
||||
import { useAppDispatch } from "../../../store";
|
||||
import { AppState, useAppDispatch } from "../../../store";
|
||||
import { getUsageAsync } from "./dashboardThunks";
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
interface IDashboardSimple {
|
||||
classes: any;
|
||||
@@ -45,28 +39,15 @@ const styles = (theme: Theme) =>
|
||||
const Dashboard = ({ classes }: IDashboardSimple) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [basicResult, setBasicResult] = useState<Usage | null>(null);
|
||||
|
||||
const fetchUsage = useCallback(() => {
|
||||
api
|
||||
.invoke("GET", `/api/v1/admin/info`)
|
||||
.then((res: Usage) => {
|
||||
setBasicResult(res);
|
||||
setLoading(false);
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
dispatch(setErrorSnackMessage(err));
|
||||
setLoading(false);
|
||||
});
|
||||
}, [setBasicResult, setLoading, dispatch]);
|
||||
const usage = useSelector((state: AppState) => state.dashboard.usage);
|
||||
|
||||
useEffect(() => {
|
||||
if (loading) {
|
||||
fetchUsage();
|
||||
setLoading(false);
|
||||
dispatch(getUsageAsync());
|
||||
}
|
||||
}, [loading, fetchUsage]);
|
||||
|
||||
const widgets = get(basicResult, "widgets", null);
|
||||
}, [loading, dispatch]);
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
@@ -78,13 +59,7 @@ const Dashboard = ({ classes }: IDashboardSimple) => {
|
||||
</Grid>
|
||||
</Grid>
|
||||
) : (
|
||||
<Fragment>
|
||||
{widgets !== null ? (
|
||||
<PrDashboard />
|
||||
) : (
|
||||
<BasicDashboard usage={basicResult} />
|
||||
)}
|
||||
</Fragment>
|
||||
<PrDashboard usage={usage} />
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
@@ -14,22 +14,20 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Fragment, useCallback, useEffect, useState } from "react";
|
||||
import React, { Fragment, useState } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
import withStyles from "@mui/styles/withStyles";
|
||||
import { Box } from "@mui/material";
|
||||
import { Box, LinearProgress } from "@mui/material";
|
||||
import {
|
||||
actionsTray,
|
||||
widgetContainerCommon,
|
||||
} from "../../Common/FormComponents/common/styleLibrary";
|
||||
import { IDashboardPanel } from "./types";
|
||||
import { getWidgetsWithValue, panelsConfiguration } from "./utils";
|
||||
import { panelsConfiguration } from "./utils";
|
||||
import { TabPanel } from "../../../shared/tabs";
|
||||
import { ErrorResponseHandler } from "../../../../common/types";
|
||||
import api from "../../../../common/api";
|
||||
|
||||
import TabSelector from "../../Common/TabSelector/TabSelector";
|
||||
import { componentToUse } from "./widgetUtils";
|
||||
@@ -47,11 +45,20 @@ import {
|
||||
} from "./Widgets/LayoutUtil";
|
||||
import MergedWidgetsRenderer from "./Widgets/MergedWidgetsRenderer";
|
||||
import PageLayout from "../../Common/Layout/PageLayout";
|
||||
import { setErrorSnackMessage } from "../../../../systemSlice";
|
||||
import { Usage } from "../types";
|
||||
import BasicDashboard from "../BasicDashboard/BasicDashboard";
|
||||
import SyncIcon from "../../../../icons/SyncIcon";
|
||||
import { Button } from "mds";
|
||||
import { ITabOption } from "../../Common/TabSelector/types";
|
||||
import { getUsageAsync } from "../dashboardThunks";
|
||||
import { reloadWidgets } from "../dashboardSlice";
|
||||
import HelpBox from "../../../../common/HelpBox";
|
||||
import { PrometheusErrorIcon } from "../../../../icons";
|
||||
|
||||
interface IPrDashboard {
|
||||
classes?: any;
|
||||
apiPrefix?: string;
|
||||
usage: Usage | null;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
@@ -66,7 +73,7 @@ const styles = (theme: Theme) =>
|
||||
},
|
||||
});
|
||||
|
||||
const PrDashboard = ({ apiPrefix = "admin" }: IPrDashboard) => {
|
||||
const PrDashboard = ({ apiPrefix = "admin", usage }: IPrDashboard) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const zoomOpen = useSelector(
|
||||
(state: AppState) => state.dashboard.zoom.openZoom
|
||||
@@ -77,66 +84,17 @@ const PrDashboard = ({ apiPrefix = "admin" }: IPrDashboard) => {
|
||||
|
||||
const [timeStart, setTimeStart] = useState<any>(null);
|
||||
const [timeEnd, setTimeEnd] = useState<any>(null);
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [panelInformation, setPanelInformation] =
|
||||
useState<IDashboardPanel[]>(panelsConfiguration);
|
||||
const panelInformation = panelsConfiguration;
|
||||
const [curTab, setCurTab] = useState<number>(0);
|
||||
|
||||
const getPanelDetails = (id: number) => {
|
||||
return panelInformation.find((panel) => panel.id === id);
|
||||
};
|
||||
|
||||
const fetchUsage = useCallback(() => {
|
||||
let stepCalc = 0;
|
||||
|
||||
if (timeStart !== null && timeEnd !== null) {
|
||||
const secondsInPeriod = timeEnd.unix() - timeStart.unix();
|
||||
const periods = Math.floor(secondsInPeriod / 60);
|
||||
|
||||
stepCalc = periods < 1 ? 15 : periods;
|
||||
}
|
||||
|
||||
api
|
||||
.invoke(
|
||||
"GET",
|
||||
`/api/v1/${apiPrefix}/info?step=${stepCalc}&${
|
||||
timeStart !== null ? `&start=${timeStart.unix()}` : ""
|
||||
}${timeStart !== null && timeEnd !== null ? "&" : ""}${
|
||||
timeEnd !== null ? `end=${timeEnd.unix()}` : ""
|
||||
}`
|
||||
)
|
||||
.then((res: any) => {
|
||||
if (res.widgets) {
|
||||
const widgetsWithValue = getWidgetsWithValue(res.widgets);
|
||||
setPanelInformation(widgetsWithValue);
|
||||
} else {
|
||||
dispatch(
|
||||
setErrorSnackMessage({
|
||||
errorMessage:
|
||||
"Widget information could not be retrieved at this time. Please try again",
|
||||
detailedError: "",
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
setLoading(false);
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
dispatch(setErrorSnackMessage(err));
|
||||
setLoading(false);
|
||||
});
|
||||
}, [timeStart, timeEnd, dispatch, apiPrefix]);
|
||||
|
||||
const triggerLoad = () => {
|
||||
setLoading(true);
|
||||
dispatch(reloadWidgets());
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (loading) {
|
||||
fetchUsage();
|
||||
}
|
||||
}, [loading, fetchUsage]);
|
||||
|
||||
const renderCmpByConfig = (
|
||||
panelInfo: IDashboardPanel | undefined,
|
||||
key: string
|
||||
@@ -151,7 +109,7 @@ const PrDashboard = ({ apiPrefix = "admin" }: IPrDashboard) => {
|
||||
info={panelInfo}
|
||||
timeStart={timeStart}
|
||||
timeEnd={timeEnd}
|
||||
loading={loading}
|
||||
loading={true}
|
||||
apiPrefix={apiPrefix}
|
||||
/>
|
||||
) : (
|
||||
@@ -159,7 +117,7 @@ const PrDashboard = ({ apiPrefix = "admin" }: IPrDashboard) => {
|
||||
panelInfo,
|
||||
timeStart,
|
||||
timeEnd,
|
||||
loading,
|
||||
true,
|
||||
apiPrefix,
|
||||
zoomOpen
|
||||
)
|
||||
@@ -205,6 +163,23 @@ const PrDashboard = ({ apiPrefix = "admin" }: IPrDashboard) => {
|
||||
return renderPanelItems(resourcesPanelsLayoutAdvanced);
|
||||
};
|
||||
|
||||
let tabs: ITabOption[];
|
||||
if (usage?.advancedMetricsStatus !== "not configured") {
|
||||
tabs = [
|
||||
{ label: "Usage" },
|
||||
{ label: "Traffic" },
|
||||
{ label: "Resources" },
|
||||
{ label: "Info" },
|
||||
];
|
||||
} else {
|
||||
tabs = [
|
||||
{ label: "Info" },
|
||||
{ label: "Usage", disabled: true },
|
||||
{ label: "Traffic", disabled: true },
|
||||
{ label: "Resources", disabled: true },
|
||||
];
|
||||
}
|
||||
|
||||
return (
|
||||
<PageLayout>
|
||||
{zoomOpen && (
|
||||
@@ -224,11 +199,7 @@ const PrDashboard = ({ apiPrefix = "admin" }: IPrDashboard) => {
|
||||
onChange={(newValue: number) => {
|
||||
setCurTab(newValue);
|
||||
}}
|
||||
tabOptions={[
|
||||
{ label: "Usage" },
|
||||
{ label: "Traffic" },
|
||||
{ label: "Resources" },
|
||||
]}
|
||||
tabOptions={tabs}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid
|
||||
@@ -243,26 +214,113 @@ const PrDashboard = ({ apiPrefix = "admin" }: IPrDashboard) => {
|
||||
marginBottom: "20px",
|
||||
}}
|
||||
>
|
||||
<DateRangeSelector
|
||||
timeStart={timeStart}
|
||||
setTimeStart={setTimeStart}
|
||||
timeEnd={timeEnd}
|
||||
setTimeEnd={setTimeEnd}
|
||||
triggerSync={triggerLoad}
|
||||
/>
|
||||
{curTab ===
|
||||
(usage?.advancedMetricsStatus === "not configured" ? 0 : 3) ? (
|
||||
<Grid container>
|
||||
<Grid item>
|
||||
<Box
|
||||
sx={{
|
||||
color: "#000",
|
||||
fontSize: 18,
|
||||
lineHeight: 2,
|
||||
fontWeight: 700,
|
||||
marginLeft: "21px",
|
||||
display: "flex",
|
||||
}}
|
||||
>
|
||||
Server Information
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid item xs>
|
||||
<Grid container direction="row-reverse">
|
||||
<Grid item>
|
||||
<Button
|
||||
id={"sync"}
|
||||
type="button"
|
||||
variant="callAction"
|
||||
onClick={() => {
|
||||
dispatch(getUsageAsync());
|
||||
}}
|
||||
icon={<SyncIcon />}
|
||||
label={"Sync"}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
) : (
|
||||
<DateRangeSelector
|
||||
timeStart={timeStart}
|
||||
setTimeStart={setTimeStart}
|
||||
timeEnd={timeEnd}
|
||||
setTimeEnd={setTimeEnd}
|
||||
triggerSync={triggerLoad}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
<TabPanel index={0} value={curTab}>
|
||||
<TabPanel
|
||||
index={usage?.advancedMetricsStatus === "not configured" ? 3 : 0}
|
||||
value={curTab}
|
||||
>
|
||||
<RowPanelLayout>
|
||||
{usage?.advancedMetricsStatus === "unavailable" && (
|
||||
<HelpBox
|
||||
iconComponent={<PrometheusErrorIcon />}
|
||||
title={"We can’t retrieve advanced metrics at this time."}
|
||||
help={
|
||||
<Box
|
||||
sx={{
|
||||
fontSize: "14px",
|
||||
}}
|
||||
>
|
||||
It looks like Prometheus is not available or reachable at
|
||||
the moment.
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{panelInformation.length ? renderSummaryPanels() : null}
|
||||
</RowPanelLayout>
|
||||
</TabPanel>
|
||||
<TabPanel index={1} value={curTab}>
|
||||
<RowPanelLayout>
|
||||
{usage?.advancedMetricsStatus === "unavailable" && (
|
||||
<HelpBox
|
||||
iconComponent={<PrometheusErrorIcon />}
|
||||
title={"We can’t retrieve advanced metrics at this time."}
|
||||
help={
|
||||
<Box
|
||||
sx={{
|
||||
fontSize: "14px",
|
||||
}}
|
||||
>
|
||||
It looks like Prometheus is not available or reachable at
|
||||
the moment.
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{panelInformation.length ? renderTrafficPanels() : null}
|
||||
</RowPanelLayout>
|
||||
</TabPanel>
|
||||
<TabPanel index={2} value={curTab}>
|
||||
<RowPanelLayout>
|
||||
{usage?.advancedMetricsStatus === "unavailable" && (
|
||||
<HelpBox
|
||||
iconComponent={<PrometheusErrorIcon />}
|
||||
title={"We can’t retrieve advanced metrics at this time."}
|
||||
help={
|
||||
<Box
|
||||
sx={{
|
||||
fontSize: "14px",
|
||||
}}
|
||||
>
|
||||
It looks like Prometheus is not available or reachable at
|
||||
the moment.
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{panelInformation.length ? renderResourcesPanels() : null}
|
||||
<h2 style={{ margin: 0, borderBottom: "1px solid #dedede" }}>
|
||||
Advanced
|
||||
@@ -270,6 +328,13 @@ const PrDashboard = ({ apiPrefix = "admin" }: IPrDashboard) => {
|
||||
{panelInformation.length ? renderAdvancedResourcesPanels() : null}
|
||||
</RowPanelLayout>
|
||||
</TabPanel>
|
||||
<TabPanel
|
||||
index={usage?.advancedMetricsStatus === "not configured" ? 0 : 3}
|
||||
value={curTab}
|
||||
>
|
||||
{!usage && <LinearProgress />}
|
||||
{usage && <BasicDashboard usage={usage} />}
|
||||
</TabPanel>
|
||||
</Grid>
|
||||
</PageLayout>
|
||||
);
|
||||
|
||||
@@ -40,8 +40,9 @@ import { useTheme } from "@mui/styles";
|
||||
import Loader from "../../../Common/Loader/Loader";
|
||||
import ExpandGraphLink from "./ExpandGraphLink";
|
||||
import { setErrorSnackMessage } from "../../../../../systemSlice";
|
||||
import { useAppDispatch } from "../../../../../store";
|
||||
import { AppState, useAppDispatch } from "../../../../../store";
|
||||
import DownloadWidgetDataButton from "../../DownloadWidgetDataButton";
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
interface IBarChartWidget {
|
||||
classes: any;
|
||||
@@ -92,12 +93,14 @@ const BarChartWidget = ({
|
||||
zoomActivated = false,
|
||||
}: IBarChartWidget) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [data, setData] = useState<any>([]);
|
||||
const [result, setResult] = useState<IDashboardPanel | null>(null);
|
||||
const [hover, setHover] = useState<boolean>(false);
|
||||
const componentRef = useRef<HTMLElement>();
|
||||
|
||||
const widgetVersion = useSelector(
|
||||
(state: AppState) => state.dashboard.widgetLoadVersion
|
||||
);
|
||||
const onHover = () => {
|
||||
setHover(true);
|
||||
};
|
||||
@@ -106,10 +109,8 @@ const BarChartWidget = ({
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (propLoading) {
|
||||
setLoading(true);
|
||||
}
|
||||
}, [propLoading]);
|
||||
setLoading(true);
|
||||
}, [widgetVersion]);
|
||||
|
||||
useEffect(() => {
|
||||
if (loading) {
|
||||
|
||||
@@ -30,7 +30,8 @@ import { Cell, Pie, PieChart } from "recharts";
|
||||
import { ReportedUsageIcon } from "../../../../../icons";
|
||||
import Loader from "../../../Common/Loader/Loader";
|
||||
import { setErrorSnackMessage } from "../../../../../systemSlice";
|
||||
import { useAppDispatch } from "../../../../../store";
|
||||
import { AppState, useAppDispatch } from "../../../../../store";
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
const CapacityItem = ({
|
||||
value,
|
||||
@@ -46,18 +47,19 @@ const CapacityItem = ({
|
||||
apiPrefix: string;
|
||||
}) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
|
||||
const [totalUsableFree, setTotalUsableFree] = useState<number>(0);
|
||||
const [totalUsableFreeRatio, setTotalUsableFreeRatio] = useState<number>(0);
|
||||
const [totalUsed, setTotalUsed] = useState<number>(0);
|
||||
const [totalUsable, setTotalUsable] = useState<number>(0);
|
||||
const widgetVersion = useSelector(
|
||||
(state: AppState) => state.dashboard.widgetLoadVersion
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (propLoading) {
|
||||
setLoading(true);
|
||||
}
|
||||
}, [propLoading]);
|
||||
setLoading(true);
|
||||
}, [widgetVersion]);
|
||||
|
||||
useEffect(() => {
|
||||
if (loading) {
|
||||
|
||||
@@ -23,7 +23,8 @@ import { IDashboardPanel } from "../types";
|
||||
import Loader from "../../../Common/Loader/Loader";
|
||||
|
||||
import { setErrorSnackMessage } from "../../../../../systemSlice";
|
||||
import { useAppDispatch } from "../../../../../store";
|
||||
import { AppState, useAppDispatch } from "../../../../../store";
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
const EntityStateStatItem = ({
|
||||
panelItem,
|
||||
@@ -41,14 +42,15 @@ const EntityStateStatItem = ({
|
||||
statLabel: any;
|
||||
}) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [data, setData] = useState<string>("");
|
||||
const widgetVersion = useSelector(
|
||||
(state: AppState) => state.dashboard.widgetLoadVersion
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (propLoading) {
|
||||
setLoading(true);
|
||||
}
|
||||
}, [propLoading]);
|
||||
setLoading(true);
|
||||
}, [widgetVersion]);
|
||||
|
||||
useEffect(() => {
|
||||
if (loading) {
|
||||
|
||||
@@ -39,8 +39,9 @@ import { useTheme } from "@mui/styles";
|
||||
import Loader from "../../../Common/Loader/Loader";
|
||||
import ExpandGraphLink from "./ExpandGraphLink";
|
||||
import { setErrorSnackMessage } from "../../../../../systemSlice";
|
||||
import { useAppDispatch } from "../../../../../store";
|
||||
import { AppState, useAppDispatch } from "../../../../../store";
|
||||
import DownloadWidgetDataButton from "../../DownloadWidgetDataButton";
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
interface ILinearGraphWidget {
|
||||
classes: any;
|
||||
@@ -106,20 +107,21 @@ const LinearGraphWidget = ({
|
||||
zoomActivated = false,
|
||||
}: ILinearGraphWidget) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [hover, setHover] = useState<boolean>(false);
|
||||
const [data, setData] = useState<object[]>([]);
|
||||
const [csvData, setCsvData] = useState<object[]>([]);
|
||||
const [dataMax, setDataMax] = useState<number>(0);
|
||||
const [result, setResult] = useState<IDashboardPanel | null>(null);
|
||||
const widgetVersion = useSelector(
|
||||
(state: AppState) => state.dashboard.widgetLoadVersion
|
||||
);
|
||||
|
||||
const componentRef = useRef<HTMLElement>();
|
||||
|
||||
useEffect(() => {
|
||||
if (propLoading) {
|
||||
setLoading(true);
|
||||
}
|
||||
}, [propLoading]);
|
||||
setLoading(true);
|
||||
}, [widgetVersion]);
|
||||
|
||||
useEffect(() => {
|
||||
if (loading) {
|
||||
|
||||
@@ -30,7 +30,8 @@ import get from "lodash/get";
|
||||
import api from "../../../../../common/api";
|
||||
import Loader from "../../../Common/Loader/Loader";
|
||||
import { setErrorSnackMessage } from "../../../../../systemSlice";
|
||||
import { useAppDispatch } from "../../../../../store";
|
||||
import { AppState, useAppDispatch } from "../../../../../store";
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
interface IPieChartWidget {
|
||||
classes: any;
|
||||
@@ -79,16 +80,17 @@ const PieChartWidget = ({
|
||||
apiPrefix,
|
||||
}: IPieChartWidget) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [dataInner, setDataInner] = useState<object[]>([]);
|
||||
const [dataOuter, setDataOuter] = useState<object[]>([]);
|
||||
const [result, setResult] = useState<IDashboardPanel | null>(null);
|
||||
const widgetVersion = useSelector(
|
||||
(state: AppState) => state.dashboard.widgetLoadVersion
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (propLoading) {
|
||||
setLoading(true);
|
||||
}
|
||||
}, [propLoading]);
|
||||
setLoading(true);
|
||||
}, [widgetVersion]);
|
||||
|
||||
useEffect(() => {
|
||||
if (loading) {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Fragment, useEffect, useState } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { connect, useSelector } from "react-redux";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
import withStyles from "@mui/styles/withStyles";
|
||||
@@ -26,7 +26,7 @@ import { IDashboardPanel } from "../types";
|
||||
import { ErrorResponseHandler } from "../../../../../common/types";
|
||||
import Loader from "../../../Common/Loader/Loader";
|
||||
import { setErrorSnackMessage } from "../../../../../systemSlice";
|
||||
import { useAppDispatch } from "../../../../../store";
|
||||
import { AppState, useAppDispatch } from "../../../../../store";
|
||||
|
||||
interface ISimpleWidget {
|
||||
classes: any;
|
||||
@@ -76,14 +76,15 @@ const SimpleWidget = ({
|
||||
renderFn,
|
||||
}: ISimpleWidget) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [data, setData] = useState<string>("");
|
||||
const widgetVersion = useSelector(
|
||||
(state: AppState) => state.dashboard.widgetLoadVersion
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (propLoading) {
|
||||
setLoading(true);
|
||||
}
|
||||
}, [propLoading]);
|
||||
setLoading(true);
|
||||
}, [widgetVersion]);
|
||||
|
||||
useEffect(() => {
|
||||
if (loading) {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { connect, useSelector } from "react-redux";
|
||||
import { IDashboardPanel } from "../types";
|
||||
import { widgetDetailsToPanel } from "../utils";
|
||||
import { ErrorResponseHandler } from "../../../../../common/types";
|
||||
@@ -25,7 +25,7 @@ import DashboardItemBox from "../../DashboardItemBox";
|
||||
import BucketsCountItem from "./BucketsCountItem";
|
||||
import ObjectsCountItem from "./ObjectsCountItem";
|
||||
import { setErrorSnackMessage } from "../../../../../systemSlice";
|
||||
import { useAppDispatch } from "../../../../../store";
|
||||
import { AppState, useAppDispatch } from "../../../../../store";
|
||||
|
||||
interface ISingleRepWidget {
|
||||
title: string;
|
||||
@@ -49,14 +49,15 @@ const SingleRepWidget = ({
|
||||
apiPrefix,
|
||||
}: ISingleRepWidget) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [result, setResult] = useState<IDashboardPanel | null>(null);
|
||||
const widgetVersion = useSelector(
|
||||
(state: AppState) => state.dashboard.widgetLoadVersion
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (propLoading) {
|
||||
setLoading(true);
|
||||
}
|
||||
}, [propLoading]);
|
||||
setLoading(true);
|
||||
}, [widgetVersion]);
|
||||
|
||||
useEffect(() => {
|
||||
if (loading) {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Fragment, useEffect, useState } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { connect, useSelector } from "react-redux";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
import withStyles from "@mui/styles/withStyles";
|
||||
@@ -26,7 +26,7 @@ import { splitSizeMetric, widgetDetailsToPanel } from "../utils";
|
||||
import { IDashboardPanel } from "../types";
|
||||
import { ErrorResponseHandler } from "../../../../../common/types";
|
||||
import { setErrorSnackMessage } from "../../../../../systemSlice";
|
||||
import { useAppDispatch } from "../../../../../store";
|
||||
import { AppState, useAppDispatch } from "../../../../../store";
|
||||
|
||||
interface ISingleValueWidget {
|
||||
title: string;
|
||||
@@ -82,14 +82,15 @@ const SingleValueWidget = ({
|
||||
renderFn,
|
||||
}: ISingleValueWidget) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [data, setData] = useState<string>("");
|
||||
const widgetVersion = useSelector(
|
||||
(state: AppState) => state.dashboard.widgetLoadVersion
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (propLoading) {
|
||||
setLoading(true);
|
||||
}
|
||||
}, [propLoading]);
|
||||
setLoading(true);
|
||||
}, [widgetVersion]);
|
||||
|
||||
useEffect(() => {
|
||||
if (loading) {
|
||||
|
||||
@@ -15,11 +15,15 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||
import { zoomState } from "./types";
|
||||
import { Usage, zoomState } from "./types";
|
||||
import { IDashboardPanel } from "./Prometheus/types";
|
||||
import { getUsageAsync } from "./dashboardThunks";
|
||||
|
||||
export interface DashboardState {
|
||||
zoom: zoomState;
|
||||
usage: Usage | null;
|
||||
loadingUsage: boolean;
|
||||
widgetLoadVersion: number;
|
||||
}
|
||||
|
||||
const initialState: DashboardState = {
|
||||
@@ -27,6 +31,9 @@ const initialState: DashboardState = {
|
||||
openZoom: false,
|
||||
widgetRender: null,
|
||||
},
|
||||
usage: null,
|
||||
loadingUsage: true,
|
||||
widgetLoadVersion: 0,
|
||||
};
|
||||
export const dashboardSlice = createSlice({
|
||||
name: "dashboard",
|
||||
@@ -40,8 +47,25 @@ export const dashboardSlice = createSlice({
|
||||
state.zoom.openZoom = false;
|
||||
state.zoom.widgetRender = null;
|
||||
},
|
||||
reloadWidgets: (state) => {
|
||||
state.widgetLoadVersion++;
|
||||
},
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
builder
|
||||
.addCase(getUsageAsync.pending, (state) => {
|
||||
state.loadingUsage = true;
|
||||
})
|
||||
.addCase(getUsageAsync.rejected, (state) => {
|
||||
state.loadingUsage = false;
|
||||
})
|
||||
.addCase(getUsageAsync.fulfilled, (state, action) => {
|
||||
state.loadingUsage = false;
|
||||
state.usage = action.payload;
|
||||
});
|
||||
},
|
||||
});
|
||||
export const { openZoomPage, closeZoomPage } = dashboardSlice.actions;
|
||||
export const { openZoomPage, closeZoomPage, reloadWidgets } =
|
||||
dashboardSlice.actions;
|
||||
|
||||
export default dashboardSlice.reducer;
|
||||
|
||||
36
portal-ui/src/screens/Console/Dashboard/dashboardThunks.ts
Normal file
36
portal-ui/src/screens/Console/Dashboard/dashboardThunks.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { createAsyncThunk } from "@reduxjs/toolkit";
|
||||
import api from "../../../common/api";
|
||||
import { ErrorResponseHandler } from "../../../common/types";
|
||||
import { setErrorSnackMessage } from "../../../systemSlice";
|
||||
import { Usage } from "./types";
|
||||
|
||||
export const getUsageAsync = createAsyncThunk(
|
||||
"dashboard/getUsageAsync",
|
||||
async (_, { getState, rejectWithValue, dispatch }) => {
|
||||
return api
|
||||
.invoke("GET", `/api/v1/admin/info`)
|
||||
.then((res: Usage) => {
|
||||
return res;
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
dispatch(setErrorSnackMessage(err));
|
||||
return rejectWithValue(err);
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -20,7 +20,7 @@ export interface Usage {
|
||||
usage: number;
|
||||
buckets: number;
|
||||
objects: number;
|
||||
prometheusNotReady?: boolean;
|
||||
advancedMetricsStatus: "available" | "unavailable" | "not configured";
|
||||
widgets?: any;
|
||||
servers: ServerInfo[];
|
||||
//TODO
|
||||
|
||||
@@ -886,53 +886,62 @@ func getAdminInfoResponse(session *models.Principal, params systemApi.AdminInfoP
|
||||
}
|
||||
|
||||
func getUsageWidgetsForDeployment(ctx context.Context, prometheusURL string, mAdmin *madmin.AdminClient) (*models.AdminInfoResponse, error) {
|
||||
prometheusNotReady := false
|
||||
if prometheusURL != "" && !testPrometheusURL(ctx, prometheusURL) {
|
||||
prometheusNotReady = true
|
||||
prometheusStatus := models.AdminInfoResponseAdvancedMetricsStatusAvailable
|
||||
if prometheusURL == "" {
|
||||
prometheusStatus = models.AdminInfoResponseAdvancedMetricsStatusNotConfigured
|
||||
}
|
||||
if prometheusURL == "" || prometheusNotReady {
|
||||
if prometheusURL != "" && !testPrometheusURL(ctx, prometheusURL) {
|
||||
prometheusStatus = models.AdminInfoResponseAdvancedMetricsStatusUnavailable
|
||||
}
|
||||
|
||||
sessionResp := &models.AdminInfoResponse{
|
||||
AdvancedMetricsStatus: prometheusStatus,
|
||||
}
|
||||
doneCh := make(chan error)
|
||||
go func() {
|
||||
defer close(doneCh)
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
// serialize output
|
||||
usage, err := GetAdminInfo(ctx, adminClient)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
doneCh <- err
|
||||
}
|
||||
sessionResp := &models.AdminInfoResponse{
|
||||
Buckets: usage.Buckets,
|
||||
Objects: usage.Objects,
|
||||
Usage: usage.Usage,
|
||||
Servers: usage.Servers,
|
||||
PrometheusNotReady: prometheusNotReady,
|
||||
}
|
||||
return sessionResp, nil
|
||||
}
|
||||
sessionResp.Buckets = usage.Buckets
|
||||
sessionResp.Objects = usage.Objects
|
||||
sessionResp.Usage = usage.Usage
|
||||
sessionResp.Servers = usage.Servers
|
||||
}()
|
||||
|
||||
var wdgts []*models.Widget
|
||||
|
||||
for _, m := range widgets {
|
||||
// for each target we will launch another goroutine to fetch the values
|
||||
wdgtResult := models.Widget{
|
||||
ID: m.ID,
|
||||
Title: m.Title,
|
||||
Type: m.Type,
|
||||
}
|
||||
if len(m.Options.ReduceOptions.Calcs) > 0 {
|
||||
wdgtResult.Options = &models.WidgetOptions{
|
||||
ReduceOptions: &models.WidgetOptionsReduceOptions{
|
||||
Calcs: m.Options.ReduceOptions.Calcs,
|
||||
},
|
||||
if prometheusStatus == models.AdminInfoResponseAdvancedMetricsStatusAvailable {
|
||||
// We will tell the frontend about a list of widgets so it can fetch the ones it wants
|
||||
for _, m := range widgets {
|
||||
wdgtResult := models.Widget{
|
||||
ID: m.ID,
|
||||
Title: m.Title,
|
||||
Type: m.Type,
|
||||
}
|
||||
if len(m.Options.ReduceOptions.Calcs) > 0 {
|
||||
wdgtResult.Options = &models.WidgetOptions{
|
||||
ReduceOptions: &models.WidgetOptionsReduceOptions{
|
||||
Calcs: m.Options.ReduceOptions.Calcs,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wdgts = append(wdgts, &wdgtResult)
|
||||
wdgts = append(wdgts, &wdgtResult)
|
||||
}
|
||||
sessionResp.Widgets = wdgts
|
||||
}
|
||||
|
||||
// count the number of widgets that have completed calculating
|
||||
sessionResp := &models.AdminInfoResponse{}
|
||||
// wait for mc admin info
|
||||
err := <-doneCh
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sessionResp.Widgets = wdgts
|
||||
return sessionResp, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -5052,15 +5052,20 @@ func init() {
|
||||
"adminInfoResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"advancedMetricsStatus": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"not configured",
|
||||
"available",
|
||||
"unavailable"
|
||||
]
|
||||
},
|
||||
"buckets": {
|
||||
"type": "integer"
|
||||
},
|
||||
"objects": {
|
||||
"type": "integer"
|
||||
},
|
||||
"prometheusNotReady": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"servers": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@@ -13125,15 +13130,20 @@ func init() {
|
||||
"adminInfoResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"advancedMetricsStatus": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"not configured",
|
||||
"available",
|
||||
"unavailable"
|
||||
]
|
||||
},
|
||||
"buckets": {
|
||||
"type": "integer"
|
||||
},
|
||||
"objects": {
|
||||
"type": "integer"
|
||||
},
|
||||
"prometheusNotReady": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"servers": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
|
||||
@@ -4191,8 +4191,12 @@ definitions:
|
||||
type: integer
|
||||
usage:
|
||||
type: integer
|
||||
prometheusNotReady:
|
||||
type: boolean
|
||||
advancedMetricsStatus:
|
||||
type: string
|
||||
enum:
|
||||
- not configured
|
||||
- available
|
||||
- unavailable
|
||||
widgets:
|
||||
type: array
|
||||
items:
|
||||
@@ -5091,7 +5095,7 @@ definitions:
|
||||
$ref: "#/definitions/tier_azure"
|
||||
minio:
|
||||
type: object
|
||||
$ref: "#/definitions/tier_minio"
|
||||
$ref: "#/definitions/tier_minio"
|
||||
|
||||
tierListResponse:
|
||||
type: object
|
||||
|
||||
Reference in New Issue
Block a user