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>
+
+ <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]