This is an automated email from the ASF dual-hosted git repository.
arafat2198 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git
The following commit(s) were added to refs/heads/master by this push:
new 0c924bcc6a HDDS-11150. Recon Overview page crashes due to failed API
Calls (#6944)
0c924bcc6a is described below
commit 0c924bcc6a74aa8923582ad6c6a535af4894d662
Author: Abhishek Pal <[email protected]>
AuthorDate: Fri Jul 19 11:12:36 2024 +0530
HDDS-11150. Recon Overview page crashes due to failed API Calls (#6944)
---
.../src/components/overviewCard/overviewCard.tsx | 10 +-
.../src/utils/axiosRequestHelper.tsx | 6 +-
.../src/views/diskUsage/diskUsage.tsx | 6 -
.../src/views/insights/insights.tsx | 149 +++++++++++--
.../src/views/overview/overview.tsx | 247 +++++++++++++++------
5 files changed, 320 insertions(+), 98 deletions(-)
diff --git
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/components/overviewCard/overviewCard.tsx
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/components/overviewCard/overviewCard.tsx
index 93c0cae1f1..78ba56e548 100644
---
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/components/overviewCard/overviewCard.tsx
+++
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/components/overviewCard/overviewCard.tsx
@@ -139,8 +139,14 @@ class OverviewCard extends
React.Component<IOverviewCardProps> {
render() {
let { icon, data, title, loading, hoverable, storageReport, linkToUrl,
error } = this.props;
+
let meta = <Meta title={data} description={title} />;
- const errorClass = error ? 'card-error' : '';
+ let errorClass = error ? 'card-error' : '';
+
+ if (typeof data === 'string' && data === 'N/A'){
+ errorClass = 'card-error';
+ }
+
if (storageReport) {
meta = (
<div className='ant-card-percentage'>
@@ -156,7 +162,7 @@ class OverviewCard extends
React.Component<IOverviewCardProps> {
return (
<OverviewCardWrapper linkToUrl={linkToUrl}>
- <Card className={`overview-card ${ errorClass }`} loading={loading}
hoverable={hoverable}>
+ <Card className={`overview-card ${errorClass}`} loading={loading}
hoverable={hoverable}>
<Row type='flex' justify='space-between'>
<Col span={18}>
<Row>
diff --git
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/utils/axiosRequestHelper.tsx
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/utils/axiosRequestHelper.tsx
index 45fa3b58b6..41774088c5 100644
---
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/utils/axiosRequestHelper.tsx
+++
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/utils/axiosRequestHelper.tsx
@@ -48,11 +48,11 @@ export const AxiosPutHelper = (
}
}
-export const AxiosAllGetHelper = (
+export const PromiseAllSettledGetHelper = (
urls: string[],
controller: AbortController,
message: string = ''
-): { requests: Promise<AxiosResponse<any, any>[]>; controller: AbortController
} => {
+): { requests: Promise<PromiseSettledResult<AxiosResponse<any, any>>[]>;
controller: AbortController } => {
controller && controller.abort(message);
controller = new AbortController(); // generate new AbortController for the
upcoming request
@@ -64,7 +64,7 @@ export const AxiosAllGetHelper = (
});
return {
- requests: axios.all(axiosGetRequests),
+ requests: Promise.allSettled(axiosGetRequests),
controller: controller
}
}
diff --git
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/diskUsage/diskUsage.tsx
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/diskUsage/diskUsage.tsx
index 60b779bdf0..091d8a146d 100644
---
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/diskUsage/diskUsage.tsx
+++
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/diskUsage/diskUsage.tsx
@@ -556,12 +556,6 @@ export class DiskUsage extends
React.Component<Record<string, object>, IDUState>
</Menu>
)
- console.log(plotData);
- console.log(plotData.map((value) => {
- return {
- name: value.name
- }
- }))
const eChartsOptions = {
title: {
text: `Disk Usage for ${returnPath} (Total Size:
${byteToSize(duResponse.size, 1)})`,
diff --git
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/insights/insights.tsx
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/insights/insights.tsx
index 2412febdee..f273f758ea 100644
---
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/insights/insights.tsx
+++
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/insights/insights.tsx
@@ -17,17 +17,17 @@
*/
import React from 'react';
-import axios from 'axios';
+import axios, { CanceledError, AxiosError } from 'axios';
import filesize from 'filesize';
-import { Row, Col, Tabs } from 'antd';
+import { Row, Col, Tabs, message } from 'antd';
import { LoadingOutlined } from '@ant-design/icons';
import { ActionMeta, ValueType } from 'react-select';
-import { format, type EChartsOption } from 'echarts';
+import { graphic, type EChartsOption } from 'echarts';
import { EChart } from '@/components/eChart/eChart';
import { MultiSelect, IOption } from '@/components/multiSelect/multiSelect';
import { showDataFetchError } from '@/utils/common';
-import { AxiosAllGetHelper } from '@/utils/axiosRequestHelper';
+import { PromiseAllSettledGetHelper, PromiseAllSettledError } from
'@/utils/axiosRequestHelper';
import './insights.less';
@@ -58,6 +58,8 @@ interface IInsightsState {
bucketOptions: IOption[];
volumeOptions: IOption[];
isBucketSelectionDisabled: boolean;
+ fileCountError: string | undefined;
+ containerSizeError: string | undefined;
}
const allVolumesOption: IOption = {
@@ -86,7 +88,9 @@ export class Insights extends React.Component<Record<string,
object>, IInsightsS
selectedVolumes: [],
bucketOptions: [],
volumeOptions: [],
- isBucketSelectionDisabled: false
+ isBucketSelectionDisabled: false,
+ fileCountError: undefined,
+ containerSizeError: undefined
};
}
@@ -195,9 +199,81 @@ export class Insights extends
React.Component<Record<string, object>, IInsightsS
return (size(value));
});
- console.log(xyFileCountMap);
- console.log(xContainerCountValues);
- console.log(xyContainerCountMap);
+ let renderFileCountError = (this.state.fileCountError) ? {
+ type: 'group',
+ left: 'center',
+ top: 'middle',
+ z: 100,
+ children: [
+ {
+ type: 'rect',
+ left: 'center',
+ top: 'middle',
+ z: 100,
+ shape: {
+ width: 500,
+ height: 40
+ },
+ style: {
+ fill: '#FC909B'
+ }
+ },
+ {
+ type: 'text',
+ left: 'center',
+ top: 'middle',
+ z: 100,
+ style: {
+ text: `No data available. ${this.state.fileCountError}`,
+ font: '20px sans-serif'
+ }
+ }
+ ]
+ } : undefined
+ let renderContainerSizeError = (this.state.containerSizeError) ? {
+ type: 'group',
+ left: 'center',
+ top: 'middle',
+ z: 100,
+ children: [
+ {
+ type: 'rect',
+ left: 'center',
+ top: 'middle',
+ z: 100,
+ shape: {
+ width: 500,
+ height: 500
+ },
+ style: {
+ fill: 'rgba(256, 256, 256, 0.5)'
+ }
+ },
+ {
+ type: 'rect',
+ left: 'center',
+ top: 'middle',
+ z: 100,
+ shape: {
+ width: 500,
+ height: 40
+ },
+ style: {
+ fill: '#FC909B'
+ }
+ },
+ {
+ type: 'text',
+ left: 'center',
+ top: 'middle',
+ z: 100,
+ style: {
+ text: `No data available. ${this.state.containerSizeError}`,
+ font: '20px sans-serif'
+ }
+ }
+ ]
+ } : undefined
this.setState({
fileCountData: {
@@ -224,7 +300,8 @@ export class Insights extends
React.Component<Record<string, object>, IInsightsS
},
data: Array.from(xyFileCountMap.values()),
type: 'bar'
- }
+ },
+ graphic: renderFileCountError
},
containerCountData: {
title: {
@@ -250,7 +327,8 @@ export class Insights extends
React.Component<Record<string, object>, IInsightsS
name: xContainerCountValues[idx]
}
}),
- }
+ },
+ graphic: renderContainerSizeError
}
});
}
@@ -261,15 +339,50 @@ export class Insights extends
React.Component<Record<string, object>, IInsightsS
this.setState({
isLoading: true
});
- const { requests, controller } = AxiosAllGetHelper([
+ const { requests, controller } = PromiseAllSettledGetHelper([
'/api/v1/utilization/fileCount',
'/api/v1/utilization/containerCount'
], cancelInsightSignal);
cancelInsightSignal = controller;
- requests.then(axios.spread((fileCountresponse, containerCountresponse) => {
- const fileCountsResponse: IFileCountResponse[] = fileCountresponse.data;
- const containerCountResponse: IContainerCountResponse[] =
containerCountresponse.data;
+ requests.then(axios.spread((
+ fileCountresponse: Awaited<Promise<any>>,
+ containerCountresponse: Awaited<Promise<any>>
+ ) => {
+ let fileAPIError = undefined;
+ let containerAPIError = undefined;
+ let responseError = [
+ fileCountresponse,
+ containerCountresponse
+ ].filter((resp) => resp.status === 'rejected');
+
+ if (responseError.length !== 0) {
+ responseError.forEach((err) => {
+ if (err.reason.toString().includes("CanceledError")) {
+ throw new CanceledError('canceled', "ERR_CANCELED");
+ }
+ else {
+ if (err.reason.config.url.includes("fileCount")) {
+ fileAPIError = err.reason.toString();
+ }
+ else {
+ containerAPIError = err.reason.toString();
+ }
+ }
+ })
+ }
+
+ const fileCountsResponse: IFileCountResponse[] =
fileCountresponse.value?.data ?? [{
+ volume: '0',
+ bucket: '0',
+ fileSize: '0',
+ count: 0
+ }];
+ const containerCountResponse: IContainerCountResponse[] =
containerCountresponse.value?.data ?? [{
+ containerSize: 0,
+ count: 0
+ }];
+
// Construct volume -> bucket[] map for populating filters
// Ex: vol1 -> [bucket1, bucket2], vol2 -> [bucket1]
const volumeBucketMap: Map<string, Set<string>> =
fileCountsResponse.reduce(
@@ -280,7 +393,7 @@ export class Insights extends
React.Component<Record<string, object>, IInsightsS
const buckets = Array.from(map.get(volume)!);
map.set(volume, new Set([...buckets, bucket]));
} else {
- map.set(volume, new Set().add(bucket));
+ map.set(volume, new Set<string>().add(bucket));
}
return map;
@@ -297,7 +410,9 @@ export class Insights extends
React.Component<Record<string, object>, IInsightsS
volumeBucketMap,
fileCountsResponse,
containerCountResponse,
- volumeOptions
+ volumeOptions,
+ fileCountError: fileAPIError,
+ containerSizeError: containerAPIError
}, () => {
this.updatePlotData();
// Select all volumes by default
@@ -305,7 +420,7 @@ export class Insights extends
React.Component<Record<string, object>, IInsightsS
});
})).catch(error => {
this.setState({
- isLoading: false
+ isLoading: false,
});
showDataFetchError(error.toString());
});
diff --git
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/overview/overview.tsx
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/overview/overview.tsx
index 5b27b3f0d0..bb2e5e02f2 100644
---
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/overview/overview.tsx
+++
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/overview/overview.tsx
@@ -17,7 +17,7 @@
*/
import React from 'react';
-import axios from 'axios';
+import axios, { CanceledError } from 'axios';
import moment from 'moment';
import filesize from 'filesize';
import { Row, Col, Tooltip } from 'antd';
@@ -28,25 +28,25 @@ import OverviewCard from
'@/components/overviewCard/overviewCard';
import AutoReloadPanel from '@/components/autoReloadPanel/autoReloadPanel';
import { AutoReloadHelper } from '@/utils/autoReloadHelper';
import { showDataFetchError, byteToSize } from '@/utils/common';
-import { AxiosAllGetHelper, AxiosGetHelper, cancelRequests } from
'@/utils/axiosRequestHelper';
+import { AxiosGetHelper, cancelRequests, PromiseAllSettledGetHelper } from
'@/utils/axiosRequestHelper';
import './overview.less';
const size = filesize.partial({ round: 1 });
interface IClusterStateResponse {
- missingContainers: number;
- totalDatanodes: number;
- healthyDatanodes: number;
- pipelines: number;
+ missingContainers: number | string;
+ totalDatanodes: number | string;
+ healthyDatanodes: number | string;
+ pipelines: number | string;
storageReport: IStorageReport;
- containers: number;
- volumes: number;
- buckets: number;
- keys: number;
- openContainers: number;
- deletedContainers: number;
- keysPendingDeletion: number;
+ containers: number | string;
+ volumes: number | string;
+ buckets: number | string;
+ keys: number | string;
+ openContainers: number | string;
+ deletedContainers: number | string;
+ keysPendingDeletion: number | string;
scmServiceId: string;
omServiceId: string;
}
@@ -54,25 +54,25 @@ interface IClusterStateResponse {
interface IOverviewState {
loading: boolean;
datanodes: string;
- pipelines: number;
+ pipelines: number | string;
storageReport: IStorageReport;
- containers: number;
- volumes: number;
- buckets: number;
- keys: number;
- missingContainersCount: number;
- lastRefreshed: number;
- lastUpdatedOMDBDelta: number;
- lastUpdatedOMDBFull: number;
+ containers: number | string;
+ volumes: number | string;
+ buckets: number | string;
+ keys: number | string;
+ missingContainersCount: number | string;
+ lastRefreshed: number | string;
+ lastUpdatedOMDBDelta: number | string;
+ lastUpdatedOMDBFull: number | string;
omStatus: string;
- openContainers: number;
- deletedContainers: number;
- openSummarytotalUnrepSize: number;
- openSummarytotalRepSize: number;
- openSummarytotalOpenKeys: number;
- deletePendingSummarytotalUnrepSize: number;
- deletePendingSummarytotalRepSize: number;
- deletePendingSummarytotalDeletedKeys: number;
+ openContainers: number | string;
+ deletedContainers: number | string;
+ openSummarytotalUnrepSize: number | string;
+ openSummarytotalRepSize: number | string;
+ openSummarytotalOpenKeys: number | string;
+ deletePendingSummarytotalUnrepSize: number | string;
+ deletePendingSummarytotalRepSize: number | string;
+ deletePendingSummarytotalDeletedKeys: number | string;
scmServiceId: string;
omServiceId: string;
}
@@ -127,7 +127,7 @@ export class Overview extends
React.Component<Record<string, object>, IOverviewS
cancelOverviewSignal
]);
- const { requests, controller } = AxiosAllGetHelper([
+ const { requests, controller } = PromiseAllSettledGetHelper([
'/api/v1/clusterState',
'/api/v1/task/status',
'/api/v1/keys/open/summary',
@@ -135,17 +135,65 @@ export class Overview extends
React.Component<Record<string, object>, IOverviewS
], cancelOverviewSignal);
cancelOverviewSignal = controller;
- requests.then(axios.spread((clusterStateResponse, taskstatusResponse,
openResponse, deletePendingResponse) => {
+ requests.then(axios.spread((
+ clusterStateResponse: Awaited<Promise<any>>,
+ taskstatusResponse: Awaited<Promise<any>>,
+ openResponse: Awaited<Promise<any>>,
+ deletePendingResponse: Awaited<Promise<any>>
+ ) => {
+ let responseError = [
+ clusterStateResponse,
+ taskstatusResponse,
+ openResponse,
+ deletePendingResponse
+ ].filter((resp) => resp.status === 'rejected');
- const clusterState: IClusterStateResponse = clusterStateResponse.data;
- const taskStatus = taskstatusResponse.data;
+ if (responseError.length !== 0) {
+ responseError.forEach((err) => {
+ if (err.reason.toString().includes("CanceledError")){
+ throw new CanceledError('canceled', "ERR_CANCELED");
+ }
+ else {
+ const reqMethod = err.reason.config.method;
+ const reqURL = err.reason.config.url
+ showDataFetchError(`Failed to ${reqMethod} URL
${reqURL}\n${err.reason.toString()}`);
+ }
+ })
+ }
+
+ const clusterState: IClusterStateResponse =
clusterStateResponse.value?.data ?? {
+ missingContainers: 'N/A',
+ totalDatanodes: 'N/A',
+ healthyDatanodes: 'N/A',
+ pipelines: 'N/A',
+ storageReport: {
+ capacity: 0,
+ used: 0,
+ remaining: 0,
+ committed: 0
+ },
+ containers: 'N/A',
+ volumes: 'N/A',
+ buckets: 'N/A',
+ keys: 'N/A',
+ openContainers: 'N/A',
+ deletedContainers: 'N/A',
+ keysPendingDeletion: 'N/A',
+ scmServiceId: 'N/A',
+ omServiceId: 'N/A',
+ };
+ const taskStatus = taskstatusResponse.value?.data ?? [{
+ taskName: 'N/A', lastUpdatedTimestamp: 0, lastUpdatedSeqNumber: 0
+ }];
const missingContainersCount = clusterState.missingContainers;
const omDBDeltaObject = taskStatus && taskStatus.find((item: any) =>
item.taskName === 'OmDeltaRequest');
const omDBFullObject = taskStatus && taskStatus.find((item: any) =>
item.taskName === 'OmSnapshotRequest');
this.setState({
loading: false,
- datanodes:
`${clusterState.healthyDatanodes}/${clusterState.totalDatanodes}`,
+ datanodes: clusterState.healthyDatanodes !== 'N/A'
+ ? `${clusterState.healthyDatanodes}/${clusterState.totalDatanodes}`
+ : `N/A`,
storageReport: clusterState.storageReport,
pipelines: clusterState.pipelines,
containers: clusterState.containers,
@@ -158,12 +206,12 @@ export class Overview extends
React.Component<Record<string, object>, IOverviewS
lastRefreshed: Number(moment()),
lastUpdatedOMDBDelta: omDBDeltaObject &&
omDBDeltaObject.lastUpdatedTimestamp,
lastUpdatedOMDBFull: omDBFullObject &&
omDBFullObject.lastUpdatedTimestamp,
- openSummarytotalUnrepSize: openResponse.data &&
openResponse.data.totalUnreplicatedDataSize,
- openSummarytotalRepSize: openResponse.data &&
openResponse.data.totalReplicatedDataSize,
- openSummarytotalOpenKeys: openResponse.data &&
openResponse.data.totalOpenKeys,
- deletePendingSummarytotalUnrepSize: deletePendingResponse.data &&
deletePendingResponse.data.totalUnreplicatedDataSize,
- deletePendingSummarytotalRepSize: deletePendingResponse.data &&
deletePendingResponse.data.totalReplicatedDataSize,
- deletePendingSummarytotalDeletedKeys: deletePendingResponse.data &&
deletePendingResponse.data.totalDeletedKeys,
+ openSummarytotalUnrepSize:
openResponse.value?.data?.totalUnreplicatedDataSize,
+ openSummarytotalRepSize:
openResponse.value?.data?.totalReplicatedDataSize,
+ openSummarytotalOpenKeys: openResponse.value?.data?.totalOpenKeys,
+ deletePendingSummarytotalUnrepSize:
deletePendingResponse.value?.data?.totalUnreplicatedDataSize,
+ deletePendingSummarytotalRepSize:
deletePendingResponse.value?.data?.totalReplicatedDataSize,
+ deletePendingSummarytotalDeletedKeys:
deletePendingResponse.value?.data?.totalDeletedKeys,
scmServiceId: clusterState.scmServiceId,
omServiceId: clusterState.omServiceId
});
@@ -221,43 +269,89 @@ export class Overview extends
React.Component<Record<string, object>, IOverviewS
keys, missingContainersCount, lastRefreshed, lastUpdatedOMDBDelta,
lastUpdatedOMDBFull,
omStatus, openContainers, deletedContainers, scmServiceId, omServiceId }
= this.state;
- const datanodesElement = (
- <span>
- <CheckCircleFilled className='icon-success icon-small' /> {datanodes}
<span className='ant-card-meta-description meta'>HEALTHY</span>
- </span>
- );
+ let openKeysError: boolean = false;
+ let pendingDeleteKeysError: boolean = false;
+ if ([
+ openSummarytotalRepSize,
+ openSummarytotalUnrepSize,
+ openSummarytotalOpenKeys].some(
+ (data) => data === undefined
+ )) {
+ openKeysError = true;
+ }
+
+ if ([
+ deletePendingSummarytotalRepSize,
+ deletePendingSummarytotalUnrepSize,
+ deletePendingSummarytotalDeletedKeys].some(
+ (data) => data === undefined
+ )) {
+ pendingDeleteKeysError = true;
+ }
+ const datanodesElement = datanodes !== 'N/A'
+ ? (
+ <span>
+ <CheckCircleFilled className='icon-success icon-small' />
{datanodes} <span className='ant-card-meta-description meta'>HEALTHY</span>
+ </span>
+ )
+ : (
+ <span>
+ <Tooltip
+ placement='bottom'
+ title={'Failed to fetch Datanodes count'}>
+ <ExclamationCircleFilled className='icon-failure icon-small' />
+ <span className='padded-text'>{datanodes}</span>
+ <span className='ant-card-meta-description meta
padded-text'>UNHEALTHY</span>
+ </Tooltip>
+ </span>
+ )
const openSummaryData = (
<div>
- {openSummarytotalRepSize !== undefined ?
byteToSize(openSummarytotalRepSize, 1) : '0'} <span
className='ant-card-meta-description meta'>Total Replicated Data Size</span><br
/>
- {openSummarytotalUnrepSize !== undefined ?
byteToSize(openSummarytotalUnrepSize, 1) : '0'} <span
className='ant-card-meta-description meta'>Total UnReplicated Data
Size</span><br />
- {openSummarytotalOpenKeys !== undefined ? openSummarytotalOpenKeys :
'0'} <span className='ant-card-meta-description meta'>Total Open Keys</span>
+ {openSummarytotalRepSize !== undefined ?
byteToSize(openSummarytotalRepSize, 1) : 'N/A'} <span
className='ant-card-meta-description meta'>Total Replicated Data Size</span><br
/>
+ {openSummarytotalUnrepSize !== undefined ?
byteToSize(openSummarytotalUnrepSize, 1) : 'N/A'} <span
className='ant-card-meta-description meta'>Total UnReplicated Data
Size</span><br />
+ {openSummarytotalOpenKeys !== undefined ? openSummarytotalOpenKeys :
'N/A'} <span className='ant-card-meta-description meta'>Total Open Keys</span>
</div>
);
const deletePendingSummaryData = (
<div>
- {deletePendingSummarytotalRepSize !== undefined ?
byteToSize(deletePendingSummarytotalRepSize, 1) : '0'} <span
className='ant-card-meta-description meta'>Total Replicated Data Size</span><br
/>
- {deletePendingSummarytotalUnrepSize !== undefined ?
byteToSize(deletePendingSummarytotalUnrepSize, 1) : '0'} <span
className='ant-card-meta-description meta'>Total UnReplicated Data
Size</span><br />
- {deletePendingSummarytotalDeletedKeys !== undefined ?
deletePendingSummarytotalDeletedKeys : '0'} <span
className='ant-card-meta-description meta'>Total Pending Delete Keys</span>
+ {deletePendingSummarytotalRepSize !== undefined ?
byteToSize(deletePendingSummarytotalRepSize, 1) : 'N/A'} <span
className='ant-card-meta-description meta'>Total Replicated Data Size</span><br
/>
+ {deletePendingSummarytotalUnrepSize !== undefined ?
byteToSize(deletePendingSummarytotalUnrepSize, 1) : 'N/A'} <span
className='ant-card-meta-description meta'>Total UnReplicated Data
Size</span><br />
+ {deletePendingSummarytotalDeletedKeys !== undefined ?
deletePendingSummarytotalDeletedKeys : 'N/A'} <span
className='ant-card-meta-description meta'>Total Pending Delete Keys</span>
</div>
);
const containersTooltip = missingContainersCount === 1 ? 'container is
missing' : 'containers are missing';
- const containersLink = missingContainersCount > 0 ? '/MissingContainers' :
'/Containers';
+ const containersLink = missingContainersCount as number > 0 ?
'/MissingContainers' : '/Containers';
const volumesLink = '/Volumes';
const bucketsLink = '/Buckets';
- const containersElement = missingContainersCount > 0 ? (
- <span>
- <Tooltip placement='bottom' title={missingContainersCount > 1000 ?
`1000+ Containers are missing. For more information, go to the Containers
page.` : `${missingContainersCount} ${containersTooltip}`}>
- <ExclamationCircleFilled className='icon-failure icon-small' />
- </Tooltip>
- <span className='padded-text'>{containers -
missingContainersCount}/{containers}</span>
- </span>
- ) :
- <div>
- <span>{containers.toString()} </span>
- <Tooltip placement='bottom' title='Number of open containers'>
- <span>({openContainers})</span>
- </Tooltip>
- </div>
+ const containersElement = missingContainersCount !== 'N/A'
+ ? missingContainersCount as number > 0
+ ? (<span>
+ <Tooltip
+ placement='bottom'
+ title={
+ missingContainersCount as number > 1000
+ ? `1000+ Containers are missing. For more information, go to
the Containers page.`
+ : `${missingContainersCount} ${containersTooltip}`}>
+ <ExclamationCircleFilled className='icon-failure icon-small' />
+ </Tooltip>
+ <span className='padded-text'>{(containers as number) -
(missingContainersCount as number)}/{containers}</span>
+ </span>
+ )
+ :
+ (<div>
+ <span>{containers.toString()} </span>
+ <Tooltip placement='bottom' title='Number of open containers'>
+ <span>({openContainers})</span>
+ </Tooltip>
+ </div>)
+ : (
+ <span>
+ <Tooltip placement='bottom' title={`Failed to fetch Container
Count`}>
+ <ExclamationCircleFilled className='icon-failure icon-small' />
+ <span className='padded-text'>{missingContainersCount}</span>
+ </Tooltip>
+ </span>
+ )
const clusterCapacity = `${size(storageReport.capacity -
storageReport.remaining)}/${size(storageReport.capacity)}`;
return (
<div className='overview-content'>
@@ -272,6 +366,7 @@ export class Overview extends
React.Component<Record<string, object>, IOverviewS
<OverviewCard
hoverable loading={loading} title='Datanodes'
data={datanodesElement} icon='cluster'
+ error={datanodes === 'N/A'}
linkToUrl='/Datanodes' />
</Col>
<Col xs={24} sm={18} md={12} lg={12} xl={6}>
@@ -290,7 +385,7 @@ export class Overview extends
React.Component<Record<string, object>, IOverviewS
<OverviewCard
loading={loading} title='Containers' data={containersElement}
icon='container'
- error={missingContainersCount > 0} linkToUrl={containersLink} />
+ error={missingContainersCount as number > 0 ||
missingContainersCount === 'N/A'} linkToUrl={containersLink} />
</Col>
<Col xs={24} sm={18} md={12} lg={12} xl={6}>
<OverviewCard loading={loading} title='Volumes'
data={volumes.toString()} icon='inbox' linkToUrl={volumesLink} />
@@ -305,10 +400,22 @@ export class Overview extends
React.Component<Record<string, object>, IOverviewS
<OverviewCard loading={loading} title='Deleted Containers'
data={deletedContainers.toString()} icon='delete' />
</Col>
<Col xs={24} sm={18} md={12} lg={12} xl={6} className='summary-font'>
- <OverviewCard loading={loading} title='Open Keys Summary'
data={openSummaryData} icon='file-text' linkToUrl='/Om' />
+ <OverviewCard
+ loading={loading}
+ title='Open Keys Summary'
+ data={openSummaryData}
+ icon='file-text'
+ linkToUrl='/Om'
+ error={openKeysError} />
</Col>
<Col xs={24} sm={18} md={12} lg={12} xl={6} className='summary-font'>
- <OverviewCard loading={loading} title='Pending Deleted Keys
Summary' data={deletePendingSummaryData} icon='delete' linkToUrl='/Om' />
+ <OverviewCard
+ loading={loading}
+ title='Pending Deleted Keys Summary'
+ data={deletePendingSummaryData}
+ icon='delete'
+ linkToUrl='/Om'
+ error={pendingDeleteKeysError} />
</Col>
{scmServiceId &&
<Col xs={24} sm={18} md={12} lg={12} xl={6}>
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]