This is an automated email from the ASF dual-hosted git repository.

devesh 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 b3d191bb18 HDDS-10486. Recon datanode UI to incorporate explicit 
removal of DEAD (#6411)
b3d191bb18 is described below

commit b3d191bb18df376495431b6b78a8819efa0c57fd
Author: Smita <[email protected]>
AuthorDate: Thu Jun 20 17:58:49 2024 +0530

    HDDS-10486. Recon datanode UI to incorporate explicit removal of DEAD 
(#6411)
---
 .../webapps/recon/ozone-recon-web/api/db.json      | 47 ++++++------
 .../webapps/recon/ozone-recon-web/api/routes.json  |  3 +-
 .../src/utils/axiosRequestHelper.tsx               | 15 ++++
 .../src/views/datanodes/datanodes.less             |  4 ++
 .../src/views/datanodes/datanodes.tsx              | 84 ++++++++++++++++++++--
 5 files changed, 126 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 271e70d741..47dd6ba040 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
@@ -20,12 +20,12 @@
     "omServiceId": "omservice"
   },
   "datanodes": {
-    "totalCount": 12,
+    "totalCount": 19,
     "datanodes": [
       {
         "hostname": "localhost2.storage.enterprise.com",
-        "uuid": "b590734e-a5f2-11ea-bb37-0242ac130002",
-        "state": "HEALTHY",
+        "uuid": "b590734e-a5f2-11ea-bb37-0242ac130010",
+        "state": "DEAD",
         "opState": "IN_SERVICE",
         "lastHeartbeat": 1574728876059,
         "storageReport": {
@@ -94,7 +94,7 @@
       },
       {
         "hostname": "localhost1.storage.enterprise.com",
-        "uuid": "b5907812-a5f2-11ea-bb37-0242ac130002",
+        "uuid": "b5907812-a5f2-11ea-bb37-0242ac130011",
         "state": "HEALTHY",
         "opState": "DECOMMISSIONING",
         "lastHeartbeat": 1574724876059,
@@ -128,7 +128,7 @@
       },
       {
         "hostname": "localhost4.storage.enterprise.com",
-        "uuid": "b5907812-a5f2-11ea-bb37-0242ac130002",
+        "uuid": "b5907812-a5f2-11ea-bb37-0242ac130012",
         "state": "HEALTHY",
         "opState": "DECOMMISSIONED",
         "lastHeartbeat": 1574724876059,
@@ -162,7 +162,7 @@
       },
       {
         "hostname": "localhost3.storage.enterprise.com",
-        "uuid": "b5907812-a5f2-11ea-bb37-0242ac130002",
+        "uuid": "b5907812-a5f2-11ea-bb37-0242ac130013",
         "state": "HEALTHY",
         "opState": "ENTERING_MAINTENANCE",
         "lastHeartbeat": 1574724876059,
@@ -196,7 +196,7 @@
       },
       {
         "hostname": "localhost6.storage.enterprise.com",
-        "uuid": "b5907812-a5f2-11ea-bb37-0242ac130002",
+        "uuid": "b5907812-a5f2-11ea-bb37-0242ac130014",
         "state": "HEALTHY",
         "opState": "IN_MAINTENANCE",
         "lastHeartbeat": 1574724876059,
@@ -230,7 +230,7 @@
       },
       {
         "hostname": "localhost5.storage.enterprise.com",
-        "uuid": "b5907934-a5f2-11ea-bb37-0242ac130002",
+        "uuid": "b5907934-a5f2-11ea-bb37-0242ac130015",
         "state": "STALE",
         "opState": "IN_SERVICE",
         "lastHeartbeat": 1343544879843,
@@ -270,7 +270,7 @@
       },
       {
         "hostname": "localhost7.storage.enterprise.com",
-        "uuid": "b5907934-a5f2-11ea-bb37-0242ac130002",
+        "uuid": "b5907934-a5f2-11ea-bb37-0242ac130016",
         "state": "STALE",
         "opState": "DECOMMISSIONING",
         "lastHeartbeat": 1343544879843,
@@ -310,7 +310,7 @@
       },
       {
         "hostname": "localhost8.storage.enterprise.com",
-        "uuid": "b5907934-a5f2-11ea-bb37-0242ac130002",
+        "uuid": "b5907934-a5f2-11ea-bb37-0242ac130017",
         "state": "STALE",
         "opState": "DECOMMISSIONED",
         "lastHeartbeat": 1343544879843,
@@ -350,7 +350,7 @@
       },
       {
         "hostname": "localhost9.storage.enterprise.com",
-        "uuid": "b5907934-a5f2-11ea-bb37-0242ac130002",
+        "uuid": "b5907934-a5f2-11ea-bb37-0242ac130018",
         "state": "STALE",
         "opState": "ENTERING_MAINTENANCE",
         "lastHeartbeat": 1343544879843,
@@ -390,7 +390,7 @@
       },
       {
         "hostname": "localhost10.storage.enterprise.com",
-        "uuid": "b5907934-a5f2-11ea-bb37-0242ac130002",
+        "uuid": "b5907934-a5f2-11ea-bb37-0242ac130019",
         "state": "STALE",
         "opState": "IN_MAINTENANCE",
         "lastHeartbeat": 1343544879843,
@@ -430,7 +430,7 @@
       },
       {
         "hostname": "localhost11.storage.enterprise.com",
-        "uuid": "b5907a06-a5f2-11ea-bb37-0242ac130002",
+        "uuid": "b5907a06-a5f2-11ea-bb37-0242ac130020",
         "state": "DEAD",
         "opState": "IN_SERVICE",
         "lastHeartbeat": 1074724876059,
@@ -451,7 +451,7 @@
       },
       {
         "hostname": "localhost12.storage.enterprise.com",
-        "uuid": "b5907ac4-a5f2-11ea-bb37-0242ac130002",
+        "uuid": "b5907ac4-a5f2-11ea-bb37-0242ac130021",
         "state": "DEAD",
         "opState": "DECOMMISSIONING",
         "lastHeartbeat": 1574724876059,
@@ -485,7 +485,7 @@
       },
       {
         "hostname": "localhost13.storage.enterprise.com",
-        "uuid": "b5907b82-a5f2-11ea-bb37-0242ac130002",
+        "uuid": "b5907b82-a5f2-11ea-bb37-0242ac130022",
         "state": "DEAD",
         "opState": "DECOMMISSIONED",
         "lastHeartbeat": 1574724876059,
@@ -519,7 +519,7 @@
       },
       {
         "hostname": "localhost14.storage.enterprise.com",
-        "uuid": "b5907c40-a5f2-11ea-bb37-0242ac130002",
+        "uuid": "b5907c40-a5f2-11ea-bb37-0242ac130023",
         "state": "DEAD",
         "opState": "ENTERING_MAINTENANCE",
         "lastHeartbeat": 1574724876059,
@@ -559,7 +559,7 @@
       },
       {
         "hostname": "localhost15.storage.enterprise.com",
-        "uuid": "b5907cf4-a5f2-11ea-bb37-0242ac130002",
+        "uuid": "b5907cf4-a5f2-11ea-bb37-0242ac130024",
         "state": "DEAD",
         "opState": "IN_MAINTENANCE",
         "lastHeartbeat": 1574724876059,
@@ -593,7 +593,7 @@
       },
       {
         "hostname": "localhost16.storage.enterprise.com",
-        "uuid": "b5907f4c-a5f2-11ea-bb37-0242ac130002",
+        "uuid": "b5907f4c-a5f2-11ea-bb37-0242ac130025",
         "state": "HEALTHY",
         "opState": "IN_SERVICE",
         "lastHeartbeat": 1574724874011,
@@ -627,7 +627,7 @@
       },
       {
         "hostname": "localhost17.storage.enterprise.com",
-        "uuid": "b590801e-a5f2-11ea-bb37-0242ac130002",
+        "uuid": "b590801e-a5f2-11ea-bb37-0242ac130026",
         "state": "HEALTHY",
         "opState": "IN_SERVICE",
         "lastHeartbeat": 1574723876959,
@@ -667,7 +667,7 @@
       },
       {
         "hostname": "localhost18.storage.enterprise.com",
-        "uuid": "b59080e6-a5f2-11ea-bb37-0242ac130002",
+        "uuid": "b59080e6-a5f2-11ea-bb37-0242ac130027",
         "state": "STALE",
         "opState": "IN_SERVICE",
         "lastHeartbeat": 1474724876783,
@@ -701,7 +701,7 @@
       },
       {
         "hostname": "localhost19.storage.enterprise.com",
-        "uuid": "b59081a4-a5f2-11ea-bb37-0242ac130002",
+        "uuid": "b59081a4-a5f2-11ea-bb37-0242ac130028",
         "state": "HEALTHY",
         "opState": "IN_SERVICE",
         "lastHeartbeat": 1574724796532,
@@ -6944,5 +6944,10 @@
       }
     ],
     "status": "OK"
+  },
+  "datanodesRemove": {
+    "selectedRowKeys": [
+      "b5907812-a5f2-11ea-bb37-0242ac130011"
+    ]
   }
 }
\ No newline at end of file
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 cb6cea4c84..b2136d074b 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
@@ -49,5 +49,6 @@
   "/keys/deletePending?limit=*" : "/keydeletePending",
 
   "/containers/mismatch/deleted?limit=*": "/deleted",
-  "/keys/deletePending/dirs?limit=*": "/dirdeletePending"
+  "/keys/deletePending/dirs?limit=*": "/dirdeletePending",
+  "/datanodes/remove": "/datanodesRemove"
 }
