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 b891f7c295 HDDS-10517. Recon - Add a UI component for showing DN 
decommissioning detailed status and info (#6724)
b891f7c295 is described below

commit b891f7c29513280e1e9f32af89ac4d1caa84aaa0
Author: Smita <[email protected]>
AuthorDate: Wed Aug 7 11:54:44 2024 +0530

    HDDS-10517. Recon - Add a UI component for showing DN decommissioning 
detailed status and info (#6724)
---
 .../webapps/recon/ozone-recon-web/api/db.json      | 199 ++++++++++++++++++++-
 .../webapps/recon/ozone-recon-web/api/routes.json  |   2 +
 .../src/components/overviewCard/overviewCard.tsx   |   6 +-
 .../src/views/datanodes/datanodes.tsx              |  81 +++++++--
 .../src/views/datanodes/decommissionSummary.tsx    | 121 +++++++++++++
 .../src/views/overview/overview.tsx                |  32 +++-
 6 files changed, 414 insertions(+), 27 deletions(-)

diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/api/db.json
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/api/db.json
index 12b4297fec..5416ca7f00 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/api/db.json
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/api/db.json
@@ -94,7 +94,7 @@
       },
       {
         "hostname": "localhost1.storage.enterprise.com",
-        "uuid": "b5907812-a5f2-11ea-bb37-0242ac130011",
+        "uuid": "4712ba3d-4bb2-477a-9211-d9b50c013055",
         "state": "HEALTHY",
         "opState": "DECOMMISSIONING",
         "lastHeartbeat": 1574724876059,
@@ -162,7 +162,7 @@
       },
       {
         "hostname": "localhost3.storage.enterprise.com",
-        "uuid": "b5907812-a5f2-11ea-bb37-0242ac130013",
+        "uuid": "4712ba3d-4bb2-477a-9211-d9b50c013056",
         "state": "HEALTHY",
         "opState": "ENTERING_MAINTENANCE",
         "lastHeartbeat": 1574724876059,
@@ -6747,6 +6747,201 @@
     ],
     "status": "OK"
   },
