diff --git a/models/admin_info_response.go b/models/admin_info_response.go
index 17922e07c..3bd6f5e38 100644
--- a/models/admin_info_response.go
+++ b/models/admin_info_response.go
@@ -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
diff --git a/portal-ui/src/screens/Console/Common/FormComponents/DateRangeSelector/DateRangeSelector.tsx b/portal-ui/src/screens/Console/Common/FormComponents/DateRangeSelector/DateRangeSelector.tsx
index b8cf2d4c3..5280e48e8 100644
--- a/portal-ui/src/screens/Console/Common/FormComponents/DateRangeSelector/DateRangeSelector.tsx
+++ b/portal-ui/src/screens/Console/Common/FormComponents/DateRangeSelector/DateRangeSelector.tsx
@@ -264,7 +264,6 @@ const DateRangeSelector = ({
alignItems: "flex-end",
display: "flex",
justifyContent: "flex-end",
- marginRight: "35px",
}}
>
.
-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 (
-
+
{
gridTemplateColumns: "1fr",
gap: "27px",
marginBottom: "40px",
- marginTop: "40px",
- marginLeft: "40px",
- marginRight: "40px",
}}
>
-
- {usage?.prometheusNotReady && (
- }
- title={"We can't retrieve advanced metrics at this time"}
- help={
-
- 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.
-
- }
- />
- )}
-
- {!usage?.prometheusNotReady && (
- }
- title={"We can’t retrieve advanced metrics at this time."}
- help={
-
-
- 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.
-
- theme.colors.link,
- },
- }}
- >
-
- Read more about Prometheus on our Docs site.
-
-
-
- }
- />
- )}
-
-
{
+ {usage?.advancedMetricsStatus === "not configured" && (
+
+ }
+ title={"We can’t retrieve advanced metrics at this time."}
+ help={
+
+
+ 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.
+
+ theme.colors.link,
+ },
+ }}
+ >
+
+ Read more about Prometheus on our Docs site.
+
+
+
+ }
+ />
+
+ )}
);
diff --git a/portal-ui/src/screens/Console/Dashboard/BasicDashboard/ServersList.tsx b/portal-ui/src/screens/Console/Dashboard/BasicDashboard/ServersList.tsx
index b63cca2ad..5677d43da 100644
--- a/portal-ui/src/screens/Console/Dashboard/BasicDashboard/ServersList.tsx
+++ b/portal-ui/src/screens/Console/Dashboard/BasicDashboard/ServersList.tsx
@@ -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": {
diff --git a/portal-ui/src/screens/Console/Dashboard/Dashboard.tsx b/portal-ui/src/screens/Console/Dashboard/Dashboard.tsx
index 8d90de65e..46e06101e 100644
--- a/portal-ui/src/screens/Console/Dashboard/Dashboard.tsx
+++ b/portal-ui/src/screens/Console/Dashboard/Dashboard.tsx
@@ -14,9 +14,7 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
-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(true);
- const [basicResult, setBasicResult] = useState(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 (
@@ -78,13 +59,7 @@ const Dashboard = ({ classes }: IDashboardSimple) => {
) : (
-
- {widgets !== null ? (
-
- ) : (
-
- )}
-
+
)}
);
diff --git a/portal-ui/src/screens/Console/Dashboard/Prometheus/PrDashboard.tsx b/portal-ui/src/screens/Console/Dashboard/Prometheus/PrDashboard.tsx
index f90897034..365fd6f62 100644
--- a/portal-ui/src/screens/Console/Dashboard/Prometheus/PrDashboard.tsx
+++ b/portal-ui/src/screens/Console/Dashboard/Prometheus/PrDashboard.tsx
@@ -14,22 +14,20 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
-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(null);
const [timeEnd, setTimeEnd] = useState(null);
- const [loading, setLoading] = useState(true);
- const [panelInformation, setPanelInformation] =
- useState(panelsConfiguration);
+ const panelInformation = panelsConfiguration;
const [curTab, setCurTab] = useState(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 (
{zoomOpen && (
@@ -224,11 +199,7 @@ const PrDashboard = ({ apiPrefix = "admin" }: IPrDashboard) => {
onChange={(newValue: number) => {
setCurTab(newValue);
}}
- tabOptions={[
- { label: "Usage" },
- { label: "Traffic" },
- { label: "Resources" },
- ]}
+ tabOptions={tabs}
/>
{
marginBottom: "20px",
}}
>
-
+ {curTab ===
+ (usage?.advancedMetricsStatus === "not configured" ? 0 : 3) ? (
+
+
+
+ Server Information
+
+
+
+
+
+
+
+
+
+ ) : (
+
+ )}
-
+
+ {usage?.advancedMetricsStatus === "unavailable" && (
+ }
+ title={"We can’t retrieve advanced metrics at this time."}
+ help={
+
+ It looks like Prometheus is not available or reachable at
+ the moment.
+
+ }
+ />
+ )}
{panelInformation.length ? renderSummaryPanels() : null}
+ {usage?.advancedMetricsStatus === "unavailable" && (
+ }
+ title={"We can’t retrieve advanced metrics at this time."}
+ help={
+
+ It looks like Prometheus is not available or reachable at
+ the moment.
+
+ }
+ />
+ )}
{panelInformation.length ? renderTrafficPanels() : null}
+ {usage?.advancedMetricsStatus === "unavailable" && (
+ }
+ title={"We can’t retrieve advanced metrics at this time."}
+ help={
+
+ It looks like Prometheus is not available or reachable at
+ the moment.
+
+ }
+ />
+ )}
{panelInformation.length ? renderResourcesPanels() : null}
Advanced
@@ -270,6 +328,13 @@ const PrDashboard = ({ apiPrefix = "admin" }: IPrDashboard) => {
{panelInformation.length ? renderAdvancedResourcesPanels() : null}
+
+ {!usage && }
+ {usage && }
+
);
diff --git a/portal-ui/src/screens/Console/Dashboard/Prometheus/Widgets/BarChartWidget.tsx b/portal-ui/src/screens/Console/Dashboard/Prometheus/Widgets/BarChartWidget.tsx
index 8b4549306..ea7667120 100644
--- a/portal-ui/src/screens/Console/Dashboard/Prometheus/Widgets/BarChartWidget.tsx
+++ b/portal-ui/src/screens/Console/Dashboard/Prometheus/Widgets/BarChartWidget.tsx
@@ -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(true);
+ const [loading, setLoading] = useState(false);
const [data, setData] = useState([]);
const [result, setResult] = useState(null);
const [hover, setHover] = useState(false);
const componentRef = useRef();
-
+ 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) {
diff --git a/portal-ui/src/screens/Console/Dashboard/Prometheus/Widgets/CapacityItem.tsx b/portal-ui/src/screens/Console/Dashboard/Prometheus/Widgets/CapacityItem.tsx
index 4a1bacc08..53e9590f8 100644
--- a/portal-ui/src/screens/Console/Dashboard/Prometheus/Widgets/CapacityItem.tsx
+++ b/portal-ui/src/screens/Console/Dashboard/Prometheus/Widgets/CapacityItem.tsx
@@ -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(true);
+ const [loading, setLoading] = useState(false);
const [totalUsableFree, setTotalUsableFree] = useState(0);
const [totalUsableFreeRatio, setTotalUsableFreeRatio] = useState(0);
const [totalUsed, setTotalUsed] = useState(0);
const [totalUsable, setTotalUsable] = useState(0);
+ const widgetVersion = useSelector(
+ (state: AppState) => state.dashboard.widgetLoadVersion
+ );
useEffect(() => {
- if (propLoading) {
- setLoading(true);
- }
- }, [propLoading]);
+ setLoading(true);
+ }, [widgetVersion]);
useEffect(() => {
if (loading) {
diff --git a/portal-ui/src/screens/Console/Dashboard/Prometheus/Widgets/EntityStateStatItem.tsx b/portal-ui/src/screens/Console/Dashboard/Prometheus/Widgets/EntityStateStatItem.tsx
index 9b078e33b..fc380a102 100644
--- a/portal-ui/src/screens/Console/Dashboard/Prometheus/Widgets/EntityStateStatItem.tsx
+++ b/portal-ui/src/screens/Console/Dashboard/Prometheus/Widgets/EntityStateStatItem.tsx
@@ -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(true);
+ const [loading, setLoading] = useState(false);
const [data, setData] = useState("");
+ const widgetVersion = useSelector(
+ (state: AppState) => state.dashboard.widgetLoadVersion
+ );
useEffect(() => {
- if (propLoading) {
- setLoading(true);
- }
- }, [propLoading]);
+ setLoading(true);
+ }, [widgetVersion]);
useEffect(() => {
if (loading) {
diff --git a/portal-ui/src/screens/Console/Dashboard/Prometheus/Widgets/LinearGraphWidget.tsx b/portal-ui/src/screens/Console/Dashboard/Prometheus/Widgets/LinearGraphWidget.tsx
index a167e9613..a2ebc56a0 100644
--- a/portal-ui/src/screens/Console/Dashboard/Prometheus/Widgets/LinearGraphWidget.tsx
+++ b/portal-ui/src/screens/Console/Dashboard/Prometheus/Widgets/LinearGraphWidget.tsx
@@ -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(true);
+ const [loading, setLoading] = useState(false);
const [hover, setHover] = useState(false);
const [data, setData] = useState