\ No newline at end of file
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 cb2590c8fc..dbdd29cd9e 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
@@ -34,6 +34,21 @@ export const AxiosGetHelper = (
   }
 }
 
+export const AxiosPutHelper = (
+  url: string,
+  data: any = {},
+  controller: AbortController,
+  message: string = "",  //optional
+): { "request": Promise<AxiosResponse<any, any>>, "controller": 
AbortController } => {
+
+  controller && controller.abort(message);
+  controller = new AbortController(); // generate new AbortController for the 
upcoming request
+  return {
+    request: axios.put(url, data, { signal: controller.signal }),
+    controller: controller
+  }
+}
+
 export const AxiosAllGetHelper = (
   urls: string[],
   controller: AbortController,
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/datanodes/datanodes.less
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/datanodes/datanodes.less
index 644437dc87..fe5d35e235 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/datanodes/datanodes.less
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/views/datanodes/datanodes.less
@@ -38,3 +38,7 @@
     z-index: 99;
   }
 }
+
+.ant-popover-inner{
+  margin-left: 500px;
+}
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 c42bd8c1f9..5821b48768 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
@@ -17,7 +17,7 @@
  */
 
 import React from 'react';
-import {Table, Icon, Tooltip, Popover} from 'antd';
+import {Table, Icon, Tooltip, Popover, Button, Popconfirm} from 'antd';
 import {PaginationConfig} from 'antd/lib/pagination';
 import moment from 'moment';
 import {ReplicationIcon} from 'utils/themeIcons';