+  "decommissioninfo": {
+    "DatanodesDecommissionInfo":[
+      {
+        "datanodeDetails": {
+            "level": 3,
+            "parent": null,
+            "cost": 0,
+            "uuid": "4712ba3d-4bb2-477a-9211-d9b50c013055",
+            "uuidString": "4712ba3d-4bb2-477a-9211-d9b50c013055",
+            "ipAddress": "172.18.0.13",
+            "hostName": "ozone-ha-datanode-6.ozone-ha_default",
+            
+            "certSerialId": null,
+            "version": null,
+            "setupTime": 0,
+            "revision": null,
+            "buildDate": null,
+            "persistedOpState": "IN_SERVICE",
+            "persistedOpStateExpiryEpochSec": 0,
+            "initialVersion": 0,
+            "currentVersion": 1,
+            "decommissioned": false,
+            "maintenance": false,
+            "ipAddressAsByteString": {
+                "string": "172.18.0.13",
+                "bytes": {
+                    "validUtf8": true,
+                    "empty": false
+                }
+            },
+            "hostNameAsByteString": {
+                "string": "ozone-ha-datanode-6.ozone-ha_default",
+                "bytes": {
+                    "validUtf8": true,
+                    "empty": false
+                }
+            },
+            "networkName": "4712ba3d-4bb2-477a-9211-d9b50c013055",
+            "networkLocation": "/default-rack",
+            "networkFullPath": 
"/default-rack/4712ba3d-4bb2-477a-9211-d9b50c013055",
+            "numOfLeaves": 1,
+            "networkNameAsByteString": {
+                "string": "4712ba3d-4bb2-477a-9211-d9b50c013055",
+                "bytes": {
+                    "validUtf8": true,
+                    "empty": false
+                }
+            },
+            "networkLocationAsByteString": {
+                "string": "/default-rack",
+                "bytes": {
+                    "validUtf8": true,
+                    "empty": false
+                }
+            }
+        },
+        "metrics": null,
+        "containers": {}
+      },
+      {
+        "datanodeDetails": {
+            "level": 3,
+            "parent": null,
+            "cost": 0,
+            "uuid": "4712ba3d-4bb2-477a-9211-d9b50c013056",
+            "uuidString": "4712ba3d-4bb2-477a-9211-d9b50c013056",
+            "ipAddress": "172.18.0.13",
+            "hostName": "ozone-ha-datanode-6.ozone-ha_default",
+            "certSerialId": null,
+            "version": null,
+            "setupTime": 0,
+            "revision": null,
+            "buildDate": null,
+            "persistedOpState": "IN_SERVICE",
+            "persistedOpStateExpiryEpochSec": 0,
+            "initialVersion": 0,
+            "currentVersion": 1,
+            "decommissioned": false,
+            "maintenance": false,
+            "networkName": "4712ba3d-4bb2-477a-9211-d9b50c013055",
+            "networkLocation": "/default-rack",
+            "networkFullPath": 
"/default-rack/4712ba3d-4bb2-477a-9211-d9b50c013055",
+            "numOfLeaves": 1
+        },
+        "metrics": null,
+        "containers": {}
+      }
+    ]
+  },
+  "DatanodesDecommissionInfo": {
+    "DatanodesDecommissionInfo": [
+      {
+        "datanodeDetails": {
+          "level": 3,
+          "parent": null,
+          "cost": 0,
+          "uuid": "adc72ef9-0850-49d7-935d-e5a4e0d5be3c",
+          "uuidString": "adc72ef9-0850-49d7-935d-e5a4e0d5be3c",
+          "ipAddress": "172.18.0.10",
+          "hostName": "ozone-ha-datanode-2.ozone-ha_default",
+          "ports": [
+            {
+              "name": "HTTP",
+              "value": 9882
+            },
+            {
+              "name": "CLIENT_RPC",
+              "value": 19864
+            },
+            {
+              "name": "REPLICATION",
+              "value": 9886
+            },
+            {
+              "name": "RATIS",
+              "value": 9858
+            },
+            {
+              "name": "RATIS_ADMIN",
+              "value": 9857
+            },
+            {
+              "name": "RATIS_SERVER",
+              "value": 9856
+            },
+            {
+              "name": "RATIS_DATASTREAM",
+              "value": 9855
+            },
+            {
+              "name": "STANDALONE",
+              "value": 9859
+            }
+          ],
+          "certSerialId": null,
+          "version": null,
+          "setupTime": 0,
+          "revision": null,
+          "buildDate": null,
+          "persistedOpState": "DECOMMISSIONING",
+          "persistedOpStateExpiryEpochSec": 0,
+          "initialVersion": 0,
+          "currentVersion": 1,
+          "decommissioned": true,
+          "maintenance": false,
+          "ipAddressAsByteString": {
+            "string": "172.18.0.10",
+            "bytes": {
+              "validUtf8": true,
+              "empty": false
+            }
+          },
+          "hostNameAsByteString": {
+            "string": "ozone-ha-datanode-2.ozone-ha_default",
+            "bytes": {
+              "validUtf8": true,
+              "empty": false
+            }
+          },
+          "networkName": "adc72ef9-0850-49d7-935d-e5a4e0d5be3c",
+          "networkLocation": "/default-rack",
+          "networkFullPath": 
"/default-rack/adc72ef9-0850-49d7-935d-e5a4e0d5be3c",
+          "numOfLeaves": 1,
+          "networkNameAsByteString": {
+            "string": "adc72ef9-0850-49d7-935d-e5a4e0d5be3c",
+            "bytes": {
+              "validUtf8": true,
+              "empty": false
+            }
+          },
+          "networkLocationAsByteString": {
+            "string": "/default-rack",
+            "bytes": {
+              "validUtf8": true,
+              "empty": false
+            }
+          }
+        },
+        "metrics": {
+          "decommissionStartTime": "23/05/2024 07:28:35 UTC",
+          "numOfUnclosedPipelines": 2,
+          "numOfUnderReplicatedContainers": 0.0,
+          "numOfUnclosedContainers": 0.0
+        },
+        "containers": {
+          "UnderReplicated": [],
+          "UnClosed": [
+            "#6",
+            "#10",
+            "#17"
+          ]
+        }
+      }
+    ]
+  },
   "datanodesRemove": {
     "selectedRowKeys": [
       "b5907812-a5f2-11ea-bb37-0242ac130011"
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/api/routes.json
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/api/routes.json
index 7f3c86684b..e094f2b6ea 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/api/routes.json
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/api/routes.json
@@ -51,5 +51,7 @@
 
   "/containers/mismatch/deleted?limit=*": "/deleted",
   "/keys/deletePending/dirs?limit=*": "/dirdeletePending",
+  "/datanodes/decommission/info": "/decommissioninfo",
+  "/datanodes/decommission/info/datanode?uuid=*": "/DatanodesDecommissionInfo",
   "/datanodes/remove": "/datanodesRemove"
 }
\ No newline at end of file
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 78ba56e548..c8e19db14c 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
@@ -27,7 +27,8 @@ import {
   FolderOpenOutlined,
   FileTextOutlined,
   QuestionCircleOutlined,
-  DeleteOutlined
+  DeleteOutlined,
+  HourglassOutlined
 } from '@ant-design/icons';
 import { RouteComponentProps } from 'react-router';
 import { withRouter, Link } from 'react-router-dom';
@@ -70,7 +71,8 @@ const IconSelector = ({ iconType, ...extras }: { iconType: 
string }) => {
     'inbox': <InboxOutlined {...extras} />,
     'folder-open': <FolderOpenOutlined {...extras} />,
     'file-text': <FileTextOutlined {...extras} />,
-    'delete': <DeleteOutlined {...extras} />
+    'delete': <DeleteOutlined {...extras} />,
+    'hourglass': <HourglassOutlined {...extras} />
   }
 
   const selectIcon = (type: string) => {
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/datanodes/datanodes.tsx
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/datanodes/datanodes.tsx
index c4c9bf129c..47fe41dd7b 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/datanodes/datanodes.tsx
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/datanodes/datanodes.tsx
@@ -48,10 +48,10 @@ import { ColumnSearch } from '@/utils/columnSearch';
 import { ReplicationIcon } from '@/utils/themeIcons';
 import { AutoReloadHelper } from '@/utils/autoReloadHelper';
 import { AxiosGetHelper, AxiosPutHelper } from '@/utils/axiosRequestHelper';
+import DecommissionSummary from './decommissionSummary';
 
 import './datanodes.less';
 
-
 interface IDatanodeResponse {
   hostname: string;
   state: DatanodeState;
@@ -190,7 +190,6 @@ const COLUMNS = [
     render: (text: DatanodeOpState) => renderDatanodeOpState(text),
     sorter: (a: IDatanode, b: IDatanode) => a.opState.localeCompare(b.opState)
   },
-
   {
     title: 'Uuid',
     dataIndex: 'uuid',
@@ -198,7 +197,14 @@ const COLUMNS = [
     isVisible: true,
     isSearchable: true,
     sorter: (a: IDatanode, b: IDatanode) => a.uuid.localeCompare(b.uuid),
-    defaultSortOrder: 'ascend' as const
+    defaultSortOrder: 'ascend' as const,
+    render: (uuid: IDatanode, record: IDatanode) => {
+      return (
+        //1. Compare Decommission Api's UUID with all UUID in table and show 
Decommission Summary
+        (decommissionUuids && decommissionUuids.includes(record.uuid) && 
record.opState !== 'DECOMMISSIONED') ? 
+           <DecommissionSummary uuid={uuid} record={record} /> : 
<span>{uuid}</span>
+      );
+    }
   },
   {
     title: 'Storage Capacity',
@@ -356,6 +362,17 @@ const defaultColumns: IOption[] = COLUMNS.map(column => ({
 }));
 
 let cancelSignal: AbortController;
+let cancelDecommissionSignal: AbortController;
+let decommissionUuids: string | string[] =[];
+const COLUMN_UPDATE_DECOMMISSIONING = 'DECOMMISSIONING';
+
+type DatanodeDetails = {
+  uuid: string;
+}
+
+type DatanodeDecomissionInfo = {
+datanodeDetails: DatanodeDetails
+}
 
 export class Datanodes extends React.Component<Record<string, object>, 
IDatanodesState> {
   autoReload: AutoReloadHelper;
@@ -389,24 +406,34 @@ export class Datanodes extends 
React.Component<Record<string, object>, IDatanode
     return selectedColumns;
   };
 
-  _loadData = () => {
-    this.setState(prevState => ({
-      loading: true,
-      selectedColumns: this._getSelectedColumns(prevState.selectedColumns)
-    }));
-
-    const { request, controller } = AxiosGetHelper('/api/v1/datanodes', 
cancelSignal);
-    cancelSignal = controller;
-    request.then(response => {
-      const datanodesResponse: IDatanodesResponse = response.data;
+  _loadData = async () => {
+    // Need to call decommission API on each interval to get updated status 
before datanode API to compare UUID's
+    // update Operation State Column in table Manually before rendering
+    try {
+    let decomissionResponse = await this._loadDecommisionAPI();
+    decommissionUuids =  
decomissionResponse.data?.DatanodesDecommissionInfo?.map((item: 
DatanodeDecomissionInfo) => item.datanodeDetails.uuid);
+    }
+    catch (error: any)
+    {
+      this.setState({
+        loading: false
+      });
+      decommissionUuids = [];
+      showDataFetchError(error.toString());
+    }
+    try {
+    // Call Datanode API Synchronously after completing Decommission API to 
render Operation Status Column
+    let datanodeApiResponse = await this._loadDataNodeAPI();
+      const datanodesResponse: IDatanodesResponse = datanodeApiResponse.data;
       const totalCount = datanodesResponse.totalCount;
       const datanodes: IDatanodeResponse[] = datanodesResponse.datanodes;
       const dataSource: IDatanode[] = datanodes && datanodes.map(datanode => {
+        let decommissionCondition = decommissionUuids && 
decommissionUuids.includes(datanode.uuid) && datanode.opState !== 
'DECOMMISSIONED';
         return {
           hostname: datanode.hostname,
           uuid: datanode.uuid,
           state: datanode.state,
-          opState: datanode.opState,
+          opState: decommissionCondition ? COLUMN_UPDATE_DECOMMISSIONING : 
datanode.opState,
           lastHeartbeat: datanode.lastHeartbeat,
           storageUsed: datanode.storageReport.used,
           storageTotal: datanode.storageReport.capacity,
@@ -423,19 +450,40 @@ export class Datanodes extends 
React.Component<Record<string, object>, IDatanode
           networkLocation: datanode.networkLocation
         };
       });
-
       this.setState({
         loading: false,
         dataSource,
         totalCount,
         lastUpdated: Number(moment())
       });
-    }).catch(error => {
+    }
+    catch (error: any) {
       this.setState({
         loading: false
       });
+      decommissionUuids = [];
       showDataFetchError(error.toString());
+    }
+  };
+
+  _loadDecommisionAPI = async () => {
+    this.setState({
+      loading: true
     });
+    decommissionUuids = [];
+    const { request, controller } = await 
AxiosGetHelper('/api/v1/datanodes/decommission/info', cancelDecommissionSignal);
+    cancelDecommissionSignal = controller;
+    return request
+  };
+
+  _loadDataNodeAPI = async () => {
+    this.setState(prevState => ({
+      loading: true,
+      selectedColumns: this._getSelectedColumns(prevState.selectedColumns)
+    }));
+    const { request, controller } = await AxiosGetHelper('/api/v1/datanodes', 
cancelSignal);
+    cancelSignal = controller;
+    return request;
   };
 
   removeDatanode = async (selectedRowKeys: any) => {
@@ -462,6 +510,7 @@ export class Datanodes extends 
React.Component<Record<string, object>, IDatanode
   componentWillUnmount(): void {
     this.autoReload.stopPolling();
     cancelSignal && cancelSignal.abort();
+    cancelDecommissionSignal && cancelDecommissionSignal.abort();
   }
 
   onShowSizeChange = (current: number, pageSize: number) => {
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/datanodes/decommissionSummary.tsx
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/datanodes/decommissionSummary.tsx
new file mode 100644
index 0000000000..28527c0816
--- /dev/null
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/datanodes/decommissionSummary.tsx
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import React from 'react';
+import { Descriptions, Popover, Tooltip } from 'antd';
+import { InfoCircleOutlined } from '@ant-design/icons';
+import { withRouter } from 'react-router-dom';
+import { RouteComponentProps } from 'react-router';
+import axios from 'axios';
+import { showDataFetchError } from '@/utils/common';
+
+interface IDecommissionSummaryProps extends RouteComponentProps<object> {
+  uuid: string;
+  isLoading: boolean;
+  summaryData: string[];
+  record: string[];
+}
+
+class DecommissionSummary extends React.Component<IDecommissionSummaryProps> {
+  constructor(props = {}) {
+    super(props);
+    this.state = {
+      uuid: this.props.uuid,
+      record: this.props.record,
+      isLoading: false,
+      summaryData: []
+    };
+  }
+
+  componentDidMount(): void {
+    this.setState({
+      isLoading: true
+    });
+    if (this.state?.record && this.state?.summaryData) {
+      this.fetchDecommissionSummary(this.state.uuid);
+    }
+  }
+
+  fetchDecommissionSummary = async (selectedUuid: any) => {
+    try {
+      const datanodeEndpoint = 
`/api/v1/datanodes/decommission/info/datanode?uuid=${selectedUuid}`;
+      let infoDatanodeResponse = await axios.get(datanodeEndpoint);
+      let DatanodesDecommissionInfo = [];
+      DatanodesDecommissionInfo = 
infoDatanodeResponse?.data?.DatanodesDecommissionInfo[0];
+      this.setState({
+        loading: false,
+        summaryData: DatanodesDecommissionInfo
+      });
+    }
+    catch (error) {
+      this.setState({
+        loading: false,
+        summaryData: []
+      });
+      showDataFetchError(error.toString());
+    }
+  };
+
+  render() {
+    const { summaryData, uuid } = this.state;
+    let content;
+    
+    if ( summaryData && summaryData.length !== 0 && summaryData !== null && 
summaryData !== undefined && summaryData.datanodeDetails) {   
+      const { datanodeDetails, containers, metrics } = summaryData;
+      content = (
+        <Descriptions size="small" bordered column={1} title={`Decommission 
Status: DECOMMISSIONING`}>
+          <Descriptions.Item label="Datanode"> 
<b>{datanodeDetails.uuid}</b></Descriptions.Item>
+          <Descriptions.Item 
label="Location">({datanodeDetails.networkLocation}/{datanodeDetails.ipAddress}/{datanodeDetails.hostname})</Descriptions.Item>
+          {metrics !== null && metrics !== undefined && 
Object.keys(metrics).length !== 0 &&
+            <>
+              {<Descriptions.Item label="Decommissioning Started 
at">{metrics.decommissionStartTime}</Descriptions.Item>}
+              {<Descriptions.Item label="No. of Unclosed 
Pipelines">{metrics.numOfUnclosedPipelines}</Descriptions.Item>}
+              {<Descriptions.Item label="No. of Unclosed 
Containers">{metrics.numOfUnclosedContainers}</Descriptions.Item>}
+              {<Descriptions.Item label="No. of Under-Replicated 
Containers">{metrics.numOfUnderReplicatedContainers}</Descriptions.Item>}
+            </>
+          }
+          {
+            containers && Object.keys(containers).length !== 0 &&
+            <>
+              {containers.UnderReplicated && containers.UnderReplicated.length 
> 0 && <Descriptions.Item 
label="Under-Replicated">{containers.UnderReplicated}</Descriptions.Item>}
+              {containers.UnClosed && containers.UnClosed.length > 0 && 
<Descriptions.Item label="Unclosed">{containers.UnClosed}</Descriptions.Item>}
+            </>
+          }
+        </Descriptions>
+      );
+    }
+    // Need to check summarydata is not empty 
+    return (
+      <>
+        { (summaryData && summaryData.length !== 0) ?
+          <>
+            <Tooltip title="Detailed Summary of Decomssioned Records.">
+              <InfoCircleOutlined />
+            </Tooltip>
+            <Popover content={content}  placement="top" trigger="hover">
+              &nbsp;{uuid}
+            </Popover>
+          </> : uuid
+        }
+      </>
+
+    );
+  }
+}
+
+export default withRouter(DecommissionSummary);
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 bb2e5e02f2..a83d12408b 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
@@ -73,6 +73,7 @@ interface IOverviewState {
   deletePendingSummarytotalUnrepSize: number | string;
   deletePendingSummarytotalRepSize: number | string;
   deletePendingSummarytotalDeletedKeys: number | string;
+  decommissionInfoCount: number | string;
   scmServiceId: string;
   omServiceId: string;
 }
@@ -111,7 +112,8 @@ export class Overview extends 
React.Component<Record<string, object>, IOverviewS
       openSummarytotalOpenKeys: 0,
       deletePendingSummarytotalUnrepSize: 0,
       deletePendingSummarytotalRepSize: 0,
-      deletePendingSummarytotalDeletedKeys: 0
+      deletePendingSummarytotalDeletedKeys: 0,
+      decommissionInfoCount: 0
     };
     this.autoReload = new AutoReloadHelper(this._loadData);
   }
@@ -131,7 +133,8 @@ export class Overview extends 
React.Component<Record<string, object>, IOverviewS
       '/api/v1/clusterState',
       '/api/v1/task/status',
       '/api/v1/keys/open/summary',
-      '/api/v1/keys/deletePending/summary'
+      '/api/v1/keys/deletePending/summary',
+      '/api/v1/datanodes/decommission/info'
     ], cancelOverviewSignal);
     cancelOverviewSignal = controller;
 
@@ -139,13 +142,15 @@ export class Overview extends 
React.Component<Record<string, object>, IOverviewS
       clusterStateResponse: Awaited<Promise<any>>,
       taskstatusResponse: Awaited<Promise<any>>,
       openResponse: Awaited<Promise<any>>,
-      deletePendingResponse: Awaited<Promise<any>>
+      deletePendingResponse: Awaited<Promise<any>>,
+      decommissionResponse: Awaited<Promise<any>>
     ) => {
       let responseError = [
         clusterStateResponse,
         taskstatusResponse,
         openResponse,
-        deletePendingResponse
+        deletePendingResponse,
+        decommissionResponse
       ].filter((resp) => resp.status === 'rejected');
 
       if (responseError.length !== 0) {
@@ -186,8 +191,8 @@ export class Overview extends 
React.Component<Record<string, object>, IOverviewS
         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');
+      const omDBDeltaObject = taskStatus && taskStatus.find((item:any) => 
item.taskName === 'OmDeltaRequest');
+      const omDBFullObject = taskStatus && taskStatus.find((item:any) => 
item.taskName === 'OmSnapshotRequest');
 
       this.setState({
         loading: false,
@@ -212,6 +217,7 @@ export class Overview extends 
React.Component<Record<string, object>, IOverviewS
         deletePendingSummarytotalUnrepSize: 
deletePendingResponse.value?.data?.totalUnreplicatedDataSize,
         deletePendingSummarytotalRepSize: 
deletePendingResponse.value?.data?.totalReplicatedDataSize,
         deletePendingSummarytotalDeletedKeys: 
deletePendingResponse.value?.data?.totalDeletedKeys,
+        decommissionInfoCount: 
decommissionResponse.value?.data?.DatanodesDecommissionInfo.length,
         scmServiceId: clusterState.scmServiceId,
         omServiceId: clusterState.omServiceId
       });
@@ -267,10 +273,12 @@ export class Overview extends 
React.Component<Record<string, object>, IOverviewS
     const { loading, datanodes, pipelines, storageReport, containers, volumes, 
buckets, openSummarytotalUnrepSize, openSummarytotalRepSize, 
openSummarytotalOpenKeys,
       deletePendingSummarytotalUnrepSize, deletePendingSummarytotalRepSize, 
deletePendingSummarytotalDeletedKeys,
       keys, missingContainersCount, lastRefreshed, lastUpdatedOMDBDelta, 
lastUpdatedOMDBFull,
-      omStatus, openContainers, deletedContainers, scmServiceId, omServiceId } 
= this.state;
+      omStatus, openContainers, deletedContainers, scmServiceId, omServiceId, 
decommissionInfoCount } = this.state;
 
     let openKeysError: boolean = false;
     let pendingDeleteKeysError: boolean = false;
+    let decommissionInfoError: boolean = false;
+
     if ([
       openSummarytotalRepSize,
       openSummarytotalUnrepSize,
@@ -280,6 +288,12 @@ export class Overview extends 
React.Component<Record<string, object>, IOverviewS
       openKeysError = true;
     }
 
+    if ([decommissionInfoCount].some(
+        (data) => data === undefined
+      )) {
+      decommissionInfoError = true;
+    }
+
     if ([
       deletePendingSummarytotalRepSize,
       deletePendingSummarytotalUnrepSize,
@@ -427,6 +441,10 @@ export class Overview extends 
React.Component<Record<string, object>, IOverviewS
               <OverviewCard title="OM Service" loading={loading} 
data={omServiceId} icon='file-text' linkToUrl='/Om' />
             </Col>
           }
+          <Col xs={24} sm={18} md={12} lg={12} xl={6}>
+            <OverviewCard loading={loading} title='Decommissioning Datanodes 
Summary' data={decommissionInfoCount !== undefined ? 
decommissionInfoCount.toString() : 'NA'} icon='hourglass'
+              linkToUrl='/Datanodes' error={decommissionInfoError} />
+          </Col>
         </Row>
       </div>
     );


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to