@@ -36,7 +36,7 @@ import {MultiSelect, IOption} from 
'components/multiSelect/multiSelect';
 import {ActionMeta, ValueType} from 'react-select';
 import {showDataFetchError} from 'utils/common';
 import {ColumnSearch} from 'utils/columnSearch';
-import { AxiosGetHelper } from 'utils/axiosRequestHelper';
+import { AxiosGetHelper, AxiosPutHelper } from 'utils/axiosRequestHelper';
 
 interface IDatanodeResponse {
   hostname: string;
@@ -96,6 +96,7 @@ interface IDatanodesState {
   lastUpdated: number;
   selectedColumns: IOption[];
   columnOptions: IOption[];
+  selectedRowKeys: string[];
 }
 
 const renderDatanodeState = (state: DatanodeState) => {
@@ -352,7 +353,8 @@ export class Datanodes extends 
React.Component<Record<string, object>, IDatanode
       totalCount: 0,
       lastUpdated: 0,
       selectedColumns: [],
-      columnOptions: defaultColumns
+      columnOptions: defaultColumns,
+      selectedRowKeys: []
     };
     this.autoReload = new AutoReloadHelper(this._loadData);
   }
@@ -420,6 +422,21 @@ export class Datanodes extends 
React.Component<Record<string, object>, IDatanode
       showDataFetchError(error.toString());
     });
   };
+  
+  removeDatanode = async (selectedRowKeys: any) => {
+    const { request, controller } = await 
AxiosPutHelper('/api/v1/datanodes/remove', selectedRowKeys, cancelSignal);
+    cancelSignal = controller;
+    request.then(() => {
+      this._loadData();
+    }).catch(error => {
+      showDataFetchError(error.toString());
+    }).finally(() => {
+      this.setState({
+        loading: false,
+        selectedRowKeys: []
+      });
+    });
+  }
 
   componentDidMount(): void {
     // Fetch datanodes on component mount
@@ -436,8 +453,43 @@ export class Datanodes extends 
React.Component<Record<string, object>, IDatanode
     console.log(current, pageSize);
   };
 
+  onSelectChange = (newSelectedRowKeys:any) => {
+    this.setState({
+      selectedRowKeys: newSelectedRowKeys
+    });
+  };
+
+  onDisable = (record:any) => {
+    // Enable Checkbox for explicit removal who's State = DEAD
+    if (record.state !== 'DEAD') {
+      // Will return disabled checkboxes records who's Record state is not 
DEAD and Opeartional 
State=['IN_SERVICE','ENTERING_MAINTENANCE','DECOMMISSIONING','IN_MAINTENANCE','DECOMMISSIONED']
+      return true;
+    }
+  };
+  
+  popConfirm = () => {
+    this.setState({ loading: true });
+    this.removeDatanode(this.state.selectedRowKeys);
+  };
+
+  cancel = () => {
+    this.setState({ selectedRowKeys: [] });
+  };
+
   render() {
-    const {dataSource, loading, totalCount, lastUpdated, selectedColumns, 
columnOptions} = this.state;
+    const {dataSource, loading, totalCount, lastUpdated, selectedColumns, 
columnOptions, selectedRowKeys} = this.state;
+    
+    const rowSelection = {
+      selectedRowKeys,
+      onChange: this.onSelectChange,
+      getCheckboxProps: record=> ({
+        disabled:  this.onDisable(record),
+        opState: record.opState,
+      }),
+    };
+
+    const hasSelected = selectedRowKeys.length > 0;
+
     const paginationConfig: PaginationConfig = {
       showTotal: (total: number, range) => `${range[0]}-${range[1]} of 
${total} datanodes`,
       showSizeChanger: true,
@@ -470,7 +522,29 @@ export class Datanodes extends 
React.Component<Record<string, object>, IDatanode
         </div>
 
         <div className='content-div'>
+          {totalCount > 0 &&
+            <div style={{ marginBottom: 16 }}>
+              <Popconfirm
+                disabled={!hasSelected}
+                placement="topLeft"
+                title={`Are you sure you want Recon to stop tracking the 
selected ${selectedRowKeys.length} datanodes ?`}
+                icon={
+                  <Icon type='question-circle-o' style={{ color: 'red' }} />
+                }
+                onConfirm={this.popConfirm}
+                onCancel={this.cancel}
+              >
+                <Tooltip placement="topLeft" title="Remove the dead 
datanodes.">
+                  <Icon type="info-circle"/>
+                </Tooltip>
+                &nbsp;&nbsp;
+                <Button type="primary" shape="round" icon="delete"  
disabled={!hasSelected} loading={loading}> Remove
+                </Button>
+              </Popconfirm>
+            </div>
+          }
           <Table
+            rowSelection={rowSelection}
             dataSource={dataSource}
             columns={COLUMNS.reduce<any[]>((filtered, column) => {
               if (selectedColumns.some(e => e.value === column.key)) {
@@ -489,7 +563,7 @@ export class Datanodes extends 
React.Component<Record<string, object>, IDatanode
             }, [])}
             loading={loading}
             pagination={paginationConfig}
-            rowKey='hostname'
+            rowKey='uuid'
             scroll={{x: true, y: false, scrollToFirstRowOnChange: true}}
             locale={{filterTitle: ""}}
           />


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

Reply via